texscanning.c /size: 260 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7static void tex_aux_scan_expr       (halfword level);
8static void tex_aux_scan_expression (int level);
9
10/*tex
11    A helper.
12*/
13
14inline static void tex_push_back(halfword tok, halfword cmd, halfword chr)
15{
16    if (cmd != spacer_cmd && tok != deep_frozen_relax_token && ! (cmd == relax_cmd && chr == no_relax_code)) {
17        tex_back_input(tok);
18    }
19}
20
21/*tex
22
23    Let's turn now to some procedures that \TEX\ calls upon frequently to digest certain kinds of
24    patterns in the input. Most of these are quite simple; some are quite elaborate. Almost all of
25    the routines call |get_x_token|, which can cause them to be invoked recursively.
26
27    The |scan_left_brace| routine is called when a left brace is supposed to be the next non-blank
28    token. (The term \quote {left brace} means, more precisely, a character whose catcode is
29    |left_brace|.) \TEX\ allows |\relax| to appear before the |left_brace|.
30
31*/
32
33/* This reads a mandatory |left_brace|: */
34
35void tex_scan_left_brace(void)
36{
37    /*tex Get the next non-blank non-relax non-call token */
38    while(1) {
39        tex_get_x_token();
40        switch (cur_cmd) {
41            case left_brace_cmd:
42                /* we found one */
43                return;
44            case spacer_cmd:
45            case relax_cmd:
46                /* stay in while */
47                break;
48            default:
49                /* we recover */
50                tex_handle_error(
51                    back_error_type,
52                    "Missing { inserted",
53                    "A left brace was mandatory here, so I've put one in."
54                );
55                cur_tok = left_brace_token + '{';
56                cur_cmd = left_brace_cmd;
57                cur_chr = '{';
58                ++lmt_input_state.align_state;
59                return;
60        }
61    }
62}
63
64/*tex
65
66    The |scan_optional_equals| routine looks for an optional |=| sign preceded by optional spaces;
67    |\relax| is not ignored here.
68
69*/
70
71void tex_scan_optional_equals(void)
72{
73    /*tex Get the next non-blank non-call token. */
74    do {
75        tex_get_x_token();
76    } while (cur_cmd == spacer_cmd);
77    if (cur_tok != equal_token) {
78        tex_back_input(cur_tok);
79    }
80}
81
82/*tex
83
84    Here is a procedure that sounds an alarm when mu and non-mu units are being switched.
85
86*/
87
88static void tex_aux_mu_error(int n)
89{
90    tex_handle_error(
91        normal_error_type,
92        "Incompatible glue units (case %i)",
93        n,
94        "I'm going to assume that 1mu=1pt when they're mixed."
95    );
96}
97
98/*tex
99
100    The next routine |scan_something_internal| is used to fetch internal numeric quantities like
101    |\hsize|, and also to handle the |\the| when expanding constructions like |\the\toks0| and
102    |\the\baselineskip|. Soon we will be considering the |scan_int| procedure, which calls
103    |scan_something_internal|; on the other hand, |scan_something_internal| also calls |scan_int|,
104    for constructions like |\catcode\`\$| or |\fontdimen 3 \ff|. So we have to declare |scan_int|
105    as a |forward| procedure. A few other procedures are also declared at this point.
106
107    \TEX\ doesn't know exactly what to expect when |scan_something_internal| begins. For example,
108    an integer or dimension or glue value could occur immediately after |\hskip|; and one can even
109    say |\the} with respect to token lists in constructions like |\xdef\o{\the\output}|. On the
110    other hand, only integers are allowed after a construction like |\count|. To handle the various
111    possibilities, |scan_something_internal| has a |level| parameter, which tells the \quote
112    {highest} kind of quantity that |scan_something_internal| is allowed to produce. Seven levels
113    are  distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|, |mu_val|, |tok_val|
114    and |ident_val|.
115
116    The output of |scan_something_internal| (and of the other routines |scan_int|, |scan_dimension|,
117    and |scan_glue| below) is put into the global variable |cur_val|, and its level is put into
118    |cur_val_level|. The highest values of |cur_val_level| are special: |mu_val| is used only when
119    |cur_val| points to something in a \quote {muskip} register, or to one of the three parameters
120    |\thinmuskip|, |\medmuskip|, |\thickmuskip|; |ident_val| is used only when |cur_val| points to
121    a font identifier; |tok_val| is used only when |cur_val| points to |null| or to the reference
122    count of a token list. The last two cases are allowed only when |scan_something_internal| is
123    called with |level = tok_val|.
124
125    If the output is glue, |cur_val| will point to a glue specification, and the reference count
126    of that glue will have been updated to reflect this reference; if the output is a nonempty
127    token list, |cur_val| will point to its reference count, but in this case the count will not
128    have been updated. Otherwise |cur_val| will contain the integer or scaled value in question.
129
130*/
131
132scanner_state_info lmt_scanner_state = {
133    .current_cmd       = 0,
134    .current_chr       = 0,
135    .current_cs        = 0,
136 // .current_flag      = 0,
137    .current_tok       = 0,
138    .current_val       = 0,
139    .current_val_level = 0,
140    .current_box       = 0,
141    .last_cs_name      = 0,
142    .arithmic_error    = 0,
143    .expression_depth  = 0,
144};
145
146/*tex
147
148    When a |glue_val| changes to a |dimen_val|, we use the width component of the glue; there is no
149    need to decrease the reference count, since it has not yet been increased. When a |dimen_val|
150    changes to an |int_val|, we use scaled points so that the value doesn't actually change. And
151    when a |mu_val| changes to a |glue_val|, the value doesn't change either.
152
153    In \LUATEX\ we don't share glue but we have copies, so there is no need to mess with the
154    reference count and downgrading.
155
156*/
157
158inline static void tex_aux_downgrade_cur_val(int level, int succeeded, int negative)
159{
160    switch (cur_val_level) {
161        case posit_val_level:
162            if (negative) {
163                cur_val = tex_posit_neg(cur_val);
164            }
165            switch (level) { 
166                case dimension_val_level:
167                    cur_val = tex_posit_to_dimension(cur_val);
168                cur_val_level = level;
169                    break;
170                case integer_val_level:
171                case attribute_val_level:
172                    cur_val = (halfword) tex_posit_to_integer(cur_val);
173                cur_val_level = level;
174                    break;
175            }
176//            if (cur_val_level > level) {
177//                cur_val_level = level;
178//            }
179            break;
180        case integer_val_level:
181            if (cur_val_level > level) {
182                cur_val_level = level;
183            }
184            if (negative) {
185                cur_val = -cur_val;
186            }
187            if (level == posit_val_level) { 
188                cur_val = tex_integer_to_posit(cur_val).v;
189            }
190            break;
191        case attribute_val_level:
192            if (cur_val_level > level) {
193                cur_val_level = level;
194            }
195            if (negative) {
196                cur_val = -cur_val;
197            }
198            if (level == posit_val_level) { 
199                cur_val = tex_integer_to_posit(cur_val).v;
200            }
201            break;
202        case dimension_val_level:
203            if (cur_val_level > level) {
204                cur_val_level = level;
205            }
206            if (negative) {
207                cur_val = -cur_val;
208            }
209            if (level == posit_val_level) { 
210                cur_val = tex_dimension_to_posit(cur_val).v;
211            }
212            break;
213        case muglue_val_level:            
214            if (level == glue_val_level) { 
215                goto COPYGLUE;
216            }
217        case glue_val_level:
218            if (level == posit_val_level) { 
219                cur_val_level = level;
220                cur_val = tex_dimension_to_posit(negative ? - glue_amount(cur_val) : glue_amount(cur_val)).v;
221            } else if (cur_val_level > level) { 
222                /* we can end up here with tok_val_level and a minus .. fuzzy */
223                cur_val_level = level;
224                cur_val = negative ? - glue_amount(cur_val) : glue_amount(cur_val);
225            } else {
226              COPYGLUE:
227                if (succeeded == 1) {
228                    cur_val = tex_new_glue_spec_node(cur_val);
229                }
230                if (negative) {
231                    glue_amount(cur_val) = -glue_amount(cur_val);
232                    glue_stretch(cur_val) = -glue_stretch(cur_val);
233                    glue_shrink(cur_val) = -glue_shrink(cur_val);
234                }
235            }
236            break;
237        case token_val_level:
238        case font_val_level:
239        case mathspec_val_level:
240        case fontspec_val_level:
241            /*tex
242                This test pays back as this actually happens, but we also need it for the
243                |none_lua_function| handling. We end up here in |ident_val_level| and |token_val_level|
244                and they don't downgrade, nor negate which saves a little testing.
245            */
246            break;
247     // case specification_val_level:
248     // case list_val_level:
249     // case no_val_level:
250     //     break;
251        default:
252            tex_confusion("downgrade");
253     }
254}
255
256/*tex
257
258    Some of the internal items can be fetched both routines, and these have been split off into the
259    next routine, that returns true if the command code was understood.
260
261    The |last_item_cmd| branch has been flattened a bit because we don't need to treat \ETEX\
262    specific thingies special any longer.
263
264*/
265
266static void tex_aux_set_cur_val_by_lua_value_cmd(halfword index, halfword property)
267{
268    int category = lua_value_none_code;
269    halfword value = 0; /* can also be scaled */
270    strnumber u = tex_save_cur_string();
271    lmt_token_state.luacstrings = 0;
272    category = lmt_function_call_by_category(index, property, &value);
273    switch (category) {
274        case lua_value_none_code:
275            cur_val_level = no_val_level;
276            break;
277        case lua_value_integer_code:
278        case lua_value_cardinal_code:
279            cur_val_level = integer_val_level;
280            break;
281        case lua_value_dimension_code:
282            cur_val_level = dimension_val_level;
283            break;
284        case lua_value_skip_code:
285            cur_val_level = glue_val_level;
286            break;
287        case lua_value_boolean_code:
288            /*tex For usage with |\ifboolean| */
289            value = value ? 1 : 0;
290            cur_val_level = integer_val_level;
291            break;
292        case lua_value_float_code:
293            cur_val_level = posit_val_level;
294            break;
295        case lua_value_string_code:
296            cur_val_level = no_val_level;
297            break;
298        case lua_value_node_code:
299        case lua_value_direct_code:
300            if (value) {
301                switch (node_type(value)) {
302                    case hlist_node:
303                    case vlist_node:
304                    case whatsit_node:
305                    case rule_node:
306                        cur_val_level = list_val_level;
307                        break;
308                    default:
309                        /* maybe a warning */
310                        value = null;
311                        cur_val_level = no_val_level;
312                        break;
313                }
314            } else {
315                value = null;
316                cur_val_level = no_val_level;
317            }
318            break;
319        case lua_value_conditional_code:
320            /* for now */
321        default:
322            cur_val_level = no_val_level;
323            break;
324    }
325    cur_val = value;
326    tex_restore_cur_string(u);
327    if (lmt_token_state.luacstrings > 0) {
328        tex_lua_string_start();
329    }
330}
331
332halfword tex_scan_lua_value(int index)
333{
334    tex_aux_set_cur_val_by_lua_value_cmd(index, 0);
335    return cur_val_level;
336}
337
338static halfword tex_aux_scan_register_index(void)
339{
340    do {
341        tex_get_x_token();
342    } while (cur_cmd == spacer_cmd);
343    switch (cur_cmd) {
344        case register_toks_cmd      : return cur_chr - register_toks_base; 
345        case register_integer_cmd   : return cur_chr - register_integer_base; 
346        case register_attribute_cmd : return cur_chr - register_attribute_base;
347        case register_posit_cmd     : return cur_chr - register_posit_base;
348        case register_dimension_cmd : return cur_chr - register_dimension_base;
349        case register_glue_cmd      : return cur_chr - register_glue_base; 
350        case register_muglue_cmd    : return cur_chr - register_muglue_base;
351        case char_given_cmd         : return cur_chr;
352        case mathspec_cmd           : return tex_get_math_spec(cur_chr).character_value;
353        case integer_cmd            : return cur_chr;
354     /* case index_cmd              : return cur_chr; */
355        case posit_cmd              : return cur_chr;
356        case dimension_cmd          : return cur_chr;
357        default                     : return -1; 
358    }
359}
360
361static halfword tex_aux_scan_character_index(void)
362{
363    halfword result = -1; 
364    tex_get_token();
365    if (cur_tok < cs_token_flag) {
366        result = cur_chr; 
367    } else if (cur_cmd == char_given_cmd) {
368        result = cur_chr;
369    } else if (cur_cmd == mathspec_cmd) {
370        result = tex_get_math_spec(cur_chr).character_value;
371    } else { 
372        strnumber txt = cs_text(cur_tok - cs_token_flag);
373        if (tex_single_letter(txt)) {
374            result = aux_str2uni(str_string(txt));
375        } else if (tex_is_active_cs(txt)) {
376            result = active_cs_value(txt);
377        } else {
378            result = max_character_code + 1;
379        }
380    }
381    return result > max_character_code ? -1 : result;
382}
383
384/*
385    Fetch an item in the current node, if appropriate. Here is where |\last*| |\ |, and some more
386    are implemented. The reference count for |\lastskip| will be updated later. We also handle
387    |\inputlineno| and |\badness| here, because they are legal in similar contexts. In the follow
388    up engines much more than these are handled here.
389*/
390
391static int tex_aux_set_cur_val_by_some_cmd(int code)
392{
393    switch (code) {
394        case lastpenalty_code:
395            cur_val_level = integer_val_level;
396            goto COMMON;
397        case lastkern_code:
398            cur_val_level = dimension_val_level;
399            goto COMMON;
400        case lastskip_code:
401            cur_val_level = glue_val_level;
402            goto COMMON;
403        case lastboundary_code:
404            cur_val_level = integer_val_level;
405          COMMON:
406            {
407                cur_val = 0;
408                if (cur_list.tail != contribute_head && ! (cur_list.tail && node_type(cur_list.tail) == glyph_node) && cur_list.mode != nomode) {
409                    switch (code) {
410                        case lastpenalty_code:
411                            if (node_type(cur_list.tail) == penalty_node) {
412                                cur_val = penalty_amount(cur_list.tail);
413                            }
414                            break;
415                        case lastkern_code:
416                            if (node_type(cur_list.tail) == kern_node) {
417                                cur_val = kern_amount(cur_list.tail);
418                            }
419                            break;
420                        case lastskip_code:
421                            if (node_type(cur_list.tail) == glue_node) {
422                                cur_val = cur_list.tail;
423                                if (node_subtype(cur_list.tail) == mu_glue) {
424                                    cur_val_level = muglue_val_level;
425                                }
426                            }
427                            break; /* should we return 1 ? */
428                        case lastboundary_code:
429                            if (node_type(cur_list.tail) == boundary_node && node_subtype(cur_list.tail) == user_boundary) {
430                                cur_val = boundary_data(cur_list.tail);
431                            }
432                            break;
433                    }
434                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
435                    switch (code) {
436                        case lastpenalty_code:
437                            cur_val = lmt_page_builder_state.last_penalty;
438                            break;
439                        case lastkern_code:
440                            cur_val = lmt_page_builder_state.last_kern;
441                            break;
442                        case lastskip_code:
443                            if (lmt_page_builder_state.last_glue != max_halfword) {
444                                cur_val = lmt_page_builder_state.last_glue;
445                            }
446                            break; /* should we return 1 ? */
447                        case lastboundary_code:
448                            cur_val = lmt_page_builder_state.last_boundary;
449                            break;
450                    }
451                }
452                break;
453            }
454        case last_node_type_code:
455            /*tex
456                We have mode nodes and when the mode parameter is set we report the real numbers.
457                This is a bit messy.
458            */
459            {
460                cur_val_level = integer_val_level;
461                if (cur_list.tail != contribute_head && cur_list.mode != nomode) {
462                    cur_val = node_type(cur_list.tail);
463                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
464                    cur_val = lmt_page_builder_state.last_node_type;
465                } else if (cur_list.tail == cur_list.head || cur_list.mode == nomode) {
466                    cur_val = -1;
467                } else {
468                    cur_val = node_type(cur_list.tail);
469                }
470                break;
471            }
472        case last_node_subtype_code:
473            {
474                cur_val_level = integer_val_level;
475                if (cur_list.tail != contribute_head && cur_list.mode != nomode) {
476                    cur_val = node_subtype(cur_list.tail);
477                } else if (cur_list.mode == vmode && cur_list.tail == cur_list.head) {
478                    cur_val = lmt_page_builder_state.last_node_subtype;
479                } else if (cur_list.tail == cur_list.head || cur_list.mode == nomode) {
480                    cur_val = -1;
481                } else {
482                    cur_val = node_subtype(cur_list.tail);
483                }
484                break;
485            }
486        case input_line_no_code:
487            cur_val = lmt_input_state.input_line;
488            cur_val_level = integer_val_level;
489            break;
490        case badness_code:
491            cur_val = lmt_packaging_state.last_badness;
492            cur_val_level = integer_val_level;
493            break;
494        case overshoot_code:
495            cur_val = lmt_packaging_state.last_overshoot;
496            cur_val_level = dimension_val_level;
497            break;
498        case luatex_version_code:
499            cur_val = lmt_version_state.version;
500            cur_val_level = integer_val_level;
501            break;
502        case luatex_revision_code:
503            cur_val = lmt_version_state.revision;
504            cur_val_level = integer_val_level;
505            break;
506        case current_group_level_code:
507            cur_val = cur_level - level_one;
508            cur_val_level = integer_val_level;
509            break;
510        case current_group_type_code:
511            cur_val = cur_group;
512            cur_val_level = integer_val_level;
513            break;
514        case current_stack_size_code:
515            cur_val = lmt_save_state.save_stack_data.ptr;
516            cur_val_level = integer_val_level;
517            break;
518        case current_if_level_code:
519            {
520                halfword q = lmt_condition_state.cond_ptr;
521                cur_val = 0;
522                while (q) {
523                    ++cur_val;
524                    q = node_next(q);
525                }
526                cur_val_level = integer_val_level;
527                break;
528            }
529        case current_if_type_code:
530            {
531                /*tex
532                    We have more conditions than standard \TEX\ and \ETEX\ and the order is also somewhat
533                    different. One problem is that in \ETEX\ a zero means \quotation {not in an test}, so
534                    we're one off! Not that it matters much as this feature is probably never really used,
535                    but we kept if for compatibility reasons. But it's gone now ... as usual with some
536                    sentiment as it was nicely abstracted cleaned up code.
537                */
538                cur_val = lmt_condition_state.cond_ptr ? (lmt_condition_state.cur_if - first_real_if_test_code) : -1;
539                cur_val_level = integer_val_level;
540                break;
541            }
542        case current_if_branch_code:
543            {
544                switch (lmt_condition_state.if_limit) {
545                    case if_code:
546                        cur_val = 0;
547                        break;
548                    case fi_code:
549                        cur_val = -1;
550                        break;
551                    case else_code:
552                    case or_code:
553                    case or_else_code:
554                    case or_unless_code:
555                        cur_val = 1;
556                        break;
557                    default:
558                        cur_val = 0;
559                        break;
560                }
561                cur_val_level = integer_val_level;
562                break;
563            }
564        case glue_stretch_order_code:
565        case glue_shrink_order_code:
566            {
567                /*TeX
568                    Not that we need it but \LUATEX\ now has |\eTeXglue..order|. In \CONTEXT\ we're
569                    not using the internal codes anyway (or symbolic constants). In \LUATEX\ there
570                    is some \ETEX\ related shifting but we don't do that here.
571                */
572                halfword q = tex_scan_glue(glue_val_level, 0, 0);
573                cur_val = (code == glue_stretch_order_code) ? glue_stretch_order(q) : glue_shrink_order(q);
574                tex_flush_node(q);
575                cur_val_level = integer_val_level;
576                break;
577            }
578        case font_id_code:
579            {
580                cur_val = tex_scan_font_identifier(NULL);
581                cur_val_level = integer_val_level;
582                break;
583            }
584        case glyph_x_scaled_code:
585            {
586                cur_val = tex_font_x_scaled(tex_scan_dimension(0, 0, 0, 1, NULL));
587                cur_val_level = dimension_val_level;
588                break;
589            }
590        case glyph_y_scaled_code:
591            {
592                cur_val = tex_font_y_scaled(tex_scan_dimension(0, 0, 0, 1, NULL));
593                cur_val_level = dimension_val_level;
594                break;
595            }
596        case font_spec_id_code:
597        case font_spec_scale_code:
598        case font_spec_xscale_code:
599        case font_spec_yscale_code:
600        case font_spec_slant_code:
601        case font_spec_weight_code:
602            {
603                halfword fs = tex_scan_fontspec_identifier();
604                if (fs) {
605                    switch (code) {
606                        case font_spec_id_code:
607                            cur_val = font_spec_identifier(fs);
608                            break;
609                        case font_spec_scale_code:
610                            cur_val = font_spec_scale(fs);
611                            break;
612                        case font_spec_xscale_code:
613                            cur_val = font_spec_x_scale(fs);
614                            break;
615                        case font_spec_yscale_code:
616                            cur_val = font_spec_y_scale(fs);
617                            break;
618                        case font_spec_slant_code:
619                            cur_val = font_spec_slant(fs);
620                            break;
621                        case font_spec_weight_code:
622                            cur_val = font_spec_weight(fs);
623                            break;
624                    }
625                } else {
626                    cur_val = 0;
627                }
628                cur_val_level = integer_val_level;
629                break;
630            }
631        case font_char_wd_code:
632        case font_char_ht_code:
633        case font_char_dp_code:
634        case font_char_ic_code:
635        case font_char_ta_code:
636        case font_char_ba_code:
637        case scaled_font_char_wd_code:
638        case scaled_font_char_ht_code:
639        case scaled_font_char_dp_code:
640        case scaled_font_char_ic_code:
641        case scaled_font_char_ta_code:
642        case scaled_font_char_ba_code:
643            {
644                halfword fnt = tex_scan_font_identifier(NULL);
645                halfword chr = tex_scan_char_number(0);
646                if (tex_char_exists(fnt, chr)) {
647                    switch (code) {
648                        case font_char_wd_code:
649                        case scaled_font_char_wd_code:
650                            cur_val = tex_char_width_from_font(fnt, chr);
651                            break;
652                        case font_char_ht_code:
653                        case scaled_font_char_ht_code:
654                            cur_val = tex_char_height_from_font(fnt, chr);
655                            break;
656                        case font_char_dp_code:
657                        case scaled_font_char_dp_code:
658                            cur_val = tex_char_depth_from_font(fnt, chr);
659                            break;
660                        case font_char_ic_code:
661                        case scaled_font_char_ic_code:
662                            cur_val = tex_char_italic_from_font(fnt, chr);
663                            break;
664                        case font_char_ta_code:
665                        case scaled_font_char_ta_code:
666                            cur_val = tex_char_top_anchor_from_font(fnt, chr);
667                            break;
668                        case font_char_ba_code:
669                        case scaled_font_char_ba_code:
670                            cur_val = tex_char_bottom_anchor_from_font(fnt, chr);
671                            break;
672                    }
673                    switch (code) {
674                        case scaled_font_char_wd_code:
675                        case scaled_font_char_ic_code:
676                        case scaled_font_char_ta_code:
677                        case scaled_font_char_ba_code:
678                            cur_val = tex_font_x_scaled(cur_val);
679                            break;
680                        case scaled_font_char_ht_code:
681                        case scaled_font_char_dp_code:
682                            cur_val = tex_font_y_scaled(cur_val);
683                            break;
684                    }
685                } else {
686                    cur_val = 0;
687                }
688                cur_val_level = dimension_val_level;
689                break;
690            }
691        case font_size_code:
692            {
693                halfword fnt = tex_scan_font_identifier(NULL);
694                cur_val = font_size(fnt);
695                cur_val_level = dimension_val_level;
696                break;
697            }
698        case font_math_control_code:
699            {
700                halfword fnt = tex_scan_font_identifier(NULL);
701                cur_val = font_mathcontrol(fnt);
702                cur_val_level = integer_val_level;
703                break;
704            }
705        case font_text_control_code:
706            {
707                halfword fnt = tex_scan_font_identifier(NULL);
708                cur_val = font_textcontrol(fnt);
709                cur_val_level = integer_val_level;
710                break;
711            }
712        case math_scale_code:
713            {
714                halfword fnt = tex_scan_font_identifier(NULL);
715                if (tex_is_valid_font(fnt)) {
716                    cur_val = tex_get_math_font_scale(fnt, tex_math_style_to_size(tex_current_math_style()));
717                } else {
718                    cur_val = 1000;
719                }
720                cur_val_level = integer_val_level;
721                break;
722            }
723        case math_style_code:
724            {
725                cur_val = tex_current_math_style();
726                if (cur_val < 0) {
727                    cur_val = text_style;
728                }
729                cur_val_level = integer_val_level;
730                break;
731            }
732        case math_main_style_code:
733            {
734                cur_val = tex_current_math_main_style();
735                if (cur_val < 0) {
736                    cur_val = text_style;
737                }
738                cur_val_level = integer_val_level;
739                break;
740            }
741        case math_style_font_id_code:
742            {
743                halfword style = tex_scan_math_style_identifier(0, 0);
744                halfword family = tex_scan_math_family_number();
745                cur_val = tex_fam_fnt(family, tex_size_of_style(style));
746                cur_val_level = integer_val_level;
747                break;
748            }
749        case math_stack_style_code:
750            {
751                cur_val = tex_math_style_variant(cur_list.math_style, math_parameter_stack_variant);
752                if (cur_val < 0) {
753                    cur_val = text_style;
754                }
755                cur_val_level = integer_val_level;
756                break;
757            }
758        case math_char_class_code:
759        case math_char_fam_code:
760        case math_char_slot_code:
761            /* we actually need two commands or we need to look ahead */
762            {
763                mathcodeval mval = tex_no_math_code();
764                mathdictval dval = { 0, 0, 0 };
765                if (tex_scan_math_cmd_val(&mval, &dval)) {
766                    switch (code) {
767                        case math_char_class_code:
768                            cur_val = mval.class_value;
769                            break;
770                        case math_char_fam_code:
771                            cur_val = mval.family_value;
772                            break;
773                        case math_char_slot_code:
774                            cur_val = mval.character_value;
775                            break;
776                        default:
777                            cur_val = 0;
778                            break;
779                    }
780                } else {
781                     cur_val = 0;
782                }
783                cur_val_level = integer_val_level;
784                break;
785            }
786        case scaled_slant_per_point_code:
787        case scaled_interword_space_code:
788        case scaled_interword_stretch_code:
789        case scaled_interword_shrink_code:
790        case scaled_ex_height_code:
791        case scaled_em_width_code:
792        case scaled_extra_space_code:
793            {
794                cur_val =  tex_get_scaled_parameter(cur_font_par, (code - scaled_slant_per_point_code + 1));
795                cur_val_level = dimension_val_level;
796                break;
797            }
798        case scaled_math_axis_code:
799        case scaled_math_ex_height_code:
800        case scaled_math_em_width_code:            
801            {
802                halfword style = tex_scan_math_style_identifier(0, 0);
803                switch (code) { 
804                    case scaled_math_axis_code:
805                        cur_val = tex_math_parameter_x_scaled(style, math_parameter_axis);
806                        break;
807                    case scaled_math_ex_height_code:
808                        cur_val = tex_math_parameter_y_scaled(style, math_parameter_exheight);
809                        break;
810                    case scaled_math_em_width_code:            
811                        cur_val = tex_math_parameter_x_scaled(style, math_parameter_quad);
812                        break;
813                }
814                cur_val_level = dimension_val_level;
815                break;
816            }
817        case last_arguments_code:
818            {
819                cur_val = lmt_expand_state.arguments;
820                cur_val_level = integer_val_level;
821                break;
822            }
823        case parameter_count_code:
824            {
825                cur_val = tex_get_parameter_count();
826                cur_val_level = integer_val_level;
827                break;
828            }
829        case parameter_index_code:
830            {
831                cur_val = tex_get_parameter_index(tex_scan_parameter_index());
832                cur_val_level = integer_val_level;
833                break;
834            }
835        /*
836        case lua_value_function_code:
837            {
838                halfword v = scan_integer(0, NULL);
839                if (v <= 0) {
840                    tex_normal_error("luafunction", "invalid number");
841                } else {
842                    set_cur_val_by_lua_value_cmd(code);
843                }
844                return 1;
845            }
846        */
847        case insert_progress_code:
848            {
849                cur_val = tex_get_insert_progress(tex_scan_integer(0, NULL));
850                cur_val_level = dimension_val_level;
851                break;
852            }
853        case left_margin_kern_code:
854        case right_margin_kern_code:
855            {
856                halfword v = tex_scan_integer(0, NULL);
857                halfword b = box_register(v);
858                if (b && (node_type(b) == hlist_node)) {
859                    if (code == left_margin_kern_code) {
860                        cur_val = tex_left_marginkern(box_list(b));
861                    } else {
862                        cur_val = tex_right_marginkern(box_list(b));
863                    }
864                } else {
865                    tex_normal_error("marginkern", "a hbox expected");
866                    cur_val = 0;
867                }
868                cur_val_level = dimension_val_level;
869                break;
870            }
871        case par_shape_length_code:
872        case par_shape_indent_code:
873        case par_shape_dimension_code:
874            {
875                halfword q = code - par_shape_length_code;
876                halfword v = tex_scan_integer(0, NULL);
877                if (v <= 0 || ! par_shape_par) {
878                    v = 0;
879                } else {
880                    int n = specification_count(par_shape_par);
881                    if (q == 2) {
882                        q = v % 2;
883                        v = (v + q) / 2;
884                    }
885                    if (v > n) {
886                        v = n;
887                    }
888                    if (n == 0) {
889                        v = 0;
890                    } else if (q) {
891                        v = tex_get_specification_indent(par_shape_par, v);
892                    } else {
893                        v = tex_get_specification_width(par_shape_par, v);
894                    }
895                }
896                cur_val = v;
897                cur_val_level = dimension_val_level; /* hm, also for length ? */
898                break;
899            }
900        case glue_stretch_code:
901        case glue_shrink_code:
902            {
903                halfword q = tex_scan_glue(glue_val_level, 0, 0);
904                cur_val = code == glue_stretch_code ? glue_stretch(q) : glue_shrink(q);
905                tex_flush_node(q);
906                cur_val_level = dimension_val_level;
907                break;
908            }
909        case mu_to_glue_code:
910            cur_val = tex_scan_glue(muglue_val_level, 0, 0);
911            cur_val_level = glue_val_level;
912            return 1;
913        case glue_to_mu_code:
914            cur_val = tex_scan_glue(glue_val_level, 0, 0);
915            cur_val_level = muglue_val_level;
916            return 1;
917        case numexpr_code:
918     /* case attrexpr_code: */
919            tex_aux_scan_expr(integer_val_level);
920            return 1;
921        case posexpr_code:
922            tex_aux_scan_expr(posit_val_level);
923            return 1;
924        case dimexpr_code:
925            tex_aux_scan_expr(dimension_val_level);
926            return 1;
927        case glueexpr_code:
928            tex_aux_scan_expr(glue_val_level);
929            return 1;
930        case muexpr_code:
931            tex_aux_scan_expr(muglue_val_level);
932            return 1;
933        case numexpression_code:
934            tex_aux_scan_expression(integer_val_level);
935            return 1;
936        case dimexpression_code:
937            tex_aux_scan_expression(dimension_val_level);
938            return 1;
939     // case dimen_to_scale_code:
940     //     cur_val_level = integer_val_level;
941     //     cur_val = round_xn_over_d(100, scan_dimension(0, 0, 0, 0, NULL), 65536);
942     //     return 1;
943        case numeric_scale_code:
944            cur_val_level = integer_val_level;
945            cur_val = tex_scan_scale(0);
946            return 1;
947        case numeric_scaled_code:
948            {
949                scaled n = tex_scan_scale(0);
950                scaled i = tex_scan_integer(0, NULL);
951                cur_val_level = integer_val_level;
952                cur_val = tex_xn_over_d(i, n, scaling_factor);
953            }
954            return 1;
955        case index_of_register_code:
956            cur_val = tex_aux_scan_register_index();
957            cur_val_level = integer_val_level;
958            return 1;
959        case index_of_character_code:
960            cur_val = tex_aux_scan_character_index();
961            cur_val_level = integer_val_level;
962            return 1;
963        case last_chk_integer_code:
964            cur_val_level = integer_val_level;
965            cur_val = lmt_condition_state.chk_integer;
966            return 1;
967        case last_chk_dimension_code:
968            cur_val_level = dimension_val_level;
969            cur_val = lmt_condition_state.chk_dimension;
970            return 1;
971        case last_left_class_code:
972            cur_val_level = integer_val_level;
973            cur_val = lmt_math_state.last_left;
974            if (! valid_math_class_code(cur_val)) {
975                cur_val = unset_noad_class;
976            }
977            return 1;
978        case last_right_class_code:
979            cur_val_level = integer_val_level;
980            cur_val = lmt_math_state.last_right;
981            if (! valid_math_class_code(cur_val)) {
982                cur_val = unset_noad_class;
983            }
984            return 1;
985        case last_atom_class_code:
986            cur_val_level = integer_val_level;
987            cur_val = lmt_math_state.last_atom;
988            if (! valid_math_class_code(cur_val)) {
989                cur_val = unset_noad_class;
990            }
991            return 1;
992        case nested_loop_iterator_code:
993            cur_val = tex_nested_loop_iterator();
994            cur_val_level = integer_val_level;
995            return 1;
996        case previous_loop_iterator_code:
997            cur_val = tex_previous_loop_iterator();
998            cur_val_level = integer_val_level;
999            return 1;
1000        case current_loop_iterator_code:
1001        case last_loop_iterator_code:
1002            cur_val_level = integer_val_level;
1003            cur_val = lmt_main_control_state.loop_iterator;
1004            return 1;
1005        case current_loop_nesting_code:
1006            cur_val_level = integer_val_level;
1007            cur_val = lmt_main_control_state.loop_nesting;
1008            return 1;
1009        case last_par_trigger_code:
1010            cur_val_level = integer_val_level;
1011            cur_val = lmt_main_control_state.last_par_trigger;
1012            return 1;
1013        case last_par_context_code:
1014            cur_val_level = integer_val_level;
1015            cur_val = lmt_main_control_state.last_par_context;
1016            return 1;
1017        case last_page_extra_code:
1018            cur_val_level = integer_val_level;
1019            cur_val = lmt_page_builder_state.last_extra_used;
1020            return 1;
1021        case math_atom_glue_code:
1022            {
1023                halfword style = tex_scan_math_style_identifier(0, 0);
1024                halfword leftclass = tex_scan_math_class_number(0);
1025                halfword rightclass = tex_scan_math_class_number(0);
1026                cur_val = tex_math_spacing_glue(leftclass, rightclass, style);
1027                cur_val_level = muglue_val_level;
1028                break;
1029            }
1030    }
1031    return 0;
1032}
1033
1034static void tex_aux_set_cur_val_by_auxiliary_cmd(int code)
1035{
1036    switch (code) {
1037        case space_factor_code:
1038            if (is_h_mode(cur_list.mode)) {
1039                cur_val = cur_list.space_factor;
1040            } else {
1041                tex_handle_error(normal_error_type, "Improper %C", auxiliary_cmd, code,
1042                    "You can refer to \\spacefactor only in horizontal mode and not in \n"
1043                    "inside \\write. So I'm forgetting what you said and using zero instead."
1044                );
1045                cur_val = 0;
1046            }
1047            cur_val_level = integer_val_level;
1048            break;
1049        case prev_depth_code:
1050            if (is_v_mode(cur_list.mode)) {
1051                cur_val = cur_list.prev_depth;
1052            } else {
1053                tex_handle_error(normal_error_type, "Improper %C", auxiliary_cmd, code,
1054                    "You can refer to \\prevdepth only in horizontal mode and not in \n"
1055                    "inside \\write. So I'm forgetting what you said and using zero instead."
1056                );
1057                cur_val = 0;
1058            }
1059            cur_val_level = dimension_val_level;
1060            break;
1061        case prev_graf_code:
1062            if (cur_list.mode == nomode) {
1063                /*tex So |prev_graf=0| within |\write|, not that we have that. */
1064                cur_val = 0;
1065            } else {
1066                cur_val = lmt_nest_state.nest[tex_vmode_nest_index()].prev_graf;
1067            }
1068            cur_val_level = integer_val_level;
1069            break;
1070        case interaction_mode_code:
1071            cur_val = lmt_error_state.interaction;
1072            cur_val_level = integer_val_level;
1073            break;
1074        case insert_mode_code:
1075            cur_val = lmt_insert_state.mode;
1076            cur_val_level = integer_val_level;
1077            break;
1078    }
1079}
1080
1081static void tex_aux_set_cur_val_by_specification_cmd(int code)
1082{
1083    switch (code) { 
1084        case internal_specification_location(par_shape_code):
1085            {
1086                cur_val = tex_get_specification_count(par_shape_par);
1087                break;
1088            }
1089        case internal_specification_location(par_passes_code):
1090            {
1091                cur_val = tex_get_specification_count(par_passes_par);
1092                break;
1093            }
1094        default:
1095            {
1096                halfword v = tex_scan_integer(0, NULL); /* hm */
1097                halfword e = eq_value(code);
1098                if ((! e) || (v < 0)) {
1099                    cur_val = 0;
1100                } else {
1101                    cur_val = tex_get_specification_penalty(e, v > specification_count(e) ? specification_count(e) : v);
1102                }
1103                break;
1104            }
1105    }
1106    cur_val_level = integer_val_level;
1107}
1108
1109# define page_state_okay (lmt_page_builder_state.contents == contribute_nothing && ! lmt_page_builder_state.output_active)
1110
1111static void tex_aux_set_cur_val_by_page_property_cmd(int code)
1112{
1113    switch (code) {
1114        case page_goal_code:
1115            cur_val = page_state_okay ? max_dimension : lmt_page_builder_state.goal;
1116            cur_val_level = dimension_val_level;
1117            break;
1118        case page_vsize_code:
1119            cur_val = page_state_okay ? 0 : lmt_page_builder_state.vsize;
1120            cur_val_level = dimension_val_level;
1121            break;
1122        case page_total_code:
1123            cur_val = page_state_okay ? 0 : lmt_page_builder_state.total;
1124            cur_val_level = dimension_val_level;
1125            break;
1126        case page_excess_code:
1127            cur_val = page_state_okay ? 0 : lmt_page_builder_state.excess;
1128            cur_val_level = dimension_val_level;
1129            break;
1130        case page_depth_code:
1131            cur_val = page_state_okay ? 0 : lmt_page_builder_state.depth;
1132            cur_val_level = dimension_val_level;
1133            break;
1134        case page_stretch_code:                        
1135            cur_val = page_state_okay ? 0 : lmt_page_builder_state.stretch;
1136            cur_val_level = dimension_val_level;
1137            break;
1138        case page_fistretch_code:                    
1139            cur_val = page_state_okay ? 0 : lmt_page_builder_state.fistretch;
1140            cur_val_level = dimension_val_level;
1141            break;
1142        case page_filstretch_code:                    
1143            cur_val = page_state_okay ? 0 : lmt_page_builder_state.filstretch;
1144            cur_val_level = dimension_val_level;
1145            break;
1146        case page_fillstretch_code:                   
1147            cur_val = page_state_okay ? 0 : lmt_page_builder_state.fillstretch;
1148            cur_val_level = dimension_val_level;
1149            break;
1150        case page_filllstretch_code:                  
1151            cur_val = page_state_okay ? 0 : lmt_page_builder_state.filllstretch;
1152            cur_val_level = dimension_val_level;
1153            break;
1154        case page_shrink_code:                        
1155            cur_val = page_state_okay ? 0 : lmt_page_builder_state.shrink;
1156            cur_val_level = dimension_val_level;
1157            break;
1158        case page_last_height_code:
1159            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_height;
1160            cur_val_level = dimension_val_level;
1161            break;
1162        case page_last_depth_code:
1163            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_depth;
1164            cur_val_level = dimension_val_level;
1165            break;
1166        case page_last_stretch_code:
1167            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_stretch;
1168            cur_val_level = dimension_val_level;
1169            break;
1170        case page_last_fistretch_code:
1171            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_fistretch;
1172            cur_val_level = dimension_val_level;
1173            break;
1174        case page_last_filstretch_code:
1175            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_filstretch;
1176            cur_val_level = dimension_val_level;
1177            break;
1178        case page_last_fillstretch_code:
1179            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_fillstretch;
1180            cur_val_level = dimension_val_level;
1181            break;
1182        case page_last_filllstretch_code:
1183            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_filllstretch;
1184            cur_val_level = dimension_val_level;
1185            break;
1186        case page_last_shrink_code:
1187            cur_val = page_state_okay ? 0 : lmt_page_builder_state.last_shrink;
1188            cur_val_level = dimension_val_level;
1189            break;
1190        case dead_cycles_code:
1191            cur_val = lmt_page_builder_state.dead_cycles;
1192            cur_val_level = integer_val_level;
1193            break;
1194        case insert_penalties_code:
1195            cur_val = lmt_page_builder_state.insert_penalties;
1196            cur_val_level = integer_val_level;
1197            break;
1198        case insert_heights_code:
1199            cur_val = lmt_page_builder_state.insert_heights;
1200            cur_val_level = dimension_val_level;
1201            break;
1202        case insert_storing_code:
1203            cur_val = lmt_insert_state.storing;
1204            cur_val_level = integer_val_level;
1205            break;
1206        case insert_distance_code:
1207            cur_val = tex_get_insert_distance(tex_scan_integer(0, NULL));
1208            cur_val_level = glue_val_level;
1209            break;
1210        case insert_multiplier_code:
1211            cur_val = tex_get_insert_multiplier(tex_scan_integer(0, NULL));
1212            cur_val_level = integer_val_level;
1213            break;
1214        case insert_limit_code:
1215            cur_val = tex_get_insert_limit(tex_scan_integer(0, NULL));
1216            cur_val_level = dimension_val_level;
1217            break;
1218        case insert_storage_code:
1219            cur_val = tex_get_insert_storage(tex_scan_integer(0, NULL));
1220            cur_val_level = integer_val_level;
1221            break;
1222        case insert_penalty_code:
1223            cur_val = tex_get_insert_penalty(tex_scan_integer(0, NULL));
1224            cur_val_level = integer_val_level;
1225            break;
1226        case insert_maxdepth_code:
1227            cur_val = tex_get_insert_maxdepth(tex_scan_integer(0, NULL));
1228            cur_val_level = dimension_val_level;
1229            break;
1230        case insert_height_code:
1231            cur_val = tex_get_insert_height(tex_scan_integer(0, NULL));
1232            cur_val_level = dimension_val_level;
1233            break;
1234        case insert_depth_code:
1235            cur_val = tex_get_insert_depth(tex_scan_integer(0, NULL));
1236            cur_val_level = dimension_val_level;
1237            break;
1238        case insert_width_code:
1239            cur_val = tex_get_insert_width(tex_scan_integer(0, NULL));
1240            cur_val_level = dimension_val_level;
1241            break;
1242        default:
1243            tex_confusion("page property");
1244            break;
1245    }
1246}
1247
1248static void tex_aux_set_cur_val_by_define_char_cmd(int code)
1249{
1250    halfword index = tex_scan_char_number(0);
1251    switch (code) {
1252        case catcode_charcode:
1253            code = tex_get_cat_code(cat_code_table_par, index);
1254            break;
1255        case lccode_charcode:
1256            code = tex_get_lc_code(index);
1257            break;
1258        case uccode_charcode:
1259            code = tex_get_uc_code(index);
1260            break;
1261        case sfcode_charcode:
1262            code = tex_get_sf_code(index);
1263            break;
1264        case hccode_charcode:
1265            code = tex_get_hc_code(index);
1266            break;
1267        case hmcode_charcode:
1268            code = tex_get_hm_code(index);
1269            break;
1270        case amcode_charcode:
1271            code = tex_get_am_code(index);
1272            break;
1273        case mathcode_charcode:
1274        case extmathcode_charcode:
1275            code = tex_get_math_code_number(index);
1276            break;
1277        case delcode_charcode:
1278        case extdelcode_charcode:
1279            code = tex_get_del_code_number(index);
1280            break;
1281        default:
1282            tex_confusion("scan char");
1283            break;
1284    }
1285    cur_val = code;
1286    cur_val_level = integer_val_level;
1287}
1288
1289/*
1290    First, here is a short routine that is called from lua code. All the real work is delegated to
1291    |short_scan_something_internal| that is shared between this routine and |scan_something_internal|.
1292    In the end it was much cleaner to integrate |tex_aux_short_scan_something_internal| into the two
1293    switches.
1294*/
1295
1296static halfword tex_aux_scan_math_style_number(halfword code)
1297{
1298    switch (code) {
1299        case yet_unset_math_style:
1300            return tex_scan_math_style_identifier(0, 0);
1301        case scaled_math_style:
1302            return cur_list.math_scale;
1303        case former_choice_math_style:
1304            return 0;
1305        default:
1306            return code;
1307    }
1308}
1309
1310static void tex_aux_set_cur_val_by_math_style_cmd(halfword code)
1311{
1312    cur_val = tex_aux_scan_math_style_number(code);
1313    cur_val_level = integer_val_level;
1314}
1315
1316/*tex
1317
1318    OK, we're ready for |scan_something_internal| itself. A second parameter, |negative|, is set
1319    |true| if the value that is found should be negated. It is assumed that |cur_cmd| and |cur_chr|
1320    represent the first token of the internal quantity to be scanned; an error will be signalled if
1321    |cur_cmd < min_internal| or |cur_cmd > max_internal|.
1322
1323*/
1324
1325/*tex Fetch an internal parameter: */
1326
1327static void tex_aux_missing_number_error(void)
1328{
1329    tex_handle_error(
1330        back_error_type,
1331        "Missing number, treated as zero",
1332        "A number should have been here; I inserted '0'. (If you can't figure out why I\n"
1333        "needed to see a number, look up 'weird error' in the index to The TeXbook.)"
1334    );
1335}
1336
1337/* todo: get rid of cur_val */
1338
1339// static int tex_aux_valid_tok_level(halfword level)
1340// {
1341//     if (level == token_val_level) {
1342//         return 1;
1343//     } else {
1344//         if (lmt_error_state.intercept) {
1345//             lmt_error_state.last_intercept = 1 ;
1346//         } else {
1347//             tex_aux_missing_number_error();
1348//         }
1349//         cur_val = 0;
1350//         cur_val_level = dimension_val_level; /* why dimen */
1351//         return 0;
1352//     }
1353// }
1354
1355static int tex_aux_scan_hyph_data_number(halfword code, halfword *target)
1356{
1357    switch (code) {
1358        case prehyphenchar_code:
1359            *target = tex_get_pre_hyphen_char(language_par);
1360            break;
1361        case posthyphenchar_code:
1362            *target = tex_get_post_hyphen_char(language_par);
1363            break;
1364        case preexhyphenchar_code:
1365            *target = tex_get_pre_exhyphen_char(language_par);
1366            break;
1367        case postexhyphenchar_code:
1368            *target = tex_get_post_exhyphen_char(language_par);
1369            break;
1370        case hyphenationmin_code:
1371            *target = tex_get_hyphenation_min(language_par);
1372            break;
1373        case hjcode_code:
1374            *target = tex_get_hj_code(language_par, tex_scan_integer(0, NULL));
1375            break;
1376        default:
1377            return 0;
1378    }
1379    return 1;
1380}
1381
1382static halfword tex_aux_scan_something_internal(halfword cmd, halfword chr, int level, int negative, halfword property)
1383{
1384    int succeeded = 1;
1385    switch (cmd) {
1386        /* begin of tex_aux_short_scan_something_internal */
1387        case char_given_cmd:
1388            cur_val = chr;
1389            cur_val_level = integer_val_level;
1390            break;
1391        case some_item_cmd:
1392            {
1393                /*tex
1394                    Because the items in this case directly refer to |cur_chr|, it needs to be saved
1395                    and restored.
1396                */
1397                int save_cur_chr = cur_chr;
1398                cur_chr = chr;
1399                if (tex_aux_set_cur_val_by_some_cmd(chr)) {
1400                    succeeded = 2;
1401                } else {
1402                    cur_chr = save_cur_chr;
1403                }
1404                break;
1405            }
1406        case internal_toks_cmd:
1407        case register_toks_cmd:
1408            cur_val = eq_value(chr);
1409            cur_val_level = token_val_level;
1410            break;
1411        case internal_integer_cmd:
1412        case register_integer_cmd:
1413        case internal_attribute_cmd:
1414        case register_attribute_cmd:
1415            cur_val = eq_value(chr);
1416            cur_val_level = integer_val_level;
1417            if (level == posit_val_level) {
1418                cur_val = (halfword) tex_posit_to_integer(cur_val);
1419            }
1420            break;
1421        case internal_posit_cmd:
1422        case register_posit_cmd:
1423            cur_val = eq_value(chr);
1424            cur_val_level = posit_val_level;
1425            break;
1426        case internal_dimension_cmd:
1427        case register_dimension_cmd:
1428            cur_val = eq_value(chr);
1429            cur_val_level = dimension_val_level;
1430            if (level == posit_val_level) {
1431                cur_val = tex_posit_to_dimension(cur_val);
1432            }
1433            break;
1434        case internal_glue_cmd:
1435        case register_glue_cmd:
1436            cur_val = eq_value(chr);
1437            cur_val_level = glue_val_level;
1438            break;
1439        case internal_muglue_cmd:
1440        case register_muglue_cmd:
1441            cur_val = eq_value(chr);
1442            cur_val_level = muglue_val_level;
1443            break;
1444        case lua_value_cmd:
1445            tex_aux_set_cur_val_by_lua_value_cmd(chr, property);
1446            if (cur_val_level == no_val_level) {
1447                return 0;
1448            }
1449            break;
1450        case iterator_value_cmd:
1451            cur_val = chr > 0x100000 ? - (chr - 0x100000) : chr;
1452            cur_val_level = integer_val_level;
1453            break;
1454        case math_style_cmd:
1455            tex_aux_set_cur_val_by_math_style_cmd(chr);
1456            break;
1457        case auxiliary_cmd:
1458            tex_aux_set_cur_val_by_auxiliary_cmd(chr);
1459            break;
1460        case page_property_cmd:
1461            tex_aux_set_cur_val_by_page_property_cmd(chr);
1462            break;
1463        case specification_cmd:
1464            tex_aux_set_cur_val_by_specification_cmd(chr);
1465            break;
1466        case define_char_code_cmd:
1467            tex_aux_set_cur_val_by_define_char_cmd(chr);
1468            break;
1469        /* end of tex_aux_short_scan_something_internal */
1470        case define_font_cmd:
1471         // if (tex_aux_valid_tok_level(level)) {
1472            if (level == token_val_level) { /* Is this test still needed? */
1473                cur_val = cur_font_par;
1474                cur_val_level = font_val_level;
1475                return cur_val;
1476            } else {
1477                break;
1478            }
1479        case set_font_cmd:
1480         // if (tex_aux_valid_tok_level(level)) {
1481            if (level == token_val_level) { /* Is this test still needed? */
1482                cur_val = cur_chr;
1483                cur_val_level = font_val_level;
1484                return cur_val;
1485            } else {
1486                break;
1487            }
1488        case define_family_cmd:
1489            /*tex Fetch a math font identifier. */
1490            {
1491                halfword fam = tex_scan_math_family_number();
1492                cur_val = tex_fam_fnt(fam, chr);
1493                cur_val_level = font_val_level;
1494                return cur_val;
1495            }
1496        case math_parameter_cmd:
1497            {
1498                switch (chr) {
1499                    case math_parameter_reset_spacing:
1500                    case math_parameter_set_spacing:
1501                    case math_parameter_let_spacing:
1502                    case math_parameter_copy_spacing:
1503                        {
1504                            halfword left = tex_scan_math_class_number(0);
1505                            halfword right = tex_scan_math_class_number(0);
1506                            halfword style = tex_scan_math_style_identifier(0, 0);
1507                            halfword node = tex_math_spacing_glue(left, right, style);
1508                            cur_val = node ? node : zero_glue;
1509                            cur_val_level = muglue_val_level;
1510                            break;
1511                        }
1512                    case math_parameter_set_atom_rule:
1513                    case math_parameter_let_atom_rule:
1514                    case math_parameter_copy_atom_rule:
1515                 // case math_parameter_let_parent:
1516                    case math_parameter_copy_parent:
1517                    case math_parameter_set_defaults:
1518                        {
1519                         // cur_val = 0;
1520                         // cur_val_level = integer_val_level;
1521                            break;
1522                        }
1523                    case math_parameter_let_parent:
1524                        {
1525                            halfword mathclass = tex_scan_math_class_number(0);
1526                            if (valid_math_class_code(mathclass)) {
1527                                cur_val = tex_math_has_class_parent(mathclass);
1528                                cur_val_level = integer_val_level;
1529                            }
1530                            break;
1531                        }
1532                    case math_parameter_set_pre_penalty:
1533                    case math_parameter_set_post_penalty:
1534                    case math_parameter_set_display_pre_penalty:
1535                    case math_parameter_set_display_post_penalty:
1536                        {
1537                            halfword mathclass = tex_scan_math_class_number(0);
1538                            if (valid_math_class_code(mathclass)) {
1539                                switch (chr) {
1540                                    case math_parameter_set_pre_penalty:
1541                                        cur_val = count_parameter(first_math_pre_penalty_code + mathclass);
1542                                        break;
1543                                    case math_parameter_set_post_penalty:
1544                                        cur_val = count_parameter(first_math_post_penalty_code + mathclass);
1545                                        break;
1546                                    case math_parameter_set_display_pre_penalty:
1547                                        cur_val = count_parameter(first_math_display_pre_penalty_code + mathclass);
1548                                        break;
1549                                    case math_parameter_set_display_post_penalty:
1550                                        cur_val = count_parameter(first_math_display_post_penalty_code + mathclass);
1551                                        break;
1552                                }
1553                            } else {
1554                                cur_val = 0;
1555                            }
1556                            cur_val_level = integer_val_level;
1557                            break;
1558                        }
1559                    case math_parameter_ignore:
1560                        {
1561                            halfword code = tex_scan_math_parameter();
1562                            cur_val = code >= 0 ? count_parameter(first_math_ignore_code + code) : 0;
1563                            cur_val_level = integer_val_level;
1564                            break;
1565                        }
1566                    case math_parameter_options:
1567                        {
1568                            halfword mathclass = tex_scan_math_class_number(0);
1569                            if (valid_math_class_code(mathclass)) {
1570                                cur_val = count_parameter(first_math_options_code + mathclass);
1571                            } else {
1572                                cur_val = 0;
1573                            }
1574                            break;
1575                        }
1576                    default:
1577                        {
1578                            cur_val = tex_scan_math_style_identifier(0, 0);
1579                            switch (math_parameter_value_type(chr)) {
1580                                case math_integer_parameter:
1581                                    cur_val_level = integer_val_level;
1582                                    break;
1583                                case math_dimension_parameter:
1584                                    cur_val_level = dimension_val_level;
1585                                    break;
1586                                case math_muglue_parameter:
1587                                    cur_val_level = muglue_val_level;
1588                                    break;
1589                                case math_style_parameter:
1590                                    cur_val_level = integer_val_level;
1591                                    break;
1592                            }
1593                            chr = tex_get_math_parameter(cur_val, chr, NULL);
1594                            if (cur_val_level == muglue_val_level) {
1595                                switch (chr) {
1596                                    case petty_muskip_code:
1597                                        chr = petty_muskip_par;
1598                                        break;
1599                                    case tiny_muskip_code:
1600                                        chr = tiny_muskip_par;
1601                                        break;
1602                                    case thin_muskip_code:
1603                                        chr = thin_muskip_par;
1604                                        break;
1605                                    case med_muskip_code:
1606                                        chr = med_muskip_par;
1607                                        break;
1608                                    case thick_muskip_code:
1609                                        chr = thick_muskip_par;
1610                                        break;
1611                                }
1612                            }
1613                            cur_val = chr;
1614                            break;
1615                        }
1616                }
1617            }
1618            break;
1619        case box_property_cmd:
1620            {
1621                /*tex We hike on the dimen_cmd but some are integers. */
1622                halfword n = tex_scan_box_register_number();
1623                halfword b = box_register(n);
1624                switch (chr) {
1625                    case box_width_code:
1626                        cur_val = b ? box_width(b) : 0;
1627                        cur_val_level = dimension_val_level;
1628                        break;
1629                    case box_height_code:
1630                        cur_val = b ? box_height(b) : 0;
1631                        cur_val_level = dimension_val_level;
1632                        break;
1633                    case box_depth_code:
1634                        cur_val = b ? box_depth(b) : 0;
1635                        cur_val_level = dimension_val_level;
1636                        break;
1637                    case box_direction_code:
1638                        cur_val = b ? box_dir(b) : 0;
1639                        cur_val_level = integer_val_level;
1640                        break;
1641                    case box_geometry_code:
1642                        cur_val = b ? box_geometry(b) : 0;
1643                        cur_val_level = integer_val_level;
1644                        break;
1645                    case box_orientation_code:
1646                        cur_val = b ? box_orientation(b) : 0;
1647                        cur_val_level = integer_val_level;
1648                        break;
1649                    case box_anchor_code:
1650                    case box_anchors_code:
1651                        cur_val = b ? box_anchor(b) : 0;
1652                        cur_val_level = integer_val_level;
1653                        break;
1654                    case box_source_code:
1655                        cur_val = b ? box_source_anchor(b) : 0;
1656                        cur_val_level = integer_val_level;
1657                        break;
1658                    case box_target_code:
1659                        cur_val = b ? box_target_anchor(b) : 0;
1660                        cur_val_level = integer_val_level;
1661                        break;
1662                    case box_xoffset_code:
1663                        cur_val = b ? box_x_offset(b) : 0;
1664                        cur_val_level = dimension_val_level;
1665                        break;
1666                    case box_yoffset_code:
1667                        cur_val = b ? box_y_offset(b) : 0;
1668                        cur_val_level = dimension_val_level;
1669                        break;
1670                    case box_xmove_code:
1671                        cur_val = b ? (box_width(b) - box_x_offset(b)) : 0;
1672                        cur_val_level = dimension_val_level;
1673                        break;
1674                    case box_ymove_code:
1675                        cur_val = b ? (box_total(b) - box_y_offset(b)) : 0;
1676                        cur_val_level = dimension_val_level;
1677                        break;
1678                    case box_total_code:
1679                        cur_val = b ? box_total(b) : 0;
1680                        cur_val_level = dimension_val_level;
1681                        break;
1682                    case box_shift_code:
1683                        cur_val = b ? box_shift_amount(b) : 0;
1684                        cur_val_level = dimension_val_level;
1685                        break;
1686                    case box_adapt_code:
1687                        cur_val = 0;
1688                        cur_val_level = integer_val_level;
1689                        break;
1690                    case box_repack_code:
1691                        if (node_type(b) == hlist_node) {                             
1692                            cur_val = box_list(b) ? tex_natural_hsize(box_list(b), NULL) : 0;
1693                        } else {
1694                            cur_val = box_list(b) ? tex_natural_vsize(box_list(b)) : 0;
1695                        }
1696                        cur_val_level = dimension_val_level;
1697                        break;
1698                    case box_stretch_code:
1699                        cur_val = box_list(b) ? tex_stretch(b) : 0;
1700                        cur_val_level = dimension_val_level;
1701                        break;
1702                    case box_shrink_code:
1703                        cur_val = box_list(b) ? tex_shrink(b) : 0;
1704                        cur_val_level = dimension_val_level;
1705                        break;
1706                    case box_freeze_code:
1707                        cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1708                        cur_val_level = dimension_val_level;
1709                        break;
1710                    case box_limitate_code:
1711                        /* todo: return the delta */
1712                        cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1713                        cur_val_level = dimension_val_level;
1714                        break;
1715                    case box_finalize_code:
1716                        /* todo: return what? */
1717                        cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1718                        cur_val_level = dimension_val_level;
1719                        break;
1720                    case box_limit_code:
1721                        /* todo: return the delta */
1722                        cur_val = node_type(b) == hlist_node ? box_width(b) : box_total(b);
1723                        cur_val_level = dimension_val_level;
1724                        break;
1725                    case box_attribute_code:
1726                        {
1727                            halfword att = tex_scan_attribute_register_number();
1728                            cur_val = b ? tex_has_attribute(b, att, unused_attribute_value) : unused_attribute_value; /* always b */
1729                            cur_val_level = integer_val_level;
1730                            break;
1731                        }
1732                    case box_vadjust_code: 
1733                        cur_val = 0;
1734                        if (b) { 
1735                            if (box_pre_adjusted(b)) { 
1736                                cur_val |= has_pre_adjust;
1737                            }
1738                            if (box_post_adjusted(b)) { 
1739                                cur_val |= has_post_adjust;
1740                            }
1741                            if (box_pre_migrated(b)) { 
1742                                cur_val |= has_pre_migrated;
1743                            }
1744                            if (box_post_migrated(b)) { 
1745                                cur_val |= has_post_adjust;
1746                            }
1747                        }
1748                        cur_val_level = integer_val_level;
1749                        break;
1750                }
1751                break;
1752            }
1753        case font_property_cmd:
1754            /*tex Fetch a font integer or dimension. */
1755            {
1756                switch (chr) {
1757                    case font_hyphen_code:
1758                        {
1759                            halfword fnt = tex_scan_font_identifier(NULL);
1760                            cur_val = font_hyphen_char(fnt);
1761                            cur_val_level = integer_val_level;
1762                            break;
1763                        }
1764                    case font_skew_code:
1765                        {
1766                            halfword fnt = tex_scan_font_identifier(NULL);
1767                            cur_val = font_skew_char(fnt);
1768                            cur_val_level = integer_val_level;
1769                            break;
1770                        }
1771                    case font_lp_code:
1772                        {
1773                            halfword fnt = tex_scan_font_identifier(NULL);
1774                            halfword chr = tex_scan_char_number(0);
1775                            cur_val = tex_char_lp_from_font(fnt, chr);
1776                            cur_val_level = dimension_val_level;
1777                            break;
1778                        }
1779                    case font_rp_code:
1780                        {
1781                            halfword fnt = tex_scan_font_identifier(NULL);
1782                            halfword chr = tex_scan_char_number(0);
1783                            cur_val = tex_char_rp_from_font(fnt, chr);
1784                            cur_val_level = dimension_val_level;
1785                            break;
1786                        }
1787                    case font_ef_code:
1788                        {
1789                            halfword fnt = tex_scan_font_identifier(NULL);
1790                            halfword chr = tex_scan_char_number(0);
1791                            cur_val = tex_char_ef_from_font(fnt, chr);
1792                            cur_val_level = integer_val_level;
1793                            break;
1794                        }
1795                    case font_cf_code:
1796                        {
1797                            halfword fnt = tex_scan_font_identifier(NULL);
1798                            halfword chr = tex_scan_char_number(0);
1799                            cur_val = tex_char_cf_from_font(fnt, chr);
1800                            cur_val_level = integer_val_level;
1801                            break;
1802                        }
1803                    case font_dimension_code:
1804                        {
1805                            cur_val = tex_get_font_dimension();
1806                            cur_val_level = dimension_val_level;
1807                            break;
1808                        }
1809                    case scaled_font_dimension_code:
1810                        {
1811                            cur_val = tex_get_scaled_font_dimension();
1812                            cur_val_level = dimension_val_level;
1813                            break;
1814                        }
1815                }
1816                break;
1817            }
1818        case register_cmd:
1819            /*tex Fetch a register */
1820            {
1821                switch (chr) {
1822                    case integer_val_level:
1823                        {
1824                            halfword n = tex_scan_integer_register_number();
1825                            cur_val = count_register(n);
1826                            break;
1827                        }
1828                    case attribute_val_level:
1829                        {
1830                            halfword n = tex_scan_attribute_register_number();
1831                            cur_val = attribute_register(n);
1832                            break;
1833                        }
1834                    case posit_val_level:
1835                        {
1836                            halfword n = tex_scan_posit_register_number();
1837                            cur_val = posit_register(n);
1838                            break;
1839                        }
1840                    case dimension_val_level:
1841                        {
1842                            scaled n = tex_scan_dimension_register_number();
1843                            cur_val = dimension_register(n);
1844                            break;
1845                        }
1846                    case glue_val_level:
1847                        {
1848                            halfword n = tex_scan_glue_register_number();
1849                            cur_val = skip_register(n);
1850                            break;
1851                        }
1852                    case muglue_val_level:
1853                        {
1854                            halfword n = tex_scan_muglue_register_number();
1855                            cur_val = muskip_register(n);
1856                            break;
1857                        }
1858                    case token_val_level:
1859                        {
1860                            halfword n = tex_scan_toks_register_number();
1861                            cur_val = toks_register(n);
1862                            break;
1863                        }
1864                }
1865                cur_val_level = chr;
1866                break;
1867            }
1868        case ignore_something_cmd:
1869            break;
1870        case hyphenation_cmd:
1871            if (tex_aux_scan_hyph_data_number(chr, &cur_val)) {
1872                cur_val_level = integer_val_level;
1873                break;
1874            } else {
1875                goto DEFAULT;
1876            }
1877        case integer_cmd:
1878        case index_cmd:
1879            cur_val = chr;
1880            cur_val_level = integer_val_level;
1881            break;
1882        case dimension_cmd:
1883            cur_val = chr;
1884            cur_val_level = dimension_val_level;
1885            break;
1886        case posit_cmd:
1887            cur_val = chr;
1888            cur_val_level = posit_val_level;
1889            break;
1890        case gluespec_cmd:
1891            cur_val = chr;
1892            cur_val_level = glue_val_level;
1893            break;
1894        case mugluespec_cmd:
1895            cur_val = chr;
1896            cur_val_level = muglue_val_level;
1897            break;
1898        case mathspec_cmd:
1899            cur_val = chr;
1900            if (chr) {
1901                switch (node_subtype(chr)) {
1902                    case tex_mathcode:
1903                        cur_val = math_spec_value(chr);
1904                        cur_val_level = integer_val_level;
1905                        break;
1906                    case umath_mathcode:
1907                 /* case umathnum_mathcode: */
1908                    case mathspec_mathcode:
1909                        cur_val_level = mathspec_val_level;
1910                        break;
1911                    default:
1912                        cur_val = 0;
1913                        cur_val_level = integer_val_level;
1914                        break;
1915                }
1916            } else {
1917                cur_val_level = integer_val_level;
1918            }
1919            break;
1920        case fontspec_cmd:
1921            cur_val = tex_get_font_identifier(chr) ? chr : null;
1922            cur_val_level = fontspec_val_level;
1923            break;
1924        case association_cmd:
1925            switch (chr) {
1926                case unit_association_code:
1927                    cur_val = tex_get_unit_class(tex_scan_unit_register_number(0));
1928                    cur_val_level = integer_val_level;
1929                    break;
1930            }
1931            break;
1932        case begin_paragraph_cmd:
1933            switch (chr) {
1934                case snapshot_par_code:
1935                    {
1936                        halfword par = tex_find_par_par(cur_list.head);
1937                        cur_val = par ? par_state(par) : 0;
1938                        cur_val_level = integer_val_level;
1939                        break;
1940                    }
1941                /* case attribute_par_code: */
1942                case wrapup_par_code:
1943                    {
1944                        halfword par = tex_find_par_par(cur_list.head);
1945                        cur_val = par ? par_end_par_tokens(par) : null;
1946                        cur_val_level = token_val_level;
1947                        break;
1948                    }
1949                default:
1950                    goto DEFAULT;
1951            }
1952            break;
1953        /*
1954        case special_box_cmd:
1955            switch (chr) {
1956                case left_box_code:
1957                    cur_val = cur_mode == hmode ? local_left_box_par : null;
1958                    cur_val_level = list_val_level;
1959                    return cur_val;
1960                case right_box_code:
1961                    cur_val = cur_mode == hmode ? local_right_box_par : null;
1962                    cur_val_level = list_val_level;
1963                    return cur_val;
1964                default:
1965                   goto DEFAULT;
1966            }
1967            break;
1968        */
1969        /*tex 
1970            This one is not in the [min_internal_cmd,max_internal_cmd] range! 
1971        */
1972        case parameter_cmd:
1973            tex_get_x_token();
1974            if (valid_iterator_reference(cur_tok)) { 
1975                cur_val = tex_expand_iterator(cur_tok);
1976                cur_val_level = integer_val_level;
1977                break;
1978            } else { 
1979                goto DEFAULT;
1980            }
1981        case interaction_cmd:
1982            cur_val = cur_chr;
1983            cur_val_level = integer_val_level;
1984            break;
1985        default:
1986          DEFAULT:
1987            /*tex Complain that |\the| can not do this; give zero result. */
1988            tex_handle_error(
1989                normal_error_type,
1990                "You can't use '%C' after \\the",
1991                cmd, chr,
1992                "I'm forgetting what you said and using zero instead."
1993            );
1994            cur_val = 0;
1995            cur_val_level = (level == token_val_level) ? integer_val_level : dimension_val_level;
1996            break;
1997    }
1998    tex_aux_downgrade_cur_val(level, succeeded, negative);
1999    return cur_val;
2000}
2001
2002void tex_scan_something_simple(halfword cmd, halfword chr)
2003{
2004    if (cmd >= min_internal_cmd && cmd <= max_internal_cmd) {
2005        tex_aux_scan_something_internal(cmd, chr, no_val_level, 0, 0);
2006    } else {
2007     // tex_handle_error(
2008     //     normal_error_type,
2009     //     "You can't use '%C' as (Lua) tex library index",
2010     //     cmd, chr,
2011     //     "I'm forgetting what you said and using zero instead."
2012     // );
2013        cur_val = 0;
2014        cur_val_level = integer_val_level;
2015    }
2016}
2017
2018/*tex
2019
2020    It is nice to have routines that say what they do, so the original |scan_eight_bit_int| is
2021    superceded by |scan_register_number| and |scan_mark_number|. It may become split up even further
2022    in the future.
2023
2024    Many of the |restricted classes| routines are the essentially the same except for the upper
2025    limit and the error message, so it makes sense to combine these all into one function.
2026
2027*/
2028
2029inline static halfword tex_aux_scan_limited_int(int optional_equal, int min, int max, const char *invalid)
2030{
2031    halfword v = tex_scan_integer(optional_equal, NULL);
2032    if (v < min || v > max) {
2033        tex_handle_error(
2034            normal_error_type,
2035            "%s (%i) should be in the range %i..%i",
2036            invalid, v, min, max,
2037            "I'm going to use 0 instead of that illegal code value."
2038        );
2039        return 0;
2040    } else {
2041        return v;
2042    }
2043}
2044
2045halfword   tex_scan_integer_register_number   (void)               { return tex_aux_scan_limited_int(0, 0, max_integer_register_index, "Integer register index"); }
2046halfword   tex_scan_dimension_register_number (void)               { return tex_aux_scan_limited_int(0, 0, max_dimension_register_index, "Dimension register index"); }
2047halfword   tex_scan_attribute_register_number (void)               { return tex_aux_scan_limited_int(0, 0, max_attribute_register_index, "Attribute register index"); }
2048halfword   tex_scan_posit_register_number     (void)               { return tex_aux_scan_limited_int(0, 0, max_posit_register_index, "Posit register index"); }
2049halfword   tex_scan_glue_register_number      (void)               { return tex_aux_scan_limited_int(0, 0, max_glue_register_index, "Glue register index"); }
2050halfword   tex_scan_muglue_register_number    (void)               { return tex_aux_scan_limited_int(0, 0, max_muglue_register_index, "Muglue register index"); }
2051halfword   tex_scan_toks_register_number      (void)               { return tex_aux_scan_limited_int(0, 0, max_toks_register_index, "Toks register index"); }
2052halfword   tex_scan_box_register_number       (void)               { return tex_aux_scan_limited_int(0, 0, max_box_register_index, "Box register index"); }
2053halfword   tex_scan_unit_register_number      (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_unit_register_index, "Unit register index"); }
2054halfword   tex_scan_mark_number               (void)               { return tex_aux_scan_limited_int(0, 0, max_mark_index, "Marks index"); }
2055halfword   tex_scan_char_number               (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_character_code, "Character code"); }
2056halfword   tex_scan_math_char_number          (void)               { return tex_aux_scan_limited_int(0, 0, max_math_character_code, "Character code"); }
2057halfword   tex_scan_math_family_number        (void)               { return tex_aux_scan_limited_int(0, 0, max_math_family_index, "Math family"); }
2058halfword   tex_scan_math_properties_number    (void)               { return tex_aux_scan_limited_int(0, 0, max_math_property, "Math properties"); }
2059halfword   tex_scan_math_group_number         (void)               { return tex_aux_scan_limited_int(0, 0, max_math_group, "Math group"); }
2060halfword   tex_scan_math_index_number         (void)               { return tex_aux_scan_limited_int(0, 0, max_math_index, "Math index"); }
2061halfword   tex_scan_math_discretionary_number (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_math_discretionary, "Math discretionary"); }
2062singleword tex_scan_box_index                 (void)               { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_index, "Box index"); }
2063singleword tex_scan_box_axis                  (void)               { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_axis, "Box axis"); }
2064halfword   tex_scan_category_code             (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_category_code,"Category code"); }
2065halfword   tex_scan_space_factor              (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_space_factor, max_space_factor, "Space factor"); }
2066halfword   tex_scan_scale_factor              (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_scale_factor, max_scale_factor, "Scale factor"); }
2067halfword   tex_scan_function_reference        (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_function_reference, "Function reference"); }
2068halfword   tex_scan_bytecode_reference        (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_bytecode_index, "Bytecode reference"); }
2069halfword   tex_scan_limited_scale             (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, -max_limited_scale, max_limited_scale, "Limited scale"); }
2070halfword   tex_scan_positive_scale            (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, min_limited_scale, max_limited_scale, "Limited scale"); }
2071halfword   tex_scan_positive_number           (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_integer, "Positive number"); }
2072halfword   tex_scan_parameter_index           (void)               { return tex_aux_scan_limited_int(0, 0, 15, "Parameter index"); }
2073
2074halfword   tex_scan_math_class_number(int optional_equal) 
2075{ 
2076    halfword v = tex_aux_scan_limited_int(optional_equal, -1, max_math_class_code + 1, "Math class"); 
2077    if (v >= 0 && v <= max_math_class_code) {
2078        return v;
2079    } else { 
2080        return unset_noad_class;
2081    }
2082}
2083
2084/*tex
2085
2086    An integer number can be preceded by any number of spaces and |+| or |-| signs. Then comes
2087    either a decimal constant (i.e., radix 10), an octal constant (i.e., radix 8, preceded by~|'|),
2088    a hexadecimal constant (radix 16, preceded by~|"|), an alphabetic constant (preceded by~|`|),
2089    or an internal variable. After scanning is complete, |cur_val| will contain the answer, which
2090    must be at most $2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to 10, 8,
2091    or 16 in the cases of decimal, octal, or hexadecimal constants, otherwise |radix| is set to
2092    zero. An optional space follows a constant.
2093
2094    The |scan_int| routine is used also to scan the integer part of a fraction; for example, the
2095    |3| in |3.14159| will be found by |scan_int|. The |scan_dimension| routine assumes that |cur_tok
2096    = point_token| after the integer part of such a fraction has been scanned by |scan_int|, and
2097    that the decimal point has been backed up to be scanned again.
2098
2099*/
2100
2101static void tex_aux_number_to_big_error(void)
2102{
2103    tex_handle_error(
2104        normal_error_type,
2105        "Number too big",
2106        "I can only go up to 2147483647 = '17777777777 = \"7FFFFFFF, so I'm using that\n"
2107        "number instead of yours."
2108    );
2109}
2110
2111static void tex_aux_improper_constant_error(void)
2112{
2113    tex_handle_error(
2114        back_error_type,
2115        "Improper alphabetic constant",
2116        "A one-character control sequence belongs after a ` mark. So I'm essentially\n"
2117        "inserting \\0 here."
2118    );
2119}
2120
2121/*tex
2122
2123    The next function is somewhat special. It is also called in other scanners and therefore
2124    |cur_val| cannot simply be replaced. For that reason we do return the value but also set
2125    |cur_val|, just in case. I might sort this out some day when other stuff has been reworked.
2126
2127    The routine has been optimnized a bit (equal scanning and such) and after a while I decided to
2128    split the three cases. It makes for a bit nicer code.
2129
2130    If we backport the checking code to \LUATEX, a pre May 24 2020 copy has to be taken, because
2131    that is closer to the original.
2132
2133*/
2134
2135 
2136static void tex_aux_scan_integer_no_number() 
2137{
2138    /*tex Express astonishment that no number was here. */
2139    if (lmt_error_state.intercept) {
2140        lmt_error_state.last_intercept = 1 ;
2141        if (cur_cmd != spacer_cmd) {
2142            tex_back_input(cur_tok);
2143        }
2144    } else {
2145        tex_aux_missing_number_error();
2146    }
2147}
2148
2149halfword tex_scan_integer(int optional_equal, int *radix)
2150{
2151    bool negative = false;
2152    long long result = 0;
2153    while (1) {
2154        tex_get_x_token();
2155        if (cur_cmd == spacer_cmd) {
2156            continue;
2157        } else if (cur_tok == equal_token) {
2158            if (optional_equal) {
2159                optional_equal = 0;
2160                continue;
2161            } else { 
2162                break;
2163            }
2164        } else if (cur_tok == minus_token) {
2165            negative = ! negative;
2166        } else if (cur_tok != plus_token) {
2167            break;
2168        }
2169    };
2170    if (cur_tok == alpha_token) {
2171        /*tex
2172            Scan an alphabetic character code into |result|. A space is ignored after an alphabetic
2173            character constant, so that such constants behave like numeric ones. We don't expand the
2174            next token!
2175        */
2176        tex_get_token();
2177        if (cur_tok < cs_token_flag) {
2178            result = cur_chr;
2179            if (cur_cmd == right_brace_cmd) {
2180               ++lmt_input_state.align_state;
2181        //  } else if (cur_cmd < right_brace_cmd) {
2182            } else if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
2183                /* left_brace_cmd or relax_cmd (really?)*/
2184               --lmt_input_state.align_state;
2185            }
2186        } else {
2187            /*tex
2188                The value of a csname in this context is its name. A single letter case happens more
2189                frequently than an active character but both seldom are ran into anyway.
2190            */
2191            strnumber txt = cs_text(cur_tok - cs_token_flag);
2192            if (tex_single_letter(txt)) {
2193                result = aux_str2uni(str_string(txt));
2194            } else if (tex_is_active_cs(txt)) {
2195                result = active_cs_value(txt);
2196            } else {
2197                result = max_character_code + 1;
2198            }
2199        }
2200        if (result > max_character_code) {
2201            if (lmt_error_state.intercept) {
2202                lmt_error_state.last_intercept = 1;
2203                tex_back_input(cur_tok);
2204            } else {
2205                tex_aux_improper_constant_error();
2206                return 0;
2207            }
2208        } else {
2209            /*tex Scan an optional space. */
2210            tex_get_x_token();
2211            if (cur_cmd != spacer_cmd) {
2212                tex_back_input(cur_tok);
2213            }
2214        }
2215    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
2216        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2217        if (cur_val_level != integer_val_level) {
2218            tex_aux_scan_integer_no_number();
2219            return 0;
2220        }
2221    } else {
2222        /*tex has an error message been issued? */
2223        bool vacuous = true;
2224        bool ok_so_far = true;
2225        /*tex
2226            Scan a numeric constant. The interwoven common loop has been split up now.
2227        */
2228        switch (cur_tok) {
2229            case octal_token:
2230                {
2231                    if (radix) {
2232                        *radix = 8;
2233                    }
2234                    while (1) {
2235                        unsigned d = 0;
2236                        tex_get_x_token();
2237                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
2238                            d = cur_tok - zero_token;
2239                        } else {
2240                            goto DONE;
2241                        }
2242                        vacuous = false;
2243                        if (ok_so_far) {
2244                            result = result * 8 + d;
2245                            if (result > max_integer) {
2246                                result = max_integer;
2247                                if (lmt_error_state.intercept) {
2248                                    vacuous = true;
2249                                    goto DONE;
2250                                } else {
2251                                    tex_aux_number_to_big_error();
2252                                }
2253                                ok_so_far = 0;
2254                            }
2255                        }
2256                    }
2257                 // break;
2258                }
2259            case hex_token:
2260                {
2261                    if (radix) {
2262                        *radix = 16;
2263                    }
2264                    while (1) {
2265                        unsigned d = 0;
2266                        tex_get_x_token();
2267                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2268                            d = cur_tok - zero_token;
2269                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
2270                            d = cur_tok - A_token_l + 10;
2271                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
2272                            d = cur_tok - A_token_o + 10;
2273                        } else {
2274                            goto DONE;
2275                        }
2276                        vacuous = false;
2277                        if (ok_so_far) {
2278                            result = result * 16 + d;
2279                            if (result > max_integer) {
2280                                result = max_integer;
2281                                if (lmt_error_state.intercept) {
2282                                    vacuous = true;
2283                                    goto DONE;
2284                                } else {
2285                                    tex_aux_number_to_big_error();
2286                                }
2287                                ok_so_far = false;
2288                            }
2289                        }
2290                    }
2291                 // break;
2292                }
2293            default:
2294                if (radix) {
2295                    *radix = 10;
2296                }
2297                while (1) {
2298                    unsigned d = 0;
2299                    if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2300                        d = cur_tok - zero_token;
2301                    } else {
2302                        goto DONE;
2303                    }
2304                    vacuous = false;
2305                    if (ok_so_far) {
2306                        result = result * 10 + d;
2307                        if (result > max_integer) {
2308                            result = max_integer;
2309                            if (lmt_error_state.intercept) {
2310                                vacuous = true;
2311                                goto DONE;
2312                            } else {
2313                                tex_aux_number_to_big_error();
2314                            }
2315                            ok_so_far = false;
2316                        }
2317                    }
2318                    tex_get_x_token();
2319                }
2320                // break;
2321        }
2322      DONE:
2323        if (vacuous) {
2324            tex_aux_scan_integer_no_number();
2325        } else {
2326            tex_push_back(cur_tok, cur_cmd, cur_chr);
2327        }
2328    }
2329    /*tex For now we still keep |cur_val| set too. */
2330    cur_val = (halfword) (negative ? - result : result);
2331    return cur_val;
2332}
2333
2334void tex_scan_integer_validate(void)
2335{
2336    while (1) {
2337        tex_get_x_token();
2338        if (cur_cmd == spacer_cmd || cur_tok == minus_token) {
2339            continue;
2340        } else if (cur_tok != plus_token) {
2341            break;
2342        }
2343    };
2344    if (cur_tok == alpha_token) {
2345        long long result = 0;
2346        tex_get_token();
2347        if (cur_tok < cs_token_flag) {
2348            result = cur_chr;
2349            /* Really when validating? */
2350            if (cur_cmd == right_brace_cmd) {
2351               ++lmt_input_state.align_state;
2352            } else if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
2353               --lmt_input_state.align_state;
2354            }
2355        } else {
2356            strnumber txt = cs_text(cur_tok - cs_token_flag);
2357            if (tex_single_letter(txt)) {
2358                result = aux_str2uni(str_string(txt));
2359            } else if (tex_is_active_cs(txt)) {
2360                result = active_cs_value(txt);
2361            } else {
2362                result = max_character_code + 1;
2363            }
2364        }
2365        if (result > max_character_code) {
2366            if (lmt_error_state.intercept) {
2367                lmt_error_state.last_intercept = 1;
2368                tex_back_input(cur_tok);
2369            } else {
2370                tex_aux_improper_constant_error();
2371            }
2372        } else {
2373            tex_get_x_token();
2374            if (cur_cmd != spacer_cmd) {
2375                tex_back_input(cur_tok);
2376            }
2377        }
2378    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
2379        tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2380        if (cur_val_level != integer_val_level) {
2381            tex_aux_scan_integer_no_number();
2382        }
2383    } else {
2384        bool vacuous = true;
2385        switch (cur_tok) {
2386            case octal_token:
2387                while (1) {
2388                    tex_get_x_token();
2389                    if (! (cur_tok >= zero_token && cur_tok <= seven_token)) {
2390                        goto DONE;
2391                    }
2392                    vacuous = false;
2393                }
2394            case hex_token:
2395                while (1) {
2396                    tex_get_x_token();
2397                    if (! ((cur_tok >= zero_token && cur_tok <= nine_token) ||
2398                            (cur_tok >= A_token_l  && cur_tok <= F_token_l ) || 
2399                            (cur_tok >= A_token_o  && cur_tok <= F_token_o ) )) {
2400                        goto DONE;
2401                    }
2402                    vacuous = false;
2403                }
2404            default:
2405                while (1) {
2406                    if (! (cur_tok >= zero_token && cur_tok <= nine_token)) {
2407                        goto DONE;
2408                    }
2409                    vacuous = false;
2410                    tex_get_x_token();
2411                }
2412        }
2413      DONE:
2414        if (vacuous) {
2415            tex_aux_scan_integer_no_number();
2416        } else {
2417            tex_push_back(cur_tok, cur_cmd, cur_chr);
2418        }
2419    }
2420}
2421
2422int tex_scan_cardinal(int optional_equal, unsigned *value, int dontbark)
2423{
2424    long long result = 0;
2425 // do {
2426 //     tex_get_x_token();
2427 // } while (cur_cmd == spacer_cmd);
2428    while (1) {
2429        tex_get_x_token();
2430        if (cur_cmd != spacer_cmd) {
2431            if (optional_equal && (cur_tok == equal_token)) {
2432                optional_equal = 0;
2433            } else {
2434                break;
2435            }
2436        }
2437    }
2438    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
2439        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
2440    } else {
2441        bool vacuous = true;
2442        switch (cur_tok) {
2443            case octal_token:
2444                {
2445                    while (1) {
2446                        unsigned d = 0;
2447                        tex_get_x_token();
2448                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
2449                            d = cur_tok - zero_token;
2450                        } else {
2451                            goto DONE;
2452                        }
2453                        vacuous = false;
2454                        result = result * 8 + d;
2455                        if (result > max_cardinal) {
2456                            result = max_cardinal;
2457                        }
2458                    }
2459                 // break;
2460                }
2461            case hex_token:
2462                {
2463                    while (1) {
2464                        unsigned d = 0;
2465                        tex_get_x_token();
2466                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2467                            d = cur_tok - zero_token;
2468                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
2469                            d = cur_tok - A_token_l + 10;
2470                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
2471                            d = cur_tok - A_token_o + 10;
2472                        } else {
2473                            goto DONE;
2474                        }
2475                        vacuous = false;
2476                        result = result * 16 + d;
2477                        if (result > max_cardinal) {
2478                            result = max_cardinal;
2479                        }
2480                    }
2481                 // break;
2482                }
2483            default:
2484                {
2485                    while (1) {
2486                        unsigned d = 0;
2487                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
2488                            d = cur_tok - zero_token;
2489                        } else {
2490                            goto DONE;
2491                        }
2492                        vacuous = false;
2493                        result = result * 10 + d;
2494                        if (result > max_cardinal) {
2495                            result = max_cardinal;
2496                        }
2497                        tex_get_x_token();
2498                    }
2499                 // break;
2500                }
2501        }
2502      DONE:
2503        if (vacuous) {
2504            if (dontbark) {
2505                return 0;
2506            } else {
2507                tex_aux_missing_number_error();
2508            }
2509        } else {
2510            tex_push_back(cur_tok, cur_cmd, cur_chr);
2511        }
2512    }
2513    *value = (unsigned) result;
2514    cur_val = (halfword) result;
2515    return 1;
2516}
2517
2518/*tex
2519
2520    The following code is executed when |scan_something_internal| was called asking for |mu_val|,
2521    when we really wanted a mudimen instead of muglue.
2522
2523*/
2524
2525static halfword tex_aux_coerced_glue(halfword value, halfword level)
2526{
2527    if (level == glue_val_level || level == muglue_val_level) {
2528        int v = glue_amount(value);
2529        tex_flush_node(value);
2530        return v;
2531    } else {
2532        return value;
2533    }
2534}
2535
2536/*tex
2537
2538    The |scan_dimension| routine is similar to |scan_int|, but it sets |cur_val| to a |scaled| value,
2539    i.e., an integral number of sp. One of its main tasks is therefore to interpret the
2540    abbreviations for various kinds of units and to convert measurements to scaled points.
2541
2542    There are three parameters: |mu| is |true| if the finite units must be |mu|, while |mu| is
2543    |false| if |mu| units are disallowed; |inf| is |true| if the infinite units |fil|, |fill|,
2544    |filll| are permitted; and |shortcut| is |true| if |cur_val| already contains an integer and
2545    only the units need to be considered.
2546
2547    The order of infinity that was found in the case of infinite glue is returned in the global
2548    variable |cur_order|.
2549
2550    Constructions like |-'77 pt| are legal dimensions, so |scan_dimension| may begin with |scan_int|.
2551    This explains why it is convenient to use |scan_integer| also for the integer part of a decimal
2552    fraction.
2553
2554    Several branches of |scan_dimension| work with |cur_val| as an integer and with an auxiliary
2555    fraction |f|, so that the actual quantity of interest is $|cur_val|+|f|/2^{16}$. At the end of
2556    the routine, this \quote {unpacked} representation is put into the single word |cur_val|, which
2557    suddenly switches significance from |integer| to |scaled|.
2558
2559    The necessary conversion factors can all be specified exactly as fractions whose numerator and
2560    denominator add to 32768 or less. According to the definitions here, $\rm 2660 \, dd \approx
2561    1000.33297 \, mm$; this agrees well with the value $\rm 1000.333 \, mm$ cited by Hans Rudolf
2562    Bosshard in {\em Technische Grundlagen zur Satzherstellung} (Bern, 1980). The Didot point has
2563    been newly standardized in 1978; it's now exactly $\rm 1 \, nd = 0.375 \, mm$. Conversion uses
2564    the equation $0.375 = 21681 / 20320 / 72.27 \cdot 25.4$. The new Cicero follows the new Didot
2565    point; $\rm 1 \, nc = 12 \, nd$. These would lead to the ratios $21681 / 20320$ and $65043
2566    / 5080$, respectively. The closest approximations supported by the algorithm would be $11183 /
2567    10481$ and $1370 / 107$. In order to maintain the relation $\rm 1 \, nc = 12 \, nd$, we pick
2568    the ratio $685 / 642$ for $\rm nd$, however.
2569
2570*/
2571
2572static void tex_aux_scan_dimension_mu_error(void) {
2573    tex_handle_error(
2574        normal_error_type,
2575        "Illegal unit of measure (mu inserted)",
2576        "The unit of measurement in math glue must be mu." );
2577
2578}
2579
2580static void tex_aux_scan_dimension_fi_error(void) {
2581    tex_handle_error(
2582        normal_error_type,
2583        "Illegal unit of measure",
2584        "The unit of measurement can't be fi, fil, fill or filll here." );
2585
2586}
2587
2588static void tex_aux_scan_dimension_unknown_unit_error(void) {
2589    tex_handle_error(
2590        normal_error_type,
2591        "Illegal unit of measure (pt inserted)",
2592        "Dimensions can be in units of em, ex, sp, cm, mm, es, ts, pt, bp, dk, pc, dd\n"
2593        "cc or in; but yours is a new one! I'll assume that you meant to say pt, for\n"
2594        "printer's points: two letters."
2595    );
2596}
2597
2598/*tex 
2599    The Edith and Tove were introduced at BachoTeX 2023 and because the error message 
2600    was still in feet we decided to adapt it accordingly so now in addition it reports 
2601    different values, including Theodores little feet measured by Arthur as being roughly 
2602    five Ediths. 
2603*/
2604
2605static void tex_aux_scan_dimension_out_of_range_error(void) {
2606    tex_handle_error(
2607        normal_error_type,
2608        "Dimension too large",
2609        "I can't work with sizes bigger than about 19 feet (45 Theodores as of 2023),\n"
2610        "575 centimeters, 2300 Toves, 230 Ediths or 16383 points. Continue and I'll use\n"
2611        "the largest value I can."
2612    );
2613}
2614
2615# define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
2616
2617/*tex
2618
2619    This function sets |cur_val| to a dimension. We still have some |cur_val| sync issue so no
2620    result replacement yet. (The older variant, also already optimzied can be found in the
2621    history).
2622
2623    When order is |NULL| mu units and glue fills are not scanned.
2624
2625*/
2626
2627typedef enum scanned_unit {
2628    no_unit_scanned,         /* 0 : error */
2629    normal_unit_scanned,     /* 1 : cm mm pt bp dd cc in dk */
2630    scaled_point_scanned,    /* 2 : sp */
2631    relative_unit_scanned,   /* 3 : ex em px */
2632    math_unit_scanned,       /* 4 : mu */
2633    flexible_unit_scanned,   /* 5 : fi fil fill filll */
2634    quantitity_unit_scanned, /* 6 : internal quantity */
2635} scanned_unit;
2636
2637/*tex
2638
2639    We support the Knuthian Potrzebie cf.\ \url {https://en.wikipedia.org/wiki/Potrzebie} as the
2640    |dk| unit. It was added on 2021-09-22 exactly when we crossed the season during an evening
2641    session at the 15th \CONTEXT\ meeting in Bassenge (Boirs) Belgium. It took a few iterations to
2642    find the best numerator and denominator, but Taco Hoekwater, Harald Koenig and Mikael Sundqvist
2643    figured it out in this interactive session. The error messages have been adapted accordingly and
2644    the scanner in the |tex| library also handles it. One |dk| is 6.43985pt. There is no need to
2645    make \METAPOST\ aware of this unit because there it is just a numeric multiplier in a macro
2646    package.
2647
2648    From Wikipedia:
2649
2650    In issue 33, Mad published a partial table of the \quotation {Potrzebie System of Weights and
2651    Measures}, developed by 19-year-old Donald~E. Knuth, later a famed computer scientist. According
2652    to Knuth, the basis of this new revolutionary system is the potrzebie, which equals the thickness
2653    of Mad issue 26, or 2.2633484517438173216473 mm [...].
2654
2655    We also provide alternatives for the inch: the |es| and |ts|, two units dedicated to women 
2656    (Edith and Tove) that come close to the inch but are more metric. Their values have been 
2657    carefully callibrated at the 2023 BachoTeX meeting and a report will be published in the
2658    proceedings as well as TUGboat (medio 2023). 
2659
2660    An additional |eu| has been introduced as a multiplier for |ts| that defaults to 10 which makes 
2661    one |eu| default to one |es|. 
2662    
2663    The |true| addition has now officially been dropped. 
2664
2665*/
2666
2667/*tex 
2668    We keep this as reference:
2669*/
2670
2671// static int tex_aux_scan_unit(halfword *num, halfword *denom, halfword *value, halfword *order)
2672// {
2673// //AGAIN: /* only for true */
2674//     do {
2675//         tex_get_x_token();
2676//     } while (cur_cmd == spacer_cmd);
2677//     if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
2678//         return quantitity_unit_scanned;
2679//     } else {
2680//         int chrone, chrtwo;
2681//         halfword tokone, toktwo;
2682//         halfword save_cur_cs = cur_cs;
2683//         tokone = cur_tok;
2684//         if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2685//             chrone = cur_chr;
2686//         } else {
2687//             goto BACK_ONE;
2688//         }
2689//         tex_get_x_token();
2690//         toktwo = cur_tok;
2691//         if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2692//             chrtwo = cur_chr;
2693//         } else {
2694//             goto BACK_TWO;
2695//         }
2696//         cur_cs = save_cur_cs;
2697//         switch (chrone) {
2698//             case 'p': case 'P':
2699//                 switch (chrtwo) {
2700//                     case 't': case 'T':
2701//                         return normal_unit_scanned;
2702//                     case 'c': case 'C':
2703//                         *num = 12;
2704//                         *denom = 1;
2705//                         return normal_unit_scanned;
2706//                     case 'x': case 'X':
2707//                         *value = px_dimension_par;
2708//                         return relative_unit_scanned;
2709//                 }
2710//                 break;
2711//             case 'm': case 'M':
2712//                 switch (chrtwo) {
2713//                     case 'm': case 'M':
2714//                         *num = 7227;
2715//                         *denom = 2540;
2716//                         return normal_unit_scanned;
2717//                     case 'u': case 'U':
2718//                         if (order) {
2719//                             return math_unit_scanned;
2720//                         } else { 
2721//                             break;
2722//                         }
2723//                 }
2724//                 break;
2725//             case 'c': case 'C':
2726//                 switch (chrtwo) {
2727//                     case 'm': case 'M':
2728//                         *num = 7227;
2729//                         *denom = 254;
2730//                         return normal_unit_scanned;
2731//                     case 'c': case 'C':
2732//                         *num = 14856;
2733//                         *denom = 1157;
2734//                         return normal_unit_scanned;
2735//                 }
2736//                 break;
2737//             case 's': case 'S':
2738//                 switch (chrtwo) {
2739//                     case 'p': case 'P':
2740//                         return scaled_point_scanned;
2741//                 }
2742//                 break;
2743//             case 'b': case 'B':
2744//                 switch (chrtwo) {
2745//                     case 'p': case 'P':
2746//                         *num = 7227;
2747//                         *denom = 7200;
2748//                         return normal_unit_scanned;
2749//                 }
2750//                 break;
2751//             case 'i': case 'I':
2752//                 switch (chrtwo) {
2753//                     case 'n': case 'N':
2754//                         *num = 7227;
2755//                         *denom = 100;
2756//                         return normal_unit_scanned;
2757//                 }
2758//                 break;
2759//             case 'd': case 'D':
2760//                 switch (chrtwo) {
2761//                     case 'd': case 'D':
2762//                         *num = 1238;
2763//                         *denom = 1157;
2764//                         return normal_unit_scanned;
2765//                     case 'k': case 'K': /* number: 422042 */
2766//                         *num = 49838;  // 152940;
2767//                         *denom = 7739; //  23749;
2768//                         return normal_unit_scanned;
2769//                 }
2770//                 break;
2771//             case 't': case 'T':
2772//                 switch (chrtwo) {
2773//                     case 's': case 'S':
2774//                         *num = 4588;
2775//                         *denom = 645;
2776//                         return normal_unit_scanned;
2777//                 }
2778//              // if (order) {
2779//              //     switch (chrtwo) {
2780//              //         case 'r': case 'R':
2781//              //             if (tex_scan_mandate_keyword("true", 2)) {
2782//              //                 /*tex This is now a bogus prefix that might get dropped! */
2783//              //                 goto AGAIN;
2784//              //             }
2785//              //     }
2786//              // }
2787//                 break;
2788//             case 'e': case 'E':
2789//                 switch (chrtwo) {
2790//                     case 'm': case 'M':
2791//                         *value = tex_get_scaled_em_width(cur_font_par);
2792//                         return relative_unit_scanned;
2793//                     case 'x': case 'X':
2794//                         *value = tex_get_scaled_ex_height(cur_font_par);
2795//                         return relative_unit_scanned;
2796//                     case 's': case 'S':
2797//                         *num = 9176;
2798//                         *denom = 129;
2799//                         return normal_unit_scanned;
2800//                     case 'u': case 'U':
2801//                         *num = 9176 * eu_factor_par;
2802//                         *denom = 129 * 10;
2803//                         return normal_unit_scanned;
2804//                 }
2805//                 break;
2806//             case 'f': case 'F':
2807//                 if (order) {
2808//                     switch (chrtwo) {
2809//                         case 'i': case 'I':
2810//                             *order = fi_glue_order;
2811//                             if (tex_scan_character("lL", 0, 0, 0)) {
2812//                                 *order = fil_glue_order;
2813//                                 if (tex_scan_character("lL", 0, 0, 0)) {
2814//                                     *order = fill_glue_order;
2815//                                     if (tex_scan_character("lL", 0, 0, 0)) {
2816//                                         *order = filll_glue_order;
2817//                                     }
2818//                                 }
2819//                             }
2820//                             return flexible_unit_scanned;
2821//                     }
2822//                 }
2823//                 break;
2824//         }
2825//         /* only lowercase for now */
2826//         { 
2827//             int index = unit_parameter_index(chrone, chrtwo);
2828//             if (index >= 0) {
2829//                 halfword cs = unit_parameter(index);
2830//                 if (cs > 0) { 
2831//                     halfword cmd = eq_type(cs); 
2832//                     halfword chr = eq_value(cs);
2833//                     switch (cmd) { 
2834//                         case internal_dimension_cmd:
2835//                         case register_dimension_cmd:
2836//                             *value = eq_value(chr);
2837//                             return relative_unit_scanned;
2838//                         case dimension_cmd:
2839//                             *value = chr;
2840//                             return relative_unit_scanned;
2841//                     }
2842//                 }
2843//             }
2844//         }
2845//       BACK_TWO:
2846//         tex_back_input(toktwo);
2847//       BACK_ONE:
2848//         tex_back_input(tokone);
2849//         cur_cs = save_cur_cs;
2850//         return no_unit_scanned;
2851//     }
2852// }
2853
2854/*tex 
2855    The hash variant makes the binary some 500 bytes larger but it just looks a bit nicer and we 
2856    might need to calculate index anyway. Let the compiler sort it out. 
2857*/
2858
2859/*tex
2860    User units are bound to dimensions and some more. It could have been a callback but that is 
2861    kind of ugly at the scanner level and also bit unnatural. It would perform a bit less but this
2862    is not critical so that's not an excuse. Somehow I like it this way. We would have to pass the
2863    hash and handle it at the \LUA\ end. 
2864*/
2865
2866# define unit_hashes(a,b,c,d) \
2867         unit_parameter_hash(a,c): \
2868    case unit_parameter_hash(b,d): \
2869    case unit_parameter_hash(a,d): \
2870    case unit_parameter_hash(b,c)
2871
2872int tex_valid_userunit(halfword cmd, halfword chr, halfword cs) 
2873{
2874    (void) cs;
2875    switch (cmd) { 
2876        case call_cmd:
2877        case protected_call_cmd:
2878        case semi_protected_call_cmd:
2879        case constant_call_cmd:
2880            return chr && ! get_token_preamble(chr);
2881        case internal_dimension_cmd:
2882        case register_dimension_cmd:
2883        case dimension_cmd:
2884        case lua_value_cmd:
2885            return 1;
2886        default:
2887            return 0;
2888    }
2889}
2890
2891int tex_get_unit_class(halfword index)
2892{
2893    halfword class = unit_parameter(index);
2894    return class < 0 ? - class : class ? user_unit_class : unset_unit_class;
2895}
2896
2897int tex_get_userunit(halfword index, scaled *value)
2898{ 
2899    halfword cs = unit_parameter(index);
2900    if (cs > 0) { 
2901        halfword cmd = eq_type(cs); 
2902        halfword chr = eq_value(cs);
2903        switch (cmd) { 
2904            case internal_dimension_cmd:
2905            case register_dimension_cmd:
2906                *value = eq_value(chr);
2907                return relative_unit_scanned;
2908            case dimension_cmd:
2909                *value = chr;
2910                return relative_unit_scanned;
2911            case call_cmd:
2912            case protected_call_cmd:
2913            case semi_protected_call_cmd:
2914            case constant_call_cmd:
2915                if (chr && ! get_token_preamble(chr)) {
2916                    halfword list = token_link(chr);
2917                    tex_begin_associated_list(list);
2918                    tex_aux_scan_expr(dimension_val_level);
2919                    if (cur_val_level == dimension_val_level) { 
2920                        *value = cur_val;
2921                        return 1;
2922                    }
2923                }
2924                /*tex So we can actually have an empty macro. */
2925                *value = 0;
2926                return 1;
2927            case lua_value_cmd: 
2928                /* We pass the index but more for tracing than for usage. */
2929                tex_aux_set_cur_val_by_lua_value_cmd(chr, index);
2930                if (cur_val_level == dimension_val_level) {
2931                    *value = cur_val;
2932                    return 1;
2933             }
2934        }
2935    }
2936    /*tex We could go zero but better not. */
2937    *value = 0;
2938    return 0;
2939}
2940
2941void tex_initialize_units(void)
2942{
2943    unit_parameter(unit_parameter_hash('p','t')) = - tex_unit_class;
2944    unit_parameter(unit_parameter_hash('c','m')) = - tex_unit_class;
2945    unit_parameter(unit_parameter_hash('m','m')) = - tex_unit_class;
2946    unit_parameter(unit_parameter_hash('e','m')) = - tex_unit_class;
2947    unit_parameter(unit_parameter_hash('e','x')) = - tex_unit_class;
2948    unit_parameter(unit_parameter_hash('s','p')) = - tex_unit_class;
2949    unit_parameter(unit_parameter_hash('b','p')) = - tex_unit_class;
2950    unit_parameter(unit_parameter_hash('f','i')) = - tex_unit_class;
2951    unit_parameter(unit_parameter_hash('t','s')) = - luametatex_unit_class;
2952    unit_parameter(unit_parameter_hash('e','s')) = - luametatex_unit_class;
2953    unit_parameter(unit_parameter_hash('e','u')) = - luametatex_unit_class;
2954    unit_parameter(unit_parameter_hash('d','k')) = - luametatex_unit_class;
2955    unit_parameter(unit_parameter_hash('m','u')) = - tex_unit_class;
2956    unit_parameter(unit_parameter_hash('d','d')) = - tex_unit_class;
2957    unit_parameter(unit_parameter_hash('c','c')) = - tex_unit_class;
2958    unit_parameter(unit_parameter_hash('p','c')) = - tex_unit_class;
2959    unit_parameter(unit_parameter_hash('p','x')) = - pdftex_unit_class;
2960    unit_parameter(unit_parameter_hash('i','n')) = - tex_unit_class;
2961}
2962
2963static int tex_aux_scan_unit(halfword *num, halfword *denom, halfword *value, halfword *order)
2964{
2965//AGAIN: /* only for true */
2966    do {
2967        tex_get_x_token();
2968    } while (cur_cmd == spacer_cmd);
2969    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
2970        return quantitity_unit_scanned;
2971    } else {
2972        int chrone, chrtwo, index;
2973        halfword tokone, toktwo;
2974        halfword save_cur_cs = cur_cs;
2975        tokone = cur_tok;
2976        if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2977            chrone = cur_chr;
2978        } else {
2979            goto BACK_ONE;
2980        }
2981        tex_get_x_token();
2982        toktwo = cur_tok;
2983        if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2984            chrtwo = cur_chr;
2985        } else {
2986            goto BACK_TWO;
2987        }
2988        cur_cs = save_cur_cs;
2989        index = unit_parameter_index(chrone, chrtwo);
2990        if (index >= 0) {
2991            switch (index) { 
2992                case unit_hashes('p','P','t','T'):
2993                    return normal_unit_scanned;
2994                case unit_hashes('c','C','m','M'):
2995                    *num = 7227;
2996                    *denom = 254;
2997                    return normal_unit_scanned;
2998                case unit_hashes('m','M','m','M'):
2999                    *num = 7227;
3000                    *denom = 2540;
3001                    return normal_unit_scanned;
3002                case unit_hashes('e','E','m','M'):
3003                    *value = tex_get_scaled_em_width(cur_font_par);
3004                    return relative_unit_scanned;
3005                case unit_hashes('e','E','x','X'):
3006                    *value = tex_get_scaled_ex_height(cur_font_par);
3007                    return relative_unit_scanned;
3008                case unit_hashes('s','S','p','P'):
3009                    return scaled_point_scanned;
3010                case unit_hashes('b','B','p','P'):
3011                    *num = 7227;
3012                    *denom = 7200;
3013                    return normal_unit_scanned;
3014                case unit_hashes('f','F','i','I'):
3015                    if (order) {
3016                        *order = fi_glue_order;
3017                        if (tex_scan_character("lL", 0, 0, 0)) {
3018                            *order = fil_glue_order;
3019                            if (tex_scan_character("lL", 0, 0, 0)) {
3020                                *order = fill_glue_order;
3021                                if (tex_scan_character("lL", 0, 0, 0)) {
3022                                    *order = filll_glue_order;
3023                                }
3024                            }
3025                        }
3026                        return flexible_unit_scanned;
3027                    }
3028                    break;
3029                case unit_hashes('t','T','s','S'):
3030                    *num = 4588;
3031                    *denom = 645;
3032                    return normal_unit_scanned;
3033                case unit_hashes('e','E','s','S'):
3034                    *num = 9176;
3035                    *denom = 129;
3036                    return normal_unit_scanned;
3037                case unit_hashes('e','E','u','U'):
3038                    *num = 9176 * eu_factor_par;
3039                    *denom = 129 * 10;
3040                    return normal_unit_scanned;
3041                case unit_hashes('d','D','k','K'): /* number: 422042 */
3042                    *num = 49838;  // 152940;
3043                    *denom = 7739; //  23749;
3044                    return normal_unit_scanned;
3045                case unit_hashes('m','M','u','U'):
3046                    if (order) {
3047                        return math_unit_scanned;
3048                    } else { 
3049                        break;
3050                    }
3051                case unit_hashes('d','D','d','D'):
3052                    *num = 1238;
3053                    *denom = 1157;
3054                    return normal_unit_scanned;
3055                case unit_hashes('c','C','c','C'):
3056                    *num = 14856;
3057                    *denom = 1157;
3058                    return normal_unit_scanned;
3059                case unit_hashes('p','P','c','C'):
3060                    *num = 12;
3061                    *denom = 1;
3062                    return normal_unit_scanned;
3063                case unit_hashes('p','P','x','X'):
3064                    *value = px_dimension_par;
3065                    return relative_unit_scanned;
3066                case unit_hashes('i','I','n','N'):
3067                    *num = 7227;
3068                    *denom = 100;
3069                    return normal_unit_scanned;
3070             // case unit_hashes('t','T','r','R'):
3071             //     if (order) {
3072             //        if (tex_scan_mandate_keyword("true", 2)) {
3073             //            /*tex This is now a bogus prefix that might get dropped! */
3074             //            goto AGAIN;
3075             //        }
3076             //     }
3077             //     break;
3078                default: 
3079                    if (tex_get_userunit(index, value)) { 
3080                        return relative_unit_scanned;
3081                    }
3082            }
3083        }
3084      BACK_TWO:
3085        tex_back_input(toktwo);
3086      BACK_ONE:
3087        tex_back_input(tokone);
3088        cur_cs = save_cur_cs;
3089        return no_unit_scanned;
3090    }
3091}
3092
3093/*tex
3094    When we drop |true| support we can use the next variant which is a bit more efficient and also
3095    handles optional units. Later we will see a more limited variant that also includes the scaler.
3096    This version can still be found in the archive. 
3097
3098    The fact that we can say |.3\somedimen| is made easy because when we fail to see a unit we can 
3099    check if the seen token is some quantity. 
3100*/
3101
3102halfword tex_scan_dimension(int mu, int inf, int shortcut, int optional_equal, halfword *order)
3103{
3104    bool negative = false;
3105    int fraction = 0;
3106    int num = 0;
3107    int denom = 0;
3108    scaled v;
3109    int save_cur_val;
3110    halfword cur_order = normal_glue_order;
3111    lmt_scanner_state.arithmic_error = 0;
3112    if (! shortcut) {
3113        while (1) {
3114            tex_get_x_token();
3115            if (cur_cmd == spacer_cmd) {
3116                continue;
3117            } else if (cur_tok == equal_token) {
3118                if (optional_equal) {
3119                    optional_equal = 0;
3120                    continue;
3121                } else { 
3122                    break;
3123                }
3124            } else if (cur_tok == minus_token) {
3125                negative = ! negative;
3126            } else if (cur_tok != plus_token) {
3127                break;
3128            }
3129        }
3130        if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3131            cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, mu ? muglue_val_level : dimension_val_level, 0, 0); /* adapts cur_val_level */
3132            if (mu) {
3133                cur_val = tex_aux_coerced_glue(cur_val, cur_val_level);
3134                if (cur_val_level == muglue_val_level) {
3135                    goto ATTACH_SIGN;
3136                } else if (cur_val_level != integer_val_level) {
3137                    tex_aux_mu_error(2);
3138                }
3139            } else if (cur_val_level == dimension_val_level) {
3140                goto ATTACH_SIGN;
3141            } else if (cur_val_level == posit_val_level) {
3142                cur_val = tex_posit_to_dimension(cur_val);
3143                goto ATTACH_SIGN;
3144            }
3145        } else {
3146            int has_fraction = tex_token_is_seperator(cur_tok);
3147            if (has_fraction) {
3148                cur_val = 0;
3149            } else {
3150                int cur_radix;
3151                tex_back_input(cur_tok);
3152                cur_val = tex_scan_integer(0, &cur_radix);
3153                if (cur_radix == 10 && tex_token_is_seperator(cur_tok)) {
3154                    has_fraction = 1;
3155                    tex_get_token();
3156                }
3157            }
3158            if (has_fraction) {
3159                unsigned k = 0;
3160                unsigned char digits[18];
3161                while (1) {
3162                    tex_get_x_token();
3163                    if ((cur_tok > nine_token) || (cur_tok < zero_token)) {
3164                        break;
3165                    } else if (k < 17) {
3166                        digits[k] = (unsigned char) (cur_tok - zero_token);
3167                        ++k;
3168                    }
3169                }
3170                fraction = tex_round_decimals_digits(digits, k);
3171                if (cur_cmd != spacer_cmd) {
3172                    tex_back_input(cur_tok);
3173                }
3174            }
3175        }
3176    } else { 
3177        /* only when we scan for a glue (filler) component */
3178    }
3179    if (cur_val < 0) {
3180        negative = ! negative;
3181        cur_val = -cur_val;
3182    }
3183    save_cur_val = cur_val;
3184    /*tex
3185        Actually we have cur_tok but it's already pushed back and we also need to skip spaces so
3186        let's not overdo this.
3187    */
3188    if (! lmt_error_state.last_intercept) {
3189        switch (tex_aux_scan_unit(&num, &denom, &v, &cur_order)) {
3190            case no_unit_scanned:
3191                /* error */
3192                if (lmt_error_state.intercept) {
3193                    lmt_error_state.last_intercept = 1;
3194                } else {
3195                    tex_aux_scan_dimension_unknown_unit_error();
3196                }
3197                goto ATTACH_FRACTION;
3198            case normal_unit_scanned:
3199                /* cm mm pt bp dd cc in dk */
3200                if (mu) {
3201                    tex_aux_scan_dimension_unknown_unit_error();
3202                } else if (num) {
3203                    int remainder = 0;
3204                    cur_val = tex_xn_over_d_r(cur_val, num, denom, &remainder);
3205                    fraction = (num * fraction + 0200000 * remainder) / denom;
3206                    cur_val += fraction / 0200000;
3207                    fraction = fraction % 0200000;
3208                }
3209                goto ATTACH_FRACTION;
3210            case scaled_point_scanned:
3211                /* sp */
3212                if (mu) {
3213                    tex_aux_scan_dimension_unknown_unit_error();
3214                }
3215                goto DONE;
3216            case relative_unit_scanned:
3217                /* ex em px */
3218                if (mu) {
3219                    tex_aux_scan_dimension_unknown_unit_error();
3220                }
3221                cur_val = tex_nx_plus_y(save_cur_val, v, tex_xn_over_d(v, fraction, 0200000));
3222                goto DONE;
3223            case math_unit_scanned:
3224                /* mu (slightly different but an error anyway */
3225                if (! mu) {
3226                    tex_aux_scan_dimension_mu_error();
3227                }
3228                goto ATTACH_FRACTION;
3229            case flexible_unit_scanned:
3230                /* fi fil fill filll */
3231                if (mu) {
3232                    tex_aux_scan_dimension_unknown_unit_error();
3233                } else if (! inf) {
3234                    if (! order && lmt_error_state.intercept) {
3235                        lmt_error_state.last_intercept = 1;
3236                    } else {
3237                        tex_aux_scan_dimension_fi_error();
3238                    }
3239                }
3240                goto ATTACH_FRACTION;
3241            case quantitity_unit_scanned:
3242                /* internal quantity */
3243                cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, mu ? muglue_val_level : dimension_val_level, 0, 0); /* adapts cur_val_level */
3244                if (mu) {
3245                    cur_val = tex_aux_coerced_glue(cur_val, cur_val_level);
3246                    if (cur_val_level != muglue_val_level) {
3247                        tex_aux_mu_error(3);
3248                    }
3249                }
3250                v = cur_val;
3251                cur_val = tex_nx_plus_y(save_cur_val, v, tex_xn_over_d(v, fraction, 0200000));
3252                goto ATTACH_SIGN;
3253        }
3254    }
3255  ATTACH_FRACTION:
3256    if (cur_val >= 040000) { // 0x4000
3257        lmt_scanner_state.arithmic_error = 1;
3258    } else {
3259        cur_val = cur_val * unity + fraction;
3260    }
3261  DONE:
3262    tex_get_x_token();
3263    tex_push_back(cur_tok, cur_cmd, cur_chr);
3264  ATTACH_SIGN:
3265    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 010000000000)) { // 0x40000000
3266        if (lmt_error_state.intercept) {
3267            lmt_error_state.last_intercept = 1;
3268        } else {
3269            tex_aux_scan_dimension_out_of_range_error();
3270        }
3271        cur_val = max_dimension;
3272        lmt_scanner_state.arithmic_error = 0;
3273    }
3274    if (negative) {
3275        cur_val = -cur_val;
3276    }
3277    if (order) {
3278        *order = cur_order;
3279    }
3280    return cur_val;
3281}
3282
3283
3284void tex_scan_dimension_validate(void)
3285{
3286    lmt_scanner_state.arithmic_error = 0;
3287    while (1) {
3288        tex_get_x_token();
3289        if (cur_cmd == spacer_cmd || cur_tok == minus_token) {
3290            continue;
3291        } else if (cur_tok != plus_token) {
3292            break;
3293        }
3294    }
3295    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3296        tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
3297        if (cur_val_level == dimension_val_level || cur_val_level == posit_val_level) {
3298            return;
3299        }
3300    } else {
3301        int has_fraction = tex_token_is_seperator(cur_tok);
3302        if (! has_fraction) {
3303            int cur_radix;
3304            tex_back_input(cur_tok);
3305            tex_scan_integer(0, &cur_radix);
3306            if (cur_radix == 10 && tex_token_is_seperator(cur_tok)) {
3307                has_fraction = 1;
3308                tex_get_token();
3309            }
3310        }
3311        if (has_fraction) {
3312            while (1) {
3313                tex_get_x_token();
3314                if (cur_tok > nine_token || cur_tok < zero_token) {
3315                    break;
3316                }
3317            }
3318            if (cur_cmd != spacer_cmd) {
3319                tex_back_input(cur_tok);
3320            }
3321        }
3322    }
3323    {
3324        int num = 0;
3325        int denom = 0;
3326        scaled value;
3327        switch (tex_aux_scan_unit(&num, &denom, &value, NULL)) {
3328            case no_unit_scanned:
3329            case flexible_unit_scanned:
3330                lmt_error_state.last_intercept = 1;
3331                break;
3332            case normal_unit_scanned:
3333            case scaled_point_scanned:
3334            case relative_unit_scanned:
3335            case math_unit_scanned:
3336                break;
3337            case quantitity_unit_scanned:
3338                tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0); 
3339                return;
3340        }
3341    }
3342    tex_get_x_token();
3343    tex_push_back(cur_tok, cur_cmd, cur_chr);
3344}
3345
3346/*tex
3347
3348    The final member of \TEX's value-scanning trio is |scan_glue|, which makes |cur_val| point to
3349    a glue specification. The reference count of that glue spec will take account of the fact that
3350    |cur_val| is pointing to~it. The |level| parameter should be either |glue_val| or |mu_val|.
3351
3352    Since |scan_dimension| was so much more complex than |scan_integer|, we might expect |scan_glue| 
3353    to be even worse. But fortunately, it is very simple, since most of the work has already been 
3354    done.
3355
3356*/
3357
3358/* todo: get rid of cur_val */
3359
3360halfword tex_scan_glue(int level, int optional_equal, int options_too)
3361{
3362    /*tex should the answer be negated? */
3363    bool negative = false;
3364    /*tex new glue specification */
3365    halfword q = null;
3366    /*tex does |level=mu_val|? */
3367    int mu = level == muglue_val_level;
3368    /*tex Get the next non-blank non-sign. */
3369    do {
3370        /*tex Get the next non-blank non-call token. */
3371        while (1) {
3372            tex_get_x_token();
3373            if (cur_cmd != spacer_cmd) {
3374                if (optional_equal && (cur_tok == equal_token)) {
3375                    optional_equal = 0;
3376                } else {
3377                    break;
3378                }
3379            }
3380        }
3381        if (cur_tok == minus_token) {
3382            negative = ! negative;
3383            cur_tok = plus_token;
3384        }
3385    } while (cur_tok == plus_token);
3386    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
3387        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, level, negative, 0);
3388        if (cur_val_level >= glue_val_level) {
3389            if (cur_val_level != level) {
3390                tex_aux_mu_error(4);
3391            }
3392            return cur_val;
3393        }
3394        if (cur_val_level == integer_val_level) {
3395            cur_val = tex_scan_dimension(mu, 0, 1, 0, NULL);
3396     /* 
3397        This only works for |\mutoglue\emwidth| and not a fraction, so let's not do this now. At 
3398        some point we could consider making |\mutoglue| and |\gluetomu| more tolerant but they 
3399        are hardly used anyway.
3400     */
3401     /* } else if (cur_val_level == dimension_val_level) { */
3402     /*    mu = 0;                                         */  
3403        } else if (level == muglue_val_level) {
3404            tex_aux_mu_error(5);
3405        }
3406    } else {
3407        tex_back_input(cur_tok);
3408        cur_val = tex_scan_dimension(mu, 0, 0, 0, NULL);
3409        if (negative) {
3410            cur_val = -cur_val;
3411        }
3412    }
3413    /*tex
3414        Create a new glue specification whose width is |cur_val|; scan for its stretch and shrink
3415        components.
3416    */
3417    q = tex_new_glue_spec_node(zero_glue);
3418    glue_amount(q) = cur_val;
3419    while (1) {
3420        switch (tex_scan_character("pmlPML", 0, 1, 0)) {
3421            case 0:
3422                return q;
3423            case 'p': case 'P':
3424                if (tex_scan_mandate_keyword("plus", 1)) {
3425                    halfword order;
3426                    glue_stretch(q) = tex_scan_dimension(mu, 1, 0, 0, &order);
3427                    glue_stretch_order(q) = order;
3428                }
3429                break;
3430            case 'm': case 'M':
3431                if (tex_scan_mandate_keyword("minus", 1)) {
3432                    halfword order;
3433                    glue_shrink(q) = tex_scan_dimension(mu, 1, 0, 0, &order);
3434                    glue_shrink_order(q) = order;
3435                }
3436                break;
3437            case 'l': case 'L':
3438                if (options_too && tex_scan_mandate_keyword("limit", 1)) {
3439                    glue_options(q) |= glue_option_limit;
3440                    break;
3441                } else { 
3442                    /* fall through */
3443                }
3444            default:
3445                tex_aux_show_keyword_error(options_too ? "plus|minus|limit" : "plus|minus");
3446                return q;
3447        }
3448    }
3449}
3450
3451/*tex
3452
3453    This started as an experiment. A font object is just a container for a combination of id and
3454    scales. It permits fast font switching (not that setting the font id and scales separately is
3455    that slow) and has the benefit of a more sparse logging. We use nodes and not some array
3456    because after all we always have symbolic names and we then get saving and restoring as well as
3457    memory management for free.
3458
3459    When an spec is given we make a copy but can overload the scales after that. Otherwise we just
3460    create a new spec with default scales 1000. This fontspec object was introduced after we had
3461    experimental compact font support in \CONTEXT\ for over a year working well.
3462
3463*/
3464
3465halfword tex_scan_font(int optional_equal)
3466{
3467    halfword fv = null;
3468    halfword id, fs;
3469    if (optional_equal) {
3470        tex_scan_optional_equals();
3471    }
3472    id = tex_scan_font_identifier(&fv);
3473    if (fv) {
3474        fs = tex_copy_node(fv);
3475    } else {
3476        /*tex We create a new one and assign the mandate id. */
3477        fs = tex_new_node(font_spec_node, normal_code);
3478        font_spec_identifier(fs) = id;
3479        font_spec_scale(fs) = unused_scale_value;
3480        font_spec_x_scale(fs) = unused_scale_value;
3481        font_spec_y_scale(fs) = unused_scale_value;
3482        font_spec_slant(fs) = 0;
3483        font_spec_weight(fs) = 0;
3484    }
3485    while (1) {
3486        switch (tex_scan_character("asxywoASXYWO", 0, 1, 0)) {
3487            case 0:
3488                return fs;
3489            case 'a': case 'A':
3490                if (tex_scan_mandate_keyword("all", 1)) {
3491                    font_spec_scale(fs) = tex_scan_scale_factor(0);
3492                    font_spec_x_scale(fs) = tex_scan_scale_factor(0);
3493                    font_spec_y_scale(fs) = tex_scan_scale_factor(0);
3494                    font_spec_slant(fs) = tex_scan_scale_factor(0);
3495                    font_spec_weight(fs) =  tex_scan_scale_factor(0);
3496                }
3497                break;
3498            case 's': case 'S':
3499                switch (tex_scan_character("clCL", 0, 1, 0)) {
3500                    case 'c': case 'C':
3501                        if (tex_scan_mandate_keyword("scale", 2)) {
3502                            font_spec_scale(fs) = tex_scan_scale_factor(0);
3503                        }
3504                        break;
3505                    case 'l': case 'L':
3506                        if (tex_scan_mandate_keyword("slant", 2)) {
3507                            font_spec_slant(fs) = tex_scan_scale_factor(0);
3508                        }
3509                        break;
3510                    default:
3511                        tex_aux_show_keyword_error("scale|slant");
3512                        return fs;
3513                }
3514                break;
3515            case 'x': case 'X':
3516                if (tex_scan_mandate_keyword("xscale", 1)) {
3517                    font_spec_x_scale(fs) = tex_scan_scale_factor(0);
3518                }
3519                break;
3520            case 'y': case 'Y':
3521                if (tex_scan_mandate_keyword("yscale", 1)) {
3522                    font_spec_y_scale(fs) = tex_scan_scale_factor(0);
3523                }
3524                break;
3525            case 'w': case 'W':
3526                if (tex_scan_mandate_keyword("weight", 1)) {
3527                    font_spec_weight(fs) = tex_scan_scale_factor(0);
3528                }
3529                break;
3530            case 'o': case 'O': /* oblique */
3531                if (tex_scan_mandate_keyword("oblique", 1)) {
3532                    font_spec_slant(fs) = tex_scan_scale_factor(0);
3533                }
3534                break;
3535            default:
3536                return fs;
3537        }
3538    }
3539}
3540
3541/*tex
3542
3543    This procedure is supposed to scan something like |\skip \count 12|, i.e., whatever can follow
3544    |\the|, and it constructs a token list containing something like |-3.0pt minus 0.5 fill|.
3545
3546    There is a bit duplicate code here but it makes a nicer switch as we also need to deal with
3547    tokens and font identifiers.
3548
3549*/
3550
3551# define push_selector { \
3552    saved_selector = lmt_print_state.selector; \
3553    lmt_print_state.selector = new_string_selector_code; \
3554}
3555
3556# define pop_selector { \
3557    lmt_print_state.selector = saved_selector; \
3558}
3559
3560halfword tex_the_value_toks(int code, halfword *tail, halfword property) /* maybe split this as already checked */
3561{
3562    tex_get_x_token();
3563    cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, token_val_level, 0, property);
3564    switch (cur_val_level) {
3565        case integer_val_level:
3566        case attribute_val_level:
3567            {
3568                int saved_selector;
3569                push_selector;
3570                tex_print_int(cur_val);
3571                pop_selector;
3572                return tex_cur_str_toks(tail);
3573            }
3574        case posit_val_level:
3575            {
3576                int saved_selector;
3577                push_selector;
3578                tex_print_posit(cur_val);
3579                pop_selector;
3580                return tex_cur_str_toks(tail);
3581            }
3582        case dimension_val_level:
3583            {
3584                int saved_selector;
3585                push_selector;
3586                tex_print_dimension(cur_val, code == the_without_unit_code ? no_unit : pt_unit);
3587                pop_selector;
3588                return tex_cur_str_toks(tail);
3589            }
3590        case glue_val_level:
3591        case muglue_val_level:
3592            {
3593                int saved_selector;
3594                push_selector;
3595                tex_print_spec(cur_val, (code != the_without_unit_code) ? (cur_val_level == glue_val_level ? pt_unit : mu_unit) : no_unit);
3596                tex_flush_node(cur_val);
3597                pop_selector;
3598                return tex_cur_str_toks(tail);
3599            }
3600        case token_val_level:
3601            {
3602                /*tex Copy the token list */
3603                halfword h = null;
3604                halfword p = null;
3605                if (cur_val) {
3606                    /*tex Do not copy the reference count! */
3607                    halfword r = token_link(cur_val);
3608                    while (r) {
3609                        p = tex_store_new_token(p, token_info(r));
3610                        if (! h) {
3611                            h = p;
3612                        }
3613                        r = token_link(r);
3614                    }
3615                }
3616                if (tail) {
3617                    *tail = p;
3618                }
3619                return h;
3620            }
3621        case font_val_level:
3622            {
3623                int saved_selector;
3624                push_selector;
3625                tex_print_font_identifier(cur_val);
3626                pop_selector;
3627                return tex_cur_str_toks(tail);
3628            }
3629        case mathspec_val_level:
3630            {
3631                /*tex So we don't mess with null font. */
3632                if (cur_val) {
3633                    int saved_selector;
3634                    push_selector;
3635                    tex_print_mathspec(cur_val);
3636                    pop_selector;
3637                    return tex_cur_str_toks(tail);
3638                } else {
3639                    return null;
3640                }
3641            }
3642        case fontspec_val_level:
3643            {
3644                /*tex So we don't mess with null font. */
3645                if (cur_val) {
3646                    int saved_selector;
3647                    push_selector;
3648                    tex_print_font_specifier(cur_val);
3649                    pop_selector;
3650                    return tex_cur_str_toks(tail);
3651                } else {
3652                    return null;
3653                }
3654            }
3655        case list_val_level:
3656            {
3657                if (cur_val) {
3658                  // halfword copy = tex_copy_node_list(cur_val, null);
3659                     halfword copy = tex_copy_node(cur_val);
3660                     tex_tail_append(copy);
3661                     cur_val = null;
3662                }
3663                break;
3664            }
3665    }
3666    return null;
3667}
3668
3669void tex_detokenize_list(halfword head)
3670{
3671    int saved_selector;
3672    push_selector;
3673    tex_show_token_list(head, 0, 0);
3674    pop_selector;
3675}
3676
3677halfword tex_the_detokenized_toks(halfword *tail, int expand, int protect) // maybe we need single here too
3678{
3679    halfword head = expand ? tex_scan_toks_expand(0, tail, 1, protect) : tex_scan_general_text(tail);
3680    if (head) { 
3681        halfword first = expand ? token_link(head) : head;
3682        if (first) {
3683            int saved_selector;
3684            push_selector;
3685            tex_show_token_list(first, 0, protect); 
3686            pop_selector;
3687            tex_flush_token_list(head);
3688            return tex_cur_str_toks(tail);
3689        }
3690    } 
3691    return null;
3692}
3693
3694/*tex
3695    The |the_without_unit| variant implements |\thewithoutunit| is not really that impressive but
3696    just there because it's cheap to implement and also avoids a kind of annoying macro definition,
3697    one of the kind that demonstrates that one really understands \TEX. Now, with plenty of memory
3698    and disk space the added code is probably not noticed and adds less bytes to the binary than a
3699    macro does to the (and probably every) format file.
3700*/
3701
3702halfword tex_the_toks(int code, halfword *tail)
3703{
3704    switch (code) {
3705        case the_code:
3706        case the_without_unit_code:
3707            return tex_the_value_toks(code, tail, 0);
3708     /* case the_with_property_code: */
3709     /*     return tex_the_value_toks(code, tail, tex_scan_integer(0, 0)); */
3710        case unexpanded_code:
3711            return tex_scan_general_text(tail);
3712        case detokenize_code:
3713        case expanded_detokenize_code:
3714        case protected_detokenize_code:
3715        case protected_expanded_detokenize_code:
3716            return tex_the_detokenized_toks(tail, 
3717                code == expanded_detokenize_code  || code == protected_expanded_detokenize_code,
3718                code == protected_detokenize_code || code == protected_expanded_detokenize_code
3719            );
3720        default:
3721            return null;
3722    }
3723}
3724
3725strnumber tex_the_scanned_result(void)
3726{
3727    /*tex return value */
3728    strnumber r;
3729    /*tex holds |selector| setting */
3730    int saved_selector;
3731    push_selector;
3732    switch (cur_val_level) {
3733        case integer_val_level:
3734        case attribute_val_level:
3735            tex_print_int(cur_val);
3736            break;
3737        case posit_val_level:
3738            tex_print_posit(cur_val);
3739            break;
3740        case dimension_val_level:
3741            tex_print_dimension(cur_val, pt_unit);
3742            break;
3743        case glue_val_level:
3744            tex_print_spec(cur_val, pt_unit);
3745            tex_flush_node(cur_val);
3746            break;
3747        case muglue_val_level:
3748            tex_print_spec(cur_val, mu_unit);
3749            tex_flush_node(cur_val);
3750            break;
3751        case token_val_level:
3752            if (cur_val) {
3753                tex_token_show(cur_val);
3754                break;
3755            } else {
3756                r = get_nullstr();
3757                goto DONE;
3758            }
3759        /*
3760        case list_val_level:
3761            printf("TODO\n");
3762            if (cur_val) {
3763                cur_val = tex_copy_node(cur_val);
3764                tex_couple_nodes(cur_list.tail, cur_val);
3765                cur_list.tail = cur_val;
3766            }
3767            r = get_nullstr();
3768            goto DONE;
3769        */
3770        default:
3771            r = get_nullstr();
3772            goto DONE;
3773    }
3774    r = tex_make_string();
3775  DONE:
3776    pop_selector;
3777    return r;
3778}
3779
3780/*tex
3781
3782    The following routine is used to implement |\fontdimen n f|. We no longer automatically increase
3783    the number of allocated dimensions because we have plenty of dimensions available and loading is
3784    done differently anyway.
3785
3786*/
3787
3788static halfword tex_aux_scan_font_id_and_parameter(halfword *fnt, halfword *n)
3789{
3790    *n = tex_scan_integer(0, NULL);
3791    *fnt = tex_scan_font_identifier(NULL);
3792    if (*n <= 0 || *n > max_integer) {
3793        tex_handle_error(
3794            normal_error_type,
3795            "Font '%s' has at most %i fontdimen parameters",
3796            font_original(*fnt), font_parameter_count(*fnt),
3797            "The font parameter index is out of range."
3798        );
3799        return 0;
3800    } else {
3801        return 1;
3802    }
3803}
3804
3805void tex_set_font_dimension(void)
3806{
3807    halfword fnt, n;
3808    if (tex_aux_scan_font_id_and_parameter(&fnt, &n)) {
3809        tex_set_font_parameter(fnt, n, tex_scan_dimension(0, 0, 0, 1, NULL));
3810    }
3811}
3812
3813halfword tex_get_font_dimension(void)
3814{
3815    halfword fnt, n;
3816    return tex_aux_scan_font_id_and_parameter(&fnt, &n) ? tex_get_font_parameter(fnt, n) : null;
3817}
3818
3819void tex_set_scaled_font_dimension(void)
3820{
3821    halfword fnt, n;
3822    if (tex_aux_scan_font_id_and_parameter(&fnt, &n)) {
3823        tex_set_scaled_parameter(fnt, n, tex_scan_dimension(0, 0, 0, 1, NULL));
3824    }
3825}
3826
3827halfword tex_get_scaled_font_dimension(void)
3828{
3829    halfword fnt, n;
3830    return tex_aux_scan_font_id_and_parameter(&fnt, &n) ? tex_get_scaled_parameter(fnt, n) : null;
3831}
3832
3833/*tex Declare procedures that scan font-related stuff. */
3834
3835halfword tex_scan_math_style_identifier(int tolerant, int styles)
3836{
3837    halfword style = tex_scan_integer(0, NULL);
3838    if (is_valid_math_style(style)) {
3839        return style;
3840    } else if (styles && are_valid_math_styles(style)) {
3841        return style;
3842    } else if (tolerant) {
3843        return -1;
3844    } else {
3845        tex_handle_error(
3846            back_error_type,
3847            "Missing math style, treated as \\displaystyle",
3848            "A style should have been here; I inserted '\\displaystyle'."
3849        );
3850        return display_style;
3851    }
3852}
3853
3854halfword tex_scan_math_parameter(void)
3855{
3856    do {
3857        tex_get_x_token();
3858    } while (cur_cmd == spacer_cmd);
3859    if (cur_cmd == math_parameter_cmd && cur_chr < math_parameter_last) {
3860        return cur_chr;
3861    } else {
3862        tex_handle_error(
3863            normal_error_type,
3864            "Invalid math parameter",
3865            "I'm going to ignore this one."
3866        );
3867        return -1;
3868    }
3869}
3870
3871halfword tex_scan_fontspec_identifier(void)
3872{
3873    /*tex Get the next non-blank non-call. */
3874    do {
3875        tex_get_x_token();
3876    } while (cur_cmd == spacer_cmd);
3877    if (cur_cmd == fontspec_cmd) {
3878        return cur_chr;
3879    } else {
3880        return 0;
3881    }
3882}
3883
3884halfword tex_scan_font_identifier(halfword *spec)
3885{
3886    /*tex Get the next non-blank non-call. */
3887    do {
3888        tex_get_x_token();
3889    } while (cur_cmd == spacer_cmd);
3890    switch (cur_cmd) {
3891        case define_font_cmd:
3892            return cur_font_par;
3893        case set_font_cmd:
3894            return cur_chr;
3895        case fontspec_cmd:
3896            {
3897                halfword fnt = tex_get_font_identifier(cur_chr);
3898                if (fnt && spec) {
3899                    *spec = fnt ? cur_chr : null;
3900                }
3901                return fnt;
3902            }
3903        case define_family_cmd:
3904            {
3905                halfword siz = cur_chr;
3906                halfword fam = tex_scan_math_family_number();
3907                halfword fnt = tex_fam_fnt(fam, siz);
3908                return fnt;
3909            }
3910        case register_integer_cmd:
3911            {
3912                /*tex Checking here saves a push back when we want an integer. */
3913                halfword fnt = register_integer_number(cur_chr);
3914                if (tex_is_valid_font(fnt)) {
3915                    return fnt;
3916                } else {
3917                    break; /* to error */
3918                }
3919            }
3920        case integer_cmd:
3921            {
3922                /*tex Checking here saves a push back when we want an integer. */
3923                halfword fnt = cur_chr;
3924                if (tex_is_valid_font(fnt)) {
3925                    return fnt;
3926                } else {
3927                    break; /* to error */
3928                }
3929            }
3930        case internal_integer_cmd:
3931            {
3932                /*tex Bonus:  |\setfontid| */
3933                if (internal_integer_number(cur_chr) == font_code) {
3934                    halfword fnt = tex_scan_integer(0, NULL);
3935                    if (tex_is_valid_font(fnt)) {
3936                        return fnt;
3937                    }
3938                }
3939                break; /* to error */
3940            }
3941        default:
3942            {
3943                /*tex We abuse |scan_cardinal| here but we have to push back.  */
3944                unsigned fnt = null_font;
3945                tex_back_input(cur_tok);
3946                if (tex_scan_cardinal(0, &fnt, 1)) {
3947                    if (tex_is_valid_font((halfword) fnt)) {
3948                        return (halfword) fnt;
3949                    }
3950                }
3951                break; /* to error */
3952            }
3953    }
3954    tex_handle_error(
3955        back_error_type,
3956        "Missing or invalid font identifier (or equivalent) or integer (register or otherwise)",
3957        "I was looking for a control sequence whose current meaning has been defined by\n"
3958        "\\font or a valid font id number."
3959    );
3960    return null_font;
3961}
3962
3963/*tex
3964
3965    The |scan_general_text| procedure is much like |scan_toks (false, false)|, but will be invoked
3966    via |expand|, i.e., recursively.
3967
3968    The token list (balanced text) created by |scan_general_text| begins at |link (temp_token_head)|
3969    and ends at |cur_val|. (If |cur_val = temp_token_head|, the list is empty.)
3970
3971*/
3972
3973halfword tex_scan_general_text(halfword *tail)
3974{
3975    /*tex The tail of the token list being built: */
3976    halfword p = get_reference_token();
3977    halfword head;
3978    /*tex The number of nested left braces: */
3979    halfword unbalance = 0;
3980    halfword saved_scanner_status = lmt_input_state.scanner_status;
3981    halfword saved_warning_index = lmt_input_state.warning_index;
3982    halfword saved_def_ref = lmt_input_state.def_ref;
3983    lmt_input_state.scanner_status = scanner_is_absorbing;
3984    lmt_input_state.warning_index = cur_cs;
3985    lmt_input_state.def_ref = p;
3986    /*tex Remove the compulsory left brace. */
3987    tex_scan_left_brace();
3988    while (1) {
3989        tex_get_token();
3990        if (! cur_cs) {
3991            switch (cur_cmd) {
3992                case left_brace_cmd:
3993                    if (! cur_cs) {
3994                        ++unbalance;
3995                    }
3996                    break;
3997                case right_brace_cmd:
3998                    if (unbalance) {
3999                        --unbalance;
4000                        break;
4001                    } else {
4002                        goto DONE;
4003                    }
4004            }
4005        }
4006        p = tex_store_new_token(p, cur_tok);
4007    }
4008  DONE:
4009    head = token_link(lmt_input_state.def_ref);
4010    if (tail) {
4011        *tail = head ? p : null;
4012    }
4013    /*tex Discard reference count. */
4014    tex_put_available_token(lmt_input_state.def_ref);
4015    lmt_input_state.scanner_status = saved_scanner_status;
4016    lmt_input_state.warning_index = saved_warning_index;
4017    lmt_input_state.def_ref = saved_def_ref;
4018    return head;
4019}
4020
4021/*tex
4022
4023    |scan_toks|. This function returns a pointer to the tail of a new token list, and it also makes
4024    |def_ref| point to the reference count at the head of that list.
4025
4026    There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is true, the goal is
4027    to create the token list for a macro definition; otherwise the goal is to create the token list
4028    for some other \TEX\ primitive: |\mark|, |\output|, |\everypar|, |\lowercase|, |\uppercase|,
4029    |\message|, |\errmessage|, |\write|, or |\special|. In the latter cases a left brace must be
4030    scanned next; this left brace will not be part of the token list, nor will the matching right
4031    brace that comes at the end. If |xpand| is false, the token list will simply be copied from the
4032    input using |get_token|. Otherwise all expandable tokens will be expanded until unexpandable
4033    tokens are left, except that the results of expanding |\the| are not expanded further. If both
4034    |macro_def| and |xpand| are true, the expansion applies only to the macro body (i.e., to the
4035    material following the first |left_brace| character).
4036
4037    The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of the control
4038    sequence to display in runaway error messages.
4039
4040    Watch out: there are two extensions to the macro definition parser: a |#0| will just gobble the
4041    argument and not copy it to the parameter stack, and |#+| will not remove braces around a
4042    \quote {single group} argument, something that comes in handy when you grab and pass over an
4043    argument.
4044
4045    If the next character is a parameter number, make |cur_tok| a |match| token; but if it is a
4046    left brace, store |left_brace|, |end_match|, set |hash_brace|, and |goto done|.
4047
4048    For practical reasone, we have split the |scan_toks| function up in four smaller dedicated
4049    functions. When we add features it makes no sense to clutter the code even more. Keep in mind
4050    that compared to the reference \TEX\ inplementation we have to support |\expanded| token lists
4051    but also |\protected| and friends. There is of course some overlap now but that's a small
4052    price to pay for readability.
4053
4054    The split functions need less redundant checking and the expandable variants got one loop
4055    instead of two nested loops.
4056
4057*/
4058
4059static inline bool tex_parameter_escape_mode(void)
4060{
4061    return (parameter_mode_par & parameter_escape_mode) == parameter_escape_mode;
4062}
4063
4064halfword tex_scan_toks_normal(int left_brace_found, halfword *tail)
4065{
4066    halfword unbalance = 0;
4067    halfword result = get_reference_token();
4068    halfword p = result;
4069    lmt_input_state.scanner_status = scanner_is_absorbing;
4070    lmt_input_state.warning_index = cur_cs;
4071    lmt_input_state.def_ref = result;
4072    if (! left_brace_found) {
4073        tex_scan_left_brace();
4074    }
4075    while (1) {
4076        tex_get_token();
4077        switch (cur_cmd) { 
4078            case left_brace_cmd:
4079                if (! cur_cs) {
4080                    ++unbalance;
4081                }
4082                break;
4083            case right_brace_cmd:
4084                if (! cur_cs) {
4085                    if (unbalance) {
4086                        --unbalance;
4087                    } else {
4088                        goto DONE;
4089                    }
4090                }
4091                break;
4092            case prefix_cmd: 
4093                if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) { /* todo cur_tok == let_aliased_token */
4094                    cur_tok = token_val(prefix_cmd, always_code);
4095                } 
4096                break;
4097        }
4098        p = tex_store_new_token(p, cur_tok);
4099    }
4100  DONE:
4101    lmt_input_state.scanner_status = scanner_is_normal;
4102    if (tail) {
4103        *tail = p;
4104    }
4105    return result;
4106}
4107
4108halfword tex_scan_toks_expand(int left_brace_found, halfword *tail, int expandconstant, int keepparameters)
4109{
4110    halfword unbalance = 0;
4111    halfword result = get_reference_token();
4112    halfword p = result;
4113    lmt_input_state.scanner_status = scanner_is_absorbing;
4114    lmt_input_state.warning_index = cur_cs;
4115    lmt_input_state.def_ref = result;
4116    if (! left_brace_found) {
4117        tex_scan_left_brace();
4118    }
4119    while (1) {
4120      PICKUP:
4121        tex_get_next();
4122        switch (cur_cmd) {
4123            case call_cmd:
4124            case tolerant_call_cmd:
4125                tex_expand_current_token();
4126                goto PICKUP;
4127            case constant_call_cmd:
4128                {
4129                    halfword h = token_link(cur_chr);
4130                    while (h) { 
4131                        p = tex_store_new_token(p, token_info(h));
4132                        h = token_link(h);
4133                    }
4134                    goto PICKUP;
4135                }
4136            case protected_call_cmd:
4137            case tolerant_protected_call_cmd:
4138                cur_tok = cs_token_flag + cur_cs;
4139                goto APPENDTOKEN;
4140            case semi_protected_call_cmd:
4141            case tolerant_semi_protected_call_cmd:
4142                if (expandconstant) {
4143                    tex_expand_current_token();
4144                    goto PICKUP;
4145                } else {
4146                    cur_tok = cs_token_flag + cur_cs;
4147                    goto APPENDTOKEN;
4148                }
4149            case the_cmd:
4150                {
4151                    halfword t = null;
4152                    halfword h = tex_the_toks(cur_chr, &t);
4153                    if (h) {
4154                        set_token_link(p, h);
4155                        p = t;
4156                    }
4157                    goto PICKUP;
4158                }
4159            case parameter_cmd:
4160                {
4161                    /*tex This is kind of tricky and thereby experimental. No need for the extensive x here. */
4162                    halfword tok1, tok2;
4163                    tex_x_token();
4164                    tok1 = cur_tok;
4165                    tex_get_next();
4166                    tex_x_token();
4167                    tok2 = cur_tok; 
4168                    if (keepparameters || ! tex_parameter_escape_mode()) {
4169                        /* not done */
4170                    } else { 
4171                        halfword t;
4172                        halfword h = tex_expand_parameter(tok2, &t);
4173                        if (h) { 
4174                            set_token_link(p, h);
4175                            p = t;
4176                            goto PICKUP;
4177                        } else { 
4178                            tex_flush_token_list(h);
4179                        }
4180                    }
4181                    p = tex_store_new_token(p, tok1);
4182                    p = tex_store_new_token(p, tok2);
4183                    goto PICKUP;
4184                }
4185            case prefix_cmd:
4186                if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) {
4187                    cur_tok = token_val(prefix_cmd, always_code);
4188                    goto APPENDTOKEN;
4189                }
4190            default:
4191                if (cur_cmd > max_command_cmd) {
4192                    tex_expand_current_token();
4193                    goto PICKUP;
4194                } else {
4195                    goto DONEEXPANDING;
4196                }
4197        }
4198      DONEEXPANDING:
4199        tex_x_token();
4200     // if (cur_tok < right_brace_limit) {
4201     //     if (cur_cmd == left_brace_cmd) {
4202     //         ++unbalance;
4203     //     } else if (unbalance) {
4204     //         --unbalance;
4205     //     } else {
4206     //         goto FINALYDONE;
4207     //     }
4208     // }
4209        if (! cur_cs) {
4210            switch (cur_cmd) {
4211                case left_brace_cmd:
4212                    ++unbalance;
4213                    break;
4214                case right_brace_cmd:
4215                    if (unbalance) {
4216                        --unbalance;
4217                    } else {
4218                        goto FINALYDONE;
4219                    }
4220                    break;
4221            }
4222        }
4223      APPENDTOKEN:
4224        p = tex_store_new_token(p, cur_tok);
4225    }
4226  FINALYDONE:
4227    lmt_input_state.scanner_status = scanner_is_normal;
4228    if (tail) {
4229        *tail = p;
4230    }
4231    return result;
4232}
4233
4234static void tex_aux_too_many_parameters_error(void)
4235{
4236    tex_handle_error(
4237        normal_error_type,
4238        "You already have 15 parameters",
4239        "I'm going to ignore the # sign you just used, as well the token that followed it.\n"
4240        /*tex That last bit was added in the TeX 2021 buglet fix round. */
4241    );
4242}
4243
4244static void tex_aux_parameters_order_error(void)
4245{
4246    tex_handle_error(
4247        back_error_type,
4248        "Parameters must be numbered consecutively",
4249        "I've inserted the digit you should have used after the #."
4250    );
4251}
4252
4253static void tex_aux_missing_brace_error(void)
4254{
4255    tex_handle_error(
4256        normal_error_type,
4257        "Missing { inserted",
4258        "Where was the left brace? You said something like '\\def\\a}', which I'm going to\n"
4259        "interpret as '\\def\\a{}'."
4260    );
4261}
4262
4263static void tex_aux_illegal_parameter_in_body_error(void)
4264{
4265    tex_handle_error(
4266        back_error_type,
4267        "Illegal parameter number in definition of %S",
4268        lmt_input_state.warning_index,
4269        "You meant to type ## instead of #, right? Or maybe a } was forgotten somewhere\n"
4270        "earlier, and things are all screwed up? I'm going to assume that you meant ##."
4271    );
4272}
4273
4274/*tex
4275    There are interesting aspects in reporting the preamble, like:
4276
4277    \starttyping
4278    \def\test#1#{test#1} : macro:#1{->test#1{
4279    \stoptyping
4280
4281    So, the \type {#} gets reported as left brace.
4282
4283    The |\par| handling depends on the mode
4284
4285    \starttyping
4286    % 0x1 text | 0x2 macro | 0x4 go-on
4287
4288    \autoparagraphmode0 \def\foo#1\par{[#1]} 0: \meaningfull\foo\par \foo test\par test\par
4289    \autoparagraphmode1 \def\foo#1\par{[#1]} 1: \meaningfull\foo\par \foo test\par test\par
4290    \autoparagraphmode2 \def\foo#1\par{[#1]} 2: \meaningfull\foo\par \foo test\par test\par % discard after #1 till \par
4291    \autoparagraphmode4 \def\foo#1\par{[#1]} 4: \meaningfull\foo\par \foo test\par test\par
4292    \stoptyping
4293*/
4294
4295/* There is no real gain and we get a 1K larger binary: */ /* inline */
4296
4297static int tex_aux_valid_macro_preamble(halfword *p, int *counter, halfword *hash_brace)
4298{
4299    halfword h = *p;
4300    while (1) {
4301        tex_get_token();
4302        switch (cur_cmd) {
4303            case left_brace_cmd:
4304            case right_brace_cmd:
4305                if (cur_cs) {
4306                    break;
4307                } else { 
4308                    goto DONE;
4309                }
4310            case parameter_cmd:
4311                tex_get_token();
4312                /*
4313                    cf. TeX 2021 we not do a more strict testing. Interesting is that wondered why we
4314                    had a more generous test here but just considered that a feature or intended side
4315                    effect but in the end we have to be strict.
4316
4317                    \starttyping
4318                    \def\cs#1#\bgroup hi#1}       % was weird but okay pre 2021
4319                    \def\cs#1\bgroup{hi#1\bgroup} % but this is better indeed
4320                    \stoptyping
4321                */
4322                if (cur_tok < left_brace_limit) {
4323             /* if (cur_cmd == left_brace_cmd) { */
4324                    /*tex The |\def\foo#{}| case. */
4325                    *hash_brace = cur_tok;
4326                    *p = tex_store_new_token(*p, cur_tok);
4327                    *p = tex_store_new_token(*p, end_match_token);
4328                    set_token_preamble(h, macro_with_preamble);
4329                    set_token_parameters(h, *counter);
4330                    return 1;
4331                } else if (*counter == 0xF) {
4332                    tex_aux_too_many_parameters_error();
4333                } else {
4334                    switch (cur_tok) {
4335                        case zero_token:
4336                            ++*counter;
4337                            cur_tok = match_token;
4338                            break;
4339                        case asterisk_token:
4340                            cur_tok = spacer_match_token;
4341                            break;
4342                        case plus_token:
4343                            ++*counter;
4344                            cur_tok = keep_match_token;
4345                            break;
4346                        case minus_token:
4347                            cur_tok = thrash_match_token;
4348                            break;
4349                        case period_token:
4350                            cur_tok = par_spacer_match_token;
4351                            break;
4352                        case comma_token:
4353                            cur_tok = keep_spacer_match_token;
4354                            break;
4355                        case slash_token:
4356                            ++*counter;
4357                            cur_tok = prune_match_token;
4358                            break;
4359                        case colon_token:
4360                            cur_tok = continue_match_token;
4361                            break;
4362                        case semi_colon_token:
4363                            cur_tok = quit_match_token;
4364                            break;
4365                        case equal_token:
4366                            ++*counter;
4367                            cur_tok = mandate_match_token;
4368                            break;
4369                        case circumflex_token_l:
4370                        case circumflex_token_o:
4371                            ++*counter;
4372                            cur_tok = leading_match_token;
4373                            break;
4374                        case underscore_token_l:
4375                        case underscore_token_o:
4376                            ++*counter;
4377                            cur_tok = mandate_keep_match_token;
4378                            break;
4379                        case at_token_l:
4380                        case at_token_o:
4381                            cur_tok = par_command_match_token;
4382                            break;
4383                        case L_token_l:
4384                        case L_token_o:
4385                            cur_tok = left_match_token;
4386                            break;
4387                        case R_token_l:
4388                        case R_token_o:
4389                            cur_tok = right_match_token;
4390                            break;
4391                        case G_token_l:
4392                        case G_token_o:
4393                            cur_tok = gobble_match_token;
4394                            break;
4395                        case M_token_l:
4396                        case M_token_o:
4397                            cur_tok = gobble_more_match_token;
4398                            break;
4399                        case S_token_l:
4400                        case S_token_o:
4401                            cur_tok = brackets_match_token;
4402                            break;
4403                        case P_token_l:
4404                        case P_token_o:
4405                            cur_tok = parentheses_match_token;
4406                            break;
4407                        case X_token_l:
4408                        case X_token_o:
4409                            cur_tok = angles_match_token;
4410                            break;
4411                        default:
4412                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4413                                ++*counter;
4414                                if ((cur_tok - other_token - '0') == *counter) {
4415                                    cur_tok += match_token - other_token ;
4416                                    break;
4417                                }
4418                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4419                                ++*counter;
4420                                if ((cur_tok - A_token_l + 10) == *counter) {
4421                                    cur_tok += match_token - letter_token;
4422                                    break;
4423                                }
4424                            }
4425                            tex_aux_parameters_order_error();
4426                            cur_tok = match_token; /* zero */
4427                            break;
4428                    }
4429                }
4430                break;
4431            case end_paragraph_cmd: 
4432                if (! auto_paragraph_mode(auto_paragraph_macro)) {
4433                   cur_tok = par_command_match_token;
4434                }
4435                break;
4436        }
4437        *p = tex_store_new_token(*p, cur_tok);
4438    }
4439  DONE:
4440    if (h != *p) {
4441        *p = tex_store_new_token(*p, end_match_token);
4442        set_token_preamble(h, macro_with_preamble);
4443        set_token_parameters(h, *counter);
4444    }
4445    if (cur_cmd == right_brace_cmd) {
4446        ++lmt_input_state.align_state;
4447        tex_aux_missing_brace_error();
4448        return 0;
4449    } else {
4450        return 1;
4451    }
4452}
4453
4454halfword tex_scan_macro_normal(void)
4455{
4456    halfword hash_brace = 0;
4457    halfword counter = 0;
4458    halfword result = get_reference_token();
4459    halfword p = result;
4460    lmt_input_state.scanner_status = scanner_is_defining;
4461    lmt_input_state.warning_index = cur_cs;
4462    lmt_input_state.def_ref = result;
4463    if (tex_aux_valid_macro_preamble(&p, &counter, &hash_brace)) {
4464        halfword unbalance = 0;
4465        while (1) {
4466            tex_get_token();
4467            switch (cur_cmd) { 
4468                case left_brace_cmd:
4469                    if (! cur_cs) {
4470                        ++unbalance;
4471                    }
4472                    break;
4473                case right_brace_cmd: 
4474                    if (! cur_cs) {
4475                        if (unbalance) {
4476                            --unbalance;
4477                        } else {
4478                            goto FINALYDONE;
4479                        }
4480                    }
4481                    break;
4482                case parameter_cmd: 
4483                    {
4484                        halfword s = cur_tok;
4485                        tex_get_token();
4486                        if (cur_cmd == parameter_cmd) {
4487                            /*tex Keep the |#|. */
4488                        } else { 
4489                            halfword n;
4490                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4491                                n = cur_chr - '0';
4492                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4493                                n = cur_chr - '0' - gap_match_count; 
4494                            } else { 
4495                                n = counter + 1; 
4496                            }
4497                            if (n <= counter) {
4498                                cur_tok = token_val(parameter_reference_cmd, n);
4499                            } else { 
4500                                halfword v = tex_parameter_escape_mode() ? valid_parameter_reference(cur_tok) : 0;
4501                                if (v) { 
4502                                    p = tex_store_new_token(p, token_val(parameter_cmd, match_visualizer));
4503                                } else {
4504                                    tex_aux_illegal_parameter_in_body_error();
4505                                    cur_tok = s;
4506                                }
4507                            }
4508                        }
4509                    }
4510                    break;
4511                case prefix_cmd: 
4512                    if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) { /* todo cur_tok == let_aliased_token */
4513                        cur_tok = token_val(prefix_cmd, always_code);
4514                    }
4515                    break;
4516                default:
4517                    break;
4518            }
4519            p = tex_store_new_token(p, cur_tok);
4520        }
4521    }
4522  FINALYDONE:
4523    lmt_input_state.scanner_status = scanner_is_normal;
4524    if (hash_brace) {
4525        p = tex_store_new_token(p, hash_brace);
4526    }
4527    return result;
4528}
4529
4530halfword tex_scan_macro_expand(void)
4531{
4532    halfword hash_brace = 0;
4533    halfword counter = 0;
4534    halfword result = get_reference_token();
4535    halfword p = result;
4536    lmt_input_state.scanner_status = scanner_is_defining;
4537    lmt_input_state.warning_index = cur_cs;
4538    lmt_input_state.def_ref = result;
4539    if (tex_aux_valid_macro_preamble(&p, &counter, &hash_brace)) {
4540        halfword unbalance = 0;
4541        while (1) {
4542          PICKUP:
4543            tex_get_next();
4544            switch (cur_cmd) {
4545                case call_cmd:
4546                case tolerant_call_cmd:
4547                    tex_expand_current_token();
4548                    goto PICKUP;
4549                case index_cmd:
4550                    tex_inject_parameter(cur_chr);
4551                    goto PICKUP;
4552                case constant_call_cmd:
4553                    {
4554                        halfword h = token_link(cur_chr);
4555                        while (h) { 
4556                            p = tex_store_new_token(p, token_info(h));
4557                            h = token_link(h);
4558                        }
4559                        goto PICKUP;
4560                    }
4561                case protected_call_cmd:
4562                case semi_protected_call_cmd:
4563                case tolerant_protected_call_cmd:
4564                case tolerant_semi_protected_call_cmd:
4565                    cur_tok = cs_token_flag + cur_cs;
4566                    goto APPENDTOKEN;
4567                case the_cmd:
4568                    {
4569                        halfword t = null;
4570                        halfword h = tex_the_toks(cur_chr, &t);
4571                        if (h) {
4572                            set_token_link(p, h);
4573                            p = t;
4574                        }
4575                        goto PICKUP;
4576                    }
4577                case relax_cmd:
4578                    if (cur_chr == no_relax_code) {
4579                        /*tex Think of |\ifdim\dimen0=\dimen2\norelax| inside an |\edef|. */
4580                        goto PICKUP;
4581                    } else {
4582                        goto DONEEXPANDING;
4583                    }
4584                case prefix_cmd:
4585                    if (cur_chr == enforced_code && (! overload_mode_par || lmt_main_state.run_state != production_state)) {
4586                        cur_tok = token_val(prefix_cmd, always_code);
4587                        goto APPENDTOKEN;
4588                    } else {
4589                        goto DONEEXPANDING;
4590                    }
4591                case parameter_cmd:
4592                    {
4593                        /* move into switch ... */
4594                        halfword s = cur_tok;
4595                        tex_get_x_token();
4596                        if (cur_cmd == parameter_cmd) {
4597                            /*tex Keep the |#|. */
4598                        } else { 
4599                            halfword n;
4600                            if (cur_tok >= one_token && cur_tok <= nine_token) {
4601                                n = cur_chr - '0';
4602                            } else if (cur_tok >= A_token_l && cur_tok <= F_token_l) {
4603                                n = cur_chr - '0' - gap_match_count; 
4604                            } else { 
4605                                n = counter + 1; 
4606                            }
4607                            if (n <= counter) {
4608                                cur_tok = token_val(parameter_reference_cmd, n);
4609                            } else {
4610                                halfword v = tex_parameter_escape_mode() ? valid_parameter_reference(cur_tok) : 0;
4611                                if (v) { 
4612                                    halfword t = null;
4613                                    halfword h = tex_expand_parameter(cur_tok, &t);
4614                                    if (h) {
4615                                        set_token_link(p, h);
4616                                        p = t;
4617                                    }
4618                                    goto PICKUP;
4619                                } else {
4620                                    tex_aux_illegal_parameter_in_body_error();
4621                                    cur_tok = s;
4622                                }
4623                            }
4624                        }
4625                        goto APPENDTOKEN;
4626                    }
4627                case left_brace_cmd:
4628                    if (cur_cs) {
4629                        cur_tok = cs_token_flag + cur_cs;
4630                    } else {
4631                        cur_tok = token_val(cur_cmd, cur_chr);
4632                        ++unbalance;
4633                    }
4634                    goto APPENDTOKEN;
4635                case right_brace_cmd:
4636                    if (cur_cs) {
4637                        cur_tok = cs_token_flag + cur_cs;
4638                        goto APPENDTOKEN;
4639                    } else {
4640                        cur_tok = token_val(cur_cmd, cur_chr);
4641                        if (unbalance) {
4642                            --unbalance;
4643                            goto APPENDTOKEN;
4644                        } else {
4645                            goto FINALYDONE;
4646                        }
4647                    }
4648                default:
4649                    if (cur_cmd > max_command_cmd) {
4650                        tex_expand_current_token();
4651                        goto PICKUP;
4652                    } else {
4653                        goto DONEEXPANDING;
4654                    }
4655            }
4656          DONEEXPANDING:
4657            if (cur_cs) {
4658                cur_tok = cs_token_flag + cur_cs;
4659            } else {
4660                cur_tok = token_val(cur_cmd, cur_chr);
4661            }
4662          APPENDTOKEN:
4663            p = tex_store_new_token(p, cur_tok);
4664        }
4665    }
4666  FINALYDONE:
4667    lmt_input_state.scanner_status = scanner_is_normal;
4668    if (hash_brace) {
4669        p = tex_store_new_token(p, hash_brace);
4670    }
4671    return result;
4672}
4673
4674/*tex
4675
4676    The |scan_expr| procedure scans and evaluates an expression. Evaluating an expression is a
4677    recursive process: When the left parenthesis of a subexpression is scanned we descend to the
4678    next level of recursion; the previous level is resumed with the matching right parenthesis.
4679
4680*/
4681
4682typedef enum expression_states {
4683    expression_none,     /*tex |(| or |(expr)| */
4684    expression_add,      /*tex |+| */
4685    expression_subtract, /*tex |-| */
4686    expression_multiply, /*tex |*| */
4687    expression_divide,   /*tex |/| */
4688    expression_scale,    /*tex |* factor| */
4689    expression_idivide,  /*tex |:|, is like |/| but floored */
4690    expression_imodulo,  /*tex |;| */
4691} expression_states;
4692
4693/*tex
4694
4695    We want to make sure that each term and (intermediate) result is in the proper range. Integer
4696    values must not exceed |infinity| ($2^{31} - 1$) in absolute value, dimensions must not exceed
4697    |max_dimension| ($2^{30} - 1$). We avoid the absolute value of an integer, because this might fail
4698    for the value $-2^{31}$ using 32-bit arithmetic.
4699
4700    Todo: maybe use |long long| here.
4701
4702*/
4703
4704inline static void tex_aux_normalize_glue(halfword g)
4705{
4706    if (! glue_stretch(g)) {
4707        glue_stretch_order(g) = normal_glue_order;
4708    }
4709    if (! glue_shrink(g)) {
4710        glue_shrink_order(g) = normal_glue_order;
4711    }
4712}
4713
4714/*tex
4715
4716    Parenthesized subexpressions can be inside expressions, and this nesting has a stack. Seven
4717    local variables represent the top of the expression stack: |p| points to pushed-down entries,
4718    if any; |l| specifies the type of expression currently beeing evaluated; |e| is the expression
4719    so far and |r| is the state of its evaluation; |t| is the term so far and |s| is the state of
4720    its evaluation; finally |n| is the numerator for a combined multiplication and division, if any.
4721
4722    The function |add_or_sub (x, y, max_answer, negative)| computes the sum (for |negative = false|)
4723    or difference (for |negative = true|) of |x| and |y|, provided the absolute value of the result
4724    does not exceed |max_answer|.
4725
4726*/
4727
4728inline static int tex_aux_add_or_sub(int x, int y, int max_answer, int operation)
4729{
4730    switch (operation) {
4731        case expression_subtract:
4732            y = -y;
4733            // fall-trough
4734        case expression_add:
4735            if (x >= 0) {
4736                if (y <= max_answer - x) {
4737                    return x + y;
4738                } else {
4739                    lmt_scanner_state.arithmic_error = 1;
4740                }
4741            } else if (y >= -max_answer - x) {
4742                return x + y;
4743            } else {
4744                lmt_scanner_state.arithmic_error = 1;
4745            }
4746            break;
4747    }
4748    return 0;
4749}
4750
4751/*tex
4752
4753    The function |quotient (n, d)| computes the rounded quotient $q = \lfloor n / d + {1 \over 2}
4754    \rfloor$, when $n$ and $d$ are positive.
4755
4756*/
4757
4758// inline static int xtex_aux_quotient(int n, int d, int round)
4759// {
4760//     /*tex The answer: */
4761//     if (d == 0) {
4762//         lmt_scanner_state.arithmic_error = 1;
4763//         return 0;
4764//     } else {
4765//         /*tex Should the answer be negated? */
4766//         bool negative;
4767//         int a;
4768//         if (d > 0) {
4769//             negative = false;
4770//         } else {
4771//             d = -d;
4772//             negative = true;
4773//         }
4774//         if (n < 0) {
4775//             n = -n;
4776//             negative = ! negative;
4777//         }
4778//         a = n / d;
4779//         if (round) {
4780//             n = n - a * d;
4781//             /*tex Avoid certain compiler optimizations! Really? */
4782//             d = n - d;
4783//             if (d + n >= 0) {
4784//                 ++a;
4785//             }
4786//         }
4787//         if (negative) {
4788//             a = -a;
4789//         }
4790//         return a;
4791//     }
4792// }
4793
4794inline static int tex_aux_quotient(int n, int d, int rounded)
4795{
4796    if (d == 0) {
4797        lmt_scanner_state.arithmic_error = 1;
4798        return 0;
4799    } else if (rounded) {
4800        return lround((double) n / (double) d);
4801    } else { 
4802        return n / d;
4803    }
4804}
4805
4806inline static int tex_aux_modulo(int n, int d)
4807{
4808    if (d == 0) {
4809        lmt_scanner_state.arithmic_error = 1;
4810        return 0;
4811    } else {
4812        return n % d;
4813    }
4814}
4815
4816int tex_quotient(int n, int d, int round)
4817{
4818    return tex_aux_quotient(n, d, round);
4819}
4820
4821/*tex
4822
4823    Finally, the function |fract (x, n, d, max_answer)| computes the integer $q = \lfloor x n / d
4824    + {1 \over 2} \rfloor$, when $x$, $n$, and $d$ are positive and the result does not exceed
4825    |max_answer|. We can't use floating point arithmetic since the routine must produce identical
4826    results in all cases; and it would be too dangerous to multiply by~|n| and then divide by~|d|,
4827    in separate operations, since overflow might well occur. Hence this subroutine simulates double
4828    precision arithmetic, somewhat analogous to Metafont's |make_fraction| and |take_fraction|
4829    routines.
4830
4831*/
4832
4833int tex_fract(int x, int n, int d, int max_answer)
4834{
4835    /*tex should the answer be negated? */
4836    bool negative = false;
4837    /*tex the answer */
4838    int a = 0;
4839    /*tex a proper fraction */
4840    int f;
4841    /*tex smallest integer such that |2*h>=d| */
4842    int h;
4843    /*tex intermediate remainder */
4844    int r;
4845    /*tex temp variable */
4846    int t;
4847    if (d == 0) {
4848        goto TOO_BIG;
4849    }
4850    if (x == 0) {
4851        return 0;
4852    }
4853    if (d < 0) {
4854        d = -d;
4855        negative = true;
4856    }
4857    if (x < 0) {
4858        x = -x;
4859        negative = ! negative;
4860    }
4861    if (n < 0) {
4862        n = -n;
4863        negative = ! negative;
4864    }
4865    t = n / d;
4866    if (t > max_answer / x) {
4867        goto TOO_BIG;
4868    }
4869    a = t * x;
4870    n = n - t * d;
4871    if (n == 0) {
4872        goto FOUND;
4873    }
4874    t = x / d;
4875    if (t > (max_answer - a) / n) {
4876        goto TOO_BIG;
4877    }
4878    a = a + t * n;
4879    x = x - t * d;
4880    if (x == 0) {
4881        goto FOUND;
4882    }
4883    if (x < n) {
4884        t = x;
4885        x = n;
4886        n = t;
4887    }
4888    /*tex
4889
4890        Now |0 < n <= x < d| and we compute $f = \lfloor xn/d+{1\over2}\rfloor$. The loop here
4891        preserves the following invariant relations between |f|, |x|, |n|, and~|r|: (i)~$f + \lfloor
4892        (xn + (r + d))/d\rfloor = \lfloor x_0 n_0/d + {1\over2} \rfloor$; (ii)~|-d <= r < 0 < n <= x
4893        < d|, where $x_0$, $n_0$ are the original values of~$x$ and $n$.
4894
4895        Notice that the computation specifies |(x - d) + x| instead of |(x + x) - d|, because the
4896        latter could overflow.
4897
4898    */
4899    f = 0;
4900    r = (d / 2) - d;
4901    h = -r;
4902    while (1) {
4903        if (odd(n)) {
4904            r = r + x;
4905            if (r >= 0) {
4906                r = r - d;
4907                ++f;
4908            }
4909        }
4910        n = n / 2;
4911        if (n == 0) {
4912            break;
4913        } else if (x < h) {
4914            x = x + x;
4915        } else {
4916            t = x - d;
4917            x = t + x;
4918            f = f + n;
4919            if (x < n) {
4920                if (x == 0) {
4921                    break;
4922                } else {
4923                    t = x;
4924                    x = n;
4925                    n = t;
4926                }
4927            }
4928        }
4929    }
4930    if (f > (max_answer - a)) {
4931        goto TOO_BIG;
4932    }
4933    a = a + f;
4934  FOUND:
4935    if (negative) {
4936        a = -a;
4937    }
4938    goto DONE;
4939  TOO_BIG:
4940    lmt_scanner_state.arithmic_error = 1;
4941    a = 0;
4942  DONE:
4943    return a;
4944}
4945
4946/*tex
4947
4948    The main stacking logic approach is kept but I get the impression that the code is still
4949    suboptimal. We also accept braced expressions. 
4950
4951*/
4952
4953static void tex_aux_scan_expr(halfword level)
4954{
4955    /*tex state of expression so far */
4956    int result;
4957    /*tex state of term so far */
4958    int state;
4959    /*tex next operation or type of next factor */
4960    int operation;
4961    /*tex expression so far */
4962    int expression;
4963    /*tex term so far */
4964    int term;
4965    /*tex current factor */
4966    int factor = 0;
4967    /*tex numerator of combined multiplication and division */
4968    int numerator;
4969    /*tex saved values of |arith_error| */
4970    int error_a = lmt_scanner_state.arithmic_error;
4971    int error_b = 0;
4972    /*tex top of expression stack */
4973    halfword top = null;
4974    int braced = 0;
4975    int nonelevel = level == posit_val_level ? posit_val_level : integer_val_level; 
4976    /*tex Scan and evaluate an expression |e| of type |l|. */
4977    cur_val_level = level; /* for now */
4978    lmt_scanner_state.expression_depth++;
4979    if (lmt_scanner_state.expression_depth > 1000) {
4980        tex_fatal_error("\\*expr can only be nested 1000 deep");
4981    }
4982  RESTART:
4983    result = expression_none;
4984    state = expression_none;
4985    expression = 0;
4986    term = 0;
4987    numerator = 0;
4988  CONTINUE:
4989//    operation = state == expression_none ? level : integer_val_level; /* we abuse operation */
4990    operation = state == expression_none ? level : nonelevel; /* we abuse operation */
4991    /*tex
4992        Scan a factor |f| of type |o| or start a subexpression. Get the next non-blank non-call
4993        token.
4994    */
4995  AGAIN:
4996    do {
4997        tex_get_x_token();
4998    } while (cur_cmd == spacer_cmd);
4999    if (! braced) {
5000        if (cur_cmd == left_brace_cmd) {
5001            braced = 1;
5002            goto AGAIN;
5003        } else {
5004            braced = 2;
5005        }
5006    }
5007    if (cur_tok == left_parent_token) {
5008        /*tex Push the expression stack and |goto restart|. */
5009        halfword t = tex_get_node(expression_node_size);
5010        node_type(t) = expression_node;
5011        node_subtype(t) = 0;
5012        /* */
5013        node_next(t) = top;
5014        expression_type(t) = (singleword) level;
5015        expression_state(t) = (singleword) state;
5016        expression_result(t) = (singleword) result;
5017        expression_expression(t) = expression;
5018        expression_term(t) = term;
5019        expression_numerator(t) = numerator;
5020        top = t;
5021        level = operation;
5022        goto RESTART;
5023    }
5024    if (cur_cmd != spacer_cmd) {
5025        tex_back_input(cur_tok);
5026    }
5027    switch (operation) {
5028        case integer_val_level:
5029        case attribute_val_level:
5030            factor = tex_scan_integer(0, NULL);
5031            break;
5032        case posit_val_level:
5033            factor = tex_scan_posit(0);
5034            break;
5035        case dimension_val_level:
5036            factor = tex_scan_dimension(0, 0, 0, 0, NULL);
5037            break;
5038        case glue_val_level:
5039            factor = tex_scan_glue(glue_val_level, 0, 0);
5040            break;
5041        case muglue_val_level:
5042            factor = tex_scan_glue(muglue_val_level, 0, 0);
5043            break;
5044    }
5045  FOUND:
5046    /*tex
5047        Scan the next operator and set |o| and get the next non-blank non-call token.
5048    */
5049    do {
5050        tex_get_x_token();
5051    } while (cur_cmd == spacer_cmd);
5052    switch (cur_tok) {
5053        case plus_token:
5054            operation = expression_add;
5055            break;
5056        case minus_token:
5057            operation = expression_subtract;
5058            break;
5059        case asterisk_token:
5060            operation = expression_multiply;
5061            break;
5062        case slash_token:
5063            operation = expression_divide;
5064            break;
5065        case colon_token:
5066            operation = expression_idivide;
5067            break;
5068        case semi_colon_token:
5069            operation = expression_imodulo;
5070            break;
5071        /*tex
5072            The commented bitwise experiment as of 2020-07-20 has been removed and is now in
5073            |\scanbitexpr|. You can find it in the archive.
5074        */
5075        default:
5076            operation = expression_none;
5077            if (! top) {
5078                if (cur_cmd == relax_cmd) {
5079                    /* we're done */
5080                } else if (cur_cmd == right_brace_cmd && braced == 1) {
5081                    /* we're done */
5082                } else {
5083                    tex_back_input(cur_tok);
5084                }
5085            } else if (cur_tok != right_parent_token) {
5086                tex_handle_error(
5087                    back_error_type,
5088                    "Missing ) inserted for expression",
5089                    "I was expecting to see '+', '-', '*', '/', ':' or ')'. Didn't."
5090                );
5091            }
5092            break;
5093    }
5094    lmt_scanner_state.arithmic_error = error_b;
5095    /*tex Make sure that |f| is in the proper range. */
5096    switch (level) {
5097        case integer_val_level:
5098        case attribute_val_level:
5099            if ((factor > max_integer) || (factor < min_integer)) {
5100                lmt_scanner_state.arithmic_error = 1;
5101                factor = 0;
5102            }
5103            break;
5104        case posit_val_level:
5105            if (((unsigned) factor > max_cardinal) || ((unsigned) factor < min_cardinal)) {
5106                lmt_scanner_state.arithmic_error = 1;
5107                factor = 0;
5108            }
5109            break;
5110        case dimension_val_level:
5111            if (abs(factor) > max_dimension) {
5112                lmt_scanner_state.arithmic_error = 1;
5113                factor = 0;
5114            }
5115            break;
5116        case glue_val_level:
5117        case muglue_val_level:
5118            if ((abs(glue_amount(factor)) > max_dimension) || (abs(glue_stretch(factor)) > max_dimension) || (abs(glue_shrink(factor)) > max_dimension)) {
5119                lmt_scanner_state.arithmic_error = 1;
5120                tex_reset_glue_to_zero(factor);
5121            }
5122            break;
5123        default:
5124            if ((state > expression_subtract) && ((factor > max_integer) || (factor < min_integer))) {
5125                lmt_scanner_state.arithmic_error = 1;
5126                factor = 0;
5127            }
5128    }
5129    /*tex Cases for evaluation of the current term. */
5130    switch (state) {
5131        case expression_none:
5132            /*tex
5133                Applying the factor |f| to the partial term |t| (with the operator |s|) is delayed
5134                until the next operator |o| has been scanned. Here we handle the first factor of a
5135                partial term. A glue spec has to be copied unless the next operator is a right
5136                parenthesis; this allows us later on to simply modify the glue components.
5137            */
5138            term = factor;
5139            if ((level >= glue_val_level) && (operation != expression_none)) {
5140                /*tex Do we really need to copy here? */
5141                tex_aux_normalize_glue(term);
5142            } else {
5143                term = factor;
5144            }
5145            break;
5146        case expression_multiply:
5147            /*tex
5148                If a multiplication is followed by a division, the two operations are combined into
5149                a 'scaling' operation. Otherwise the term |t| is multiplied by the factor |f|.
5150            */
5151            if (operation == expression_divide) {
5152                numerator = factor;
5153                operation = expression_scale;
5154            } else {
5155                switch (level) {
5156                    case integer_val_level:
5157                    case attribute_val_level:
5158                        term = tex_multiply_integers(term, factor);
5159                        break;
5160                    case posit_val_level:
5161                        term = tex_posit_mul(term, factor);
5162                        break;
5163                    case dimension_val_level:
5164                        term = tex_nx_plus_y(term, factor, 0);
5165                        break;
5166                    default:
5167                        glue_amount(term) = tex_nx_plus_y(glue_amount(term), factor, 0);
5168                        glue_stretch(term) = tex_nx_plus_y(glue_stretch(term), factor, 0);
5169                        glue_shrink(term) = tex_nx_plus_y(glue_shrink(term),  factor, 0);
5170                        break;
5171                }
5172            }
5173            break;
5174        case expression_divide:
5175            /*tex Here we divide the term |t| by the factor |f|. */
5176            switch (level) {
5177                case integer_val_level:
5178                case attribute_val_level:
5179                case dimension_val_level:
5180                    term = tex_aux_quotient(term, factor, 1);
5181                    break;
5182                case posit_val_level:
5183                    if (factor == 0) {
5184                        lmt_scanner_state.arithmic_error = 1;
5185                        term = 0;
5186                    } else {   
5187                        term = tex_posit_div(term, factor);
5188                    }
5189                    break;
5190                default: 
5191                    glue_amount(term) = tex_aux_quotient(glue_amount(term), factor, 1);
5192                    glue_stretch(term) = tex_aux_quotient(glue_stretch(term), factor, 1);
5193                    glue_shrink(term) = tex_aux_quotient(glue_shrink(term), factor, 1);
5194                    break;
5195            }
5196            break;
5197        case expression_scale:
5198            /*tex Here the term |t| is multiplied by the quotient $n/f$. */
5199            switch (level) {
5200                case integer_val_level:
5201                case attribute_val_level:
5202                    term = tex_fract(term, numerator, factor, max_integer);
5203                    break;
5204                case posit_val_level:
5205                    if (numerator == 0) {
5206                        lmt_scanner_state.arithmic_error = 1;
5207                        term = 0;
5208                    } else {   
5209                        term = tex_posit_div(tex_posit_mul(term, factor), numerator);
5210                    }
5211                    break;
5212                case dimension_val_level:
5213                    term = tex_fract(term, numerator, factor, max_dimension);
5214                    break;
5215                default:
5216                    glue_amount(term) = tex_fract(glue_amount(term),   numerator, factor, max_dimension);
5217                    glue_stretch(term) = tex_fract(glue_stretch(term), numerator, factor, max_dimension);
5218                    glue_shrink(term) = tex_fract(glue_shrink(term),  numerator, factor, max_dimension);
5219                    break;
5220            }
5221            break;
5222        case expression_idivide:
5223            /*tex Here we divide the term |t| by the factor |f| but we don't round. */
5224            if (level < glue_val_level) {
5225                term = tex_aux_quotient(term, factor, 0);
5226            } else {
5227                glue_amount(term) = tex_aux_quotient(glue_amount(term),   factor, 0);
5228                glue_stretch(term) = tex_aux_quotient(glue_stretch(term), factor, 0);
5229                glue_shrink(term) = tex_aux_quotient(glue_shrink(term),  factor, 0);
5230            }
5231            break;
5232        case expression_imodulo:
5233            /* \the\numexpr#2-(#2:#1)*#1\relax */
5234            if (level < glue_val_level) {
5235                term = tex_aux_modulo(term, factor);
5236            } else {
5237                glue_amount(term) = tex_aux_modulo(glue_amount(term),   factor);
5238                glue_stretch(term) = tex_aux_modulo(glue_stretch(term), factor);
5239                glue_shrink(term) = tex_aux_modulo(glue_shrink(term),  factor);
5240            }
5241            break;
5242    }
5243    if (operation > expression_subtract) {
5244        state = operation;
5245    } else {
5246        /*tex
5247            Evaluate the current expression. When a term |t| has been completed it is copied to,
5248            added to, or subtracted from the expression |e|.
5249        */
5250        state = expression_none;
5251        if (result == expression_none) {
5252            expression = term;
5253        } else {
5254            switch (level) {
5255                case integer_val_level:
5256                case attribute_val_level:
5257                    expression = tex_aux_add_or_sub(expression, term, max_integer, result);
5258                    break;
5259                case posit_val_level:
5260                    switch (result) {
5261                        case expression_subtract:
5262                            expression = tex_posit_sub(expression, term);
5263                            break;
5264                        case expression_add:
5265                            expression = tex_posit_add(expression, term);
5266                            break;
5267                    }
5268                    break;
5269                case dimension_val_level:
5270                    expression = tex_aux_add_or_sub(expression, term, max_dimension, result);
5271                    break;
5272                default :
5273                    /*tex
5274                        Compute the sum or difference of two glue specs. We know that |stretch_order
5275                        (e) > normal| implies |stretch (e) <> 0| and |shrink_order (e)  > normal|
5276                        implies |shrink (e) <> 0|.
5277                    */
5278                    glue_amount(expression) = tex_aux_add_or_sub(glue_amount(expression), glue_amount(term), max_dimension, result);
5279                    if (glue_stretch_order(expression) == glue_stretch_order(term)) {
5280                        glue_stretch(expression) = tex_aux_add_or_sub(glue_stretch(expression), glue_stretch(term), max_dimension, result);
5281                    } else if ((glue_stretch_order(expression) < glue_stretch_order(term)) && (glue_stretch(term) != 0)) {
5282                        glue_stretch(expression) = glue_stretch(term);
5283                        glue_stretch_order(expression) = glue_stretch_order(term);
5284                    }
5285                    if (glue_shrink_order(expression) == glue_shrink_order(term)) {
5286                        glue_shrink(expression) = tex_aux_add_or_sub(glue_shrink(expression), glue_shrink(term), max_dimension, result);
5287                    } else if ((glue_shrink_order(expression) < glue_shrink_order(term)) && (glue_shrink(term) != 0)) {
5288                        glue_shrink(expression) = glue_shrink(term);
5289                        glue_shrink_order(expression) = glue_shrink_order(term);
5290                    }
5291                    tex_flush_node(term);
5292                    tex_aux_normalize_glue(expression);
5293                    break;
5294            }
5295        }
5296        result = operation;
5297    }
5298    error_b = lmt_scanner_state.arithmic_error;
5299    if (operation != expression_none) {
5300        goto CONTINUE;
5301    } else if (top) {
5302        /*tex Pop the expression stack and |goto found|. */
5303        halfword t = top;
5304        top = node_next(top);
5305        factor = expression;
5306        expression = expression_expression(t);
5307        term = expression_term(t);
5308        numerator = expression_numerator(t);
5309        state = expression_state(t);
5310        result = expression_result(t);
5311        level = expression_type(t);
5312        tex_free_node(t, expression_node_size);
5313        goto FOUND;
5314    } else if (error_b) {
5315        tex_handle_error(
5316            normal_error_type,
5317            "Arithmetic overflow",
5318            "I can't evaluate this expression, since the result is out of range."
5319        );
5320        if (level >= glue_val_level) {
5321            tex_reset_glue_to_zero(expression);
5322        } else {
5323            expression = 0;
5324        }
5325    }
5326    lmt_scanner_state.arithmic_error = error_a;
5327    lmt_scanner_state.expression_depth--;
5328    cur_val_level = level;
5329    cur_val = expression;
5330}
5331
5332/*tex
5333
5334    Already early in \LUAMETATEX\ I wondered about adding suypport for boolean expressions but at
5335    that time (2019) I still wanted it as part of \type |\numexpr|. I added some code that actually
5336    worked okay, but kept it commented. After all, we don't need it that often and \CONTEXT\ has
5337    helpers for it so it's best to avoid the extra overhead in other expressions.
5338
5339    However, occasionally, when I check the manual I came back to this. I wondered about some more
5340    that just extra bitwise operators. However, prcedence makes it a bit tricky. Also, we can't use
5341    some characters because they can be letter, other, active or have special meaning in math or
5342    alignments. Then I played with verbose operators: mod (instead of a percent sign), and
5343    |and|, |or|, |band|, |bor| and |bxor| (cf the \LUA\ bit32 library).
5344
5345    In the end I decided not to integrate it but make a dedicated |\bitexpr| instead. I played with
5346    some variants but the approach in the normal expression scanned is not really suitable for it.
5347
5348    In the end, after some variations, I decided that some reverse polish notation approach made
5349    more sense and when considering an infix to rpn translation and searching the web a bit I ran
5350    into nice example:
5351
5352        https://github.com/chidiwilliams/expression-evaluator/blob/main/simple.js
5353
5354    It shows how to handled the nested expressions. I made a comaprable variant in \LUA, extended
5355    it for more than the usual four operators, condensed it a bit and then went on to write the code
5356    below. Of course we have a completely different token parser and we use \TEX\ (temp) nodes for
5357    a few stacks. I know that we can combine the loops but that becomes messy and performance is
5358    quite okay, also because we move items from one to another stack with little overhead. Although
5359    stacks are not that large, using static sized stacks (\CCODE\ arrays) makes no sense here.
5360
5361    After the initial |\bitexpr| I eventually ended up with an integer and dimension scanner and
5362    it became more complex that originally intended, but the current implementaiton is flexible
5363    enough to extend. I can probably squeeze out some more performance.
5364
5365    Beware: details can change, for instance handling some (math) \UNICODE\ characters has been
5366    dropped because it's an inconsistent bunch and incomplete anyway.
5367
5368    In the end we have a set of dedicated scanners. We could use the existing ones but for instance
5369    units are optional here. We also have a bit more predictable sentinel, so we can optimize some
5370    push back. We don't handle mu units nor fillers. It was also kind of fun to explore that.
5371
5372*/
5373
5374typedef enum bit_expression_states {
5375    bit_expression_none,
5376
5377    bit_expression_bor,       /*  |   bor  v */
5378    bit_expression_band,      /*  &   band   */
5379    bit_expression_bxor,      /*  ^   bxor   */
5380
5381    bit_expression_bset,      /*      bset   */
5382    bit_expression_bunset,    /*      bunset */
5383
5384    bit_expression_bleft,     /*  <<         */
5385    bit_expression_bright,    /*  >>         */
5386
5387    bit_expression_less,      /*  <          */
5388    bit_expression_lessequal, /*  <=         */
5389    bit_expression_equal,     /*  =   ==     */
5390    bit_expression_moreequal, /*  >=         */
5391    bit_expression_more,      /*  >          */
5392    bit_expression_unequal,   /*  <>  !=     */
5393
5394    bit_expression_add,       /*  +          */
5395    bit_expression_subtract,  /*  -          */
5396
5397    bit_expression_multiply,  /*  *          */
5398    bit_expression_divide,    /*  /   :      */
5399
5400    bit_expression_mod,       /*  %   mod    */
5401
5402 // bit_expression_power,     /*             */
5403
5404    bit_expression_not,       /* ! ~  not    */
5405
5406    bit_expression_or,        /* or          */
5407    bit_expression_and,       /* and         */
5408
5409    bit_expression_open,
5410    bit_expression_close,
5411
5412    bit_expression_number,
5413    bit_expression_float,
5414    bit_expression_dimension,
5415} bit_expression_states;
5416
5417
5418static int bit_operator_precedence[] = {  /* like in lua */
5419    0, // bit_expression_none
5420    4, // bit_expression_bor
5421    6, // bit_expression_band
5422    5, // bit_expression_bxor
5423
5424    7, // bit_expression_bset   // like shifts
5425    7, // bit_expression_bunset // like shifts
5426
5427    7, // bit_expression_bleft
5428    7, // bit_expression_bright
5429
5430    3, // bit_expression_less
5431    3, // bit_expression_lessequal
5432    3, // bit_expression_equal
5433    3, // bit_expression_more
5434    3, // bit_expression_moreequal
5435    3, // bit_expression_unequal
5436
5437    8, // bit_expression_add
5438    8, // bit_expression_subtract
5439
5440    9, // bit_expression_multiply
5441    9, // bit_expression_divide
5442
5443    9, // bit_expression_mod
5444
5445// 10, // bit_expression_power
5446
5447   10, // bit_expression_not
5448
5449    1, // bit_expression_or
5450    2, // bit_expression_and
5451
5452    0, // bit_expression_open
5453    0, // bit_expression_close
5454
5455    0, // bit_expression_number
5456    0,
5457    0,
5458};
5459
5460static const char *bit_expression_names[] = {
5461    "none", "bor", "band", "bxor", "bset", "bunset",
5462    "<<", ">>", "<", "<=", "==", ">=", ">", "<>",
5463    "+", "-", "*", "/", "mod", "not", "or", "and",
5464    "open", "close", "number", "float", "dimension"
5465};
5466
5467/*tex
5468    This way we stay within the regular tex accuracy with 1000 scales. But I will play with a 
5469    variant that only uses doubles: |dimenexpression| and |numberexpression|. 
5470*/
5471
5472# define factor 1 // 256, 1000 : wrong results so needs a fix 
5473
5474typedef struct stack_info {
5475    halfword head;
5476    halfword tail;
5477} stack_info;
5478
5479static stack_info tex_aux_new_stack(void)
5480{
5481    return (stack_info) {
5482        .head = null,
5483        .tail = null,
5484    };
5485}
5486
5487static void tex_aux_dispose_stack(stack_info *stack)
5488{
5489    /*tex Unless we have a problem we have stacks with zero or one slot. */
5490    halfword current = stack->head;
5491    while (current) {
5492        halfword next = node_next(current);
5493        tex_free_node(current, expression_node_size);
5494        current = next;
5495    }
5496}
5497
5498static void tex_push_stack_entry(stack_info *stack, long long value)
5499{
5500    halfword n = tex_get_node(expression_node_size);
5501    node_type(n) = expression_node;
5502    node_subtype(n) = 0;
5503    expression_entry(n) = value;
5504    if (! stack->head) {
5505        stack->head = n;
5506    } else if (stack->head == stack->tail)  {
5507        node_next(stack->head) = n;
5508        node_prev(n) = stack->head;
5509    } else {
5510        node_prev(n) = stack->tail;
5511        node_next(stack->tail) = n;
5512    }
5513    stack->tail = n;
5514}
5515
5516static long long tex_pop_stack_entry(stack_info *stack)
5517{
5518    halfword t = stack->tail;
5519    if (t) {
5520        long long v = expression_entry(t);
5521        if (t == stack->head) {
5522            stack->head = null;
5523            stack->tail = null;
5524        } else {
5525            stack->tail = node_prev(t);
5526            node_next(stack->tail) = null;
5527        }
5528        tex_free_node(t, temp_node_size);
5529        return v;
5530    } else {
5531        return 0;
5532    }
5533}
5534
5535static void tex_move_stack_entry(stack_info *target, stack_info *source)
5536{
5537    halfword n = source->tail;
5538    if (n == source->head) {
5539        source->head = null;
5540        source->tail = null;
5541    } else {
5542        source->tail = node_prev(n);
5543    }
5544    if (! target->head) {
5545        target->head = n;
5546        node_prev(n) = null;
5547    } else if (target->head == target->tail)  {
5548        node_next(target->head) = n;
5549        node_prev(n) = target->head;
5550    } else {
5551        node_prev(n) = target->tail;
5552        node_next(target->tail) = n;
5553    }
5554    node_next(n) = null;
5555    target->tail = n;
5556}
5557
5558static void tex_take_stack_entry(stack_info *target, stack_info *source, halfword current)
5559{
5560    while (source->head != current) {
5561        halfword next = node_next(source->head);
5562        tex_free_node(source->head, temp_node_size);
5563        source->head = next;
5564    }
5565    if (current == source->tail) {
5566        source->head = null;
5567        source->tail = null;
5568    } else {
5569        source->head = node_next(current);
5570    }
5571    if (! target->head) {
5572        target->head = current;
5573        node_prev(current) = null;
5574    } else if (target->head == target->tail)  {
5575        node_next(target->head) = current;
5576        node_prev(current) = target->head;
5577    } else {
5578        node_prev(current) = target->tail;
5579        node_next(target->tail) = current;
5580    }
5581    node_next(current) = null;
5582    target->tail = current;
5583}
5584
5585static halfword tex_aux_scan_unit_applied(halfword value, halfword fraction, int has_fraction, int *has_unit)
5586{
5587    do {
5588        tex_get_x_token();
5589    } while (cur_cmd == spacer_cmd);
5590    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
5591        halfword saved_val = value;
5592        value = tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
5593        value = tex_nx_plus_y(saved_val, cur_val, tex_xn_over_d(cur_val, fraction, 0200000));
5594        return value;
5595    } else if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5596        halfword num = 0;
5597        halfword denom = 0;
5598        halfword saved_cs = cur_cs;
5599        halfword saved_tok = cur_tok;
5600        *has_unit = 1;
5601        switch (cur_chr) {
5602            case 'p': case 'P':
5603                tex_get_x_token();
5604                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5605                    switch (cur_chr) {
5606                        case 't': case 'T':
5607                            goto NORMALUNIT;
5608                        case 'c': case 'C':
5609                            num = 12;
5610                            denom = 1;
5611                            goto NORMALUNIT;
5612                        case 'x': case 'X':
5613                            return tex_nx_plus_y(value, px_dimension_par, tex_xn_over_d(px_dimension_par, fraction, 0200000));
5614                    }
5615                }
5616                break;
5617            case 'c': case 'C':
5618                tex_get_x_token();
5619                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5620                    switch (cur_chr) {
5621                        case 'm': case 'M':
5622                            num = 7227;
5623                            denom = 254;
5624                            goto NORMALUNIT;
5625                        case 'c': case 'C':
5626                            num = 14856;
5627                            denom = 1157;
5628                            goto NORMALUNIT;
5629                    }
5630                }
5631                break;
5632            case 's': case 'S':
5633                tex_get_x_token();
5634                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5635                    switch (cur_chr) {
5636                        case 'p': case 'P':
5637                            return scaled_point_scanned;
5638                    }
5639                }
5640                break;
5641            case 't': case 'T':
5642                tex_get_x_token();
5643                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5644                    switch (cur_chr) {
5645                        case 's': case 'S':
5646                            num = 4588;
5647                            denom = 645;
5648                            goto NORMALUNIT;
5649                    }
5650                }
5651                break;
5652            case 'b': case 'B':
5653                tex_get_x_token();
5654                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5655                    switch (cur_chr) {
5656                        case 'p': case 'P':
5657                            num = 7227;
5658                            denom = 7200;
5659                            goto NORMALUNIT;
5660                    }
5661                }
5662                break;
5663            case 'i': case 'I':
5664                tex_get_x_token();
5665                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5666                    switch (cur_chr) {
5667                        case 'n': case 'N':
5668                            num = 7227;
5669                            denom = 100;
5670                            goto NORMALUNIT;
5671                    }
5672                }
5673                break;
5674            case 'd': case 'D':
5675                tex_get_x_token();
5676                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5677                    switch (cur_chr) {
5678                        case 'd': case 'D':
5679                            num = 1238;
5680                            denom = 1157;
5681                            goto NORMALUNIT;
5682                    }
5683                }
5684                break;
5685            case 'e': case 'E':
5686                tex_get_x_token();
5687                if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
5688                    switch (cur_chr) {
5689                        case 'm': case 'M':
5690                            return tex_get_scaled_em_width(cur_font_par);
5691                        case 'x': case 'X':
5692                            return tex_get_scaled_ex_height(cur_font_par);
5693                        case 's': case 'S':
5694                            num = 9176;
5695                            denom = 129;
5696                            goto NORMALUNIT;
5697                        case 'u': case 'U':
5698                            num = 9176 * eu_factor_par;
5699                            denom = 129 * 10;
5700                            goto NORMALUNIT;
5701                    }
5702                }
5703                break;
5704            default:
5705                goto HALFUNIT;
5706        }
5707        goto NOUNIT;
5708      NORMALUNIT:
5709        if (num) {
5710            int remainder = 0;
5711            value = tex_xn_over_d_r(value, num, denom, &remainder);
5712            fraction = (num * fraction + 0200000 * remainder) / denom;
5713            value += fraction / 0200000;
5714            fraction = fraction % 0200000;
5715        }
5716        if (value >= 040000) { // 0x4000
5717            lmt_scanner_state.arithmic_error = 1;
5718        } else {
5719            value = value * unity + fraction;
5720        }
5721        return value;
5722      NOUNIT:
5723        tex_back_input(cur_tok);
5724      HALFUNIT:
5725        tex_back_input(saved_tok);
5726        cur_cs = saved_cs;
5727        cur_tok = saved_tok;
5728    } else {
5729        tex_back_input(cur_tok);
5730    }
5731    if (has_fraction) {
5732        *has_unit = 0;
5733        if (value >= 040000) { // 0x4000
5734            lmt_scanner_state.arithmic_error = 1;
5735        } else {
5736            value = value * unity + fraction;
5737        }
5738    }
5739    return value;
5740}
5741
5742// This replaces the above but I need to test it first! 
5743
5744// static halfword tex_aux_scan_unit_applied(halfword value, halfword fraction, int has_fraction, int *has_unit)
5745// {
5746//     halfword num = 0;
5747//     halfword denom = 0;
5748//     scaled unit = 0;
5749//     *has_unit = 0;
5750//     switch (tex_aux_scan_unit(&num, &denom, &unit, NULL)) {
5751//         case normal_unit_scanned:
5752//             if (num) {
5753//                 int remainder = 0;
5754//                 cur_val = tex_xn_over_d_r(cur_val, num, denom, &remainder);
5755//                 fraction = (num * fraction + 0200000 * remainder) / denom;
5756//                 cur_val += fraction / 0200000;
5757//                 fraction = fraction % 0200000;
5758//             }
5759//             *has_unit = 1;
5760//             goto FRACTION;
5761//         case scaled_point_scanned:
5762//             *has_unit = 0;
5763//             return value;
5764//         case relative_unit_scanned:
5765//             *has_unit = 1;
5766//             return tex_nx_plus_y(value, unit, tex_xn_over_d(unit, fraction, 0200000));
5767//         case quantitity_unit_scanned:
5768//             tex_aux_scan_something_internal(cur_cmd, cur_chr, dimension_val_level, 0, 0);
5769//             value = tex_nx_plus_y(value, cur_val, tex_xn_over_d(cur_val, fraction, 0200000));
5770//             *has_unit = 1;
5771//             return value;
5772//      /* case math_unit_scanned:     */ /* ignored */
5773//      /* case flexible_unit_scanned: */ /* ignored */
5774//      /* case no_unit_scanned:       */ /* nothing */
5775//     }
5776//   FRACTION:
5777//     if (has_fraction) {
5778//         *has_unit = 0;
5779//         if (value >= 040000) { // 0x4000
5780//             lmt_scanner_state.arithmic_error = 1;
5781//         } else {
5782//             value = value * unity + fraction;
5783//         }
5784//     }
5785//     return value;
5786// }
5787
5788static halfword tex_scan_bit_int(int *radix)
5789{
5790    bool negative = false;
5791    long long result = 0;
5792    do {
5793        if (cur_tok == minus_token) {
5794            negative = ! negative;
5795            cur_tok = plus_token;
5796        }
5797    } while (cur_tok == plus_token);
5798    if (cur_tok == alpha_token) {
5799        tex_get_token();
5800        if (cur_tok < cs_token_flag) {
5801            result = cur_chr;
5802        } else {
5803            strnumber txt = cs_text(cur_tok - cs_token_flag);
5804            if (tex_single_letter(txt)) {
5805                result = aux_str2uni(str_string(txt));
5806            } else if (tex_is_active_cs(txt)) {
5807                result = active_cs_value(txt);
5808            } else {
5809                result = max_character_code + 1;
5810            }
5811        }
5812        if (result > max_character_code) {
5813            tex_aux_improper_constant_error();
5814            return 0; 
5815        }
5816    } else if ((cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) || cur_cmd == parameter_cmd) {
5817        result = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
5818        if (cur_val_level != integer_val_level) {
5819            tex_aux_missing_number_error();
5820            return 0;
5821        }
5822    } else {
5823        bool vacuous = true;
5824        bool ok_so_far = true;
5825        switch (cur_tok) {
5826            case octal_token:
5827                {
5828                    if (radix) {
5829                        *radix = 8;
5830                    }
5831                    while (1) {
5832                        unsigned d = 0;
5833                        tex_get_x_token();
5834                        if ((cur_tok >= zero_token) && (cur_tok <= seven_token)) {
5835                            d = cur_tok - zero_token;
5836                        } else {
5837                            goto DONE;
5838                        }
5839                        vacuous = false;
5840                        if (ok_so_far) {
5841                            result = result * 8 + d;
5842                            if (result > max_integer) {
5843                                result = max_integer;
5844                                tex_aux_number_to_big_error();
5845                                ok_so_far = false;
5846                            }
5847                        }
5848                    }
5849                 // break;
5850                }
5851            case hex_token:
5852                {
5853                    if (radix) {
5854                        *radix = 16;
5855                    }
5856                    while (1) {
5857                        unsigned d = 0;
5858                        tex_get_x_token();
5859                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
5860                            d = cur_tok - zero_token;
5861                        } else if ((cur_tok >= A_token_l) && (cur_tok <= F_token_l)) {
5862                            d = cur_tok - A_token_l + 10;
5863                        } else if ((cur_tok >= A_token_o) && (cur_tok <= F_token_o)) {
5864                            d = cur_tok - A_token_o + 10;
5865                        } else {
5866                            goto DONE;
5867                        }
5868                        vacuous = false;
5869                        if (ok_so_far) {
5870                            result = result * 16 + d;
5871                            if (result > max_integer) {
5872                                result = max_integer;
5873                                tex_aux_number_to_big_error();
5874                                ok_so_far = false;
5875                            }
5876                        }
5877                    }
5878                 // break;
5879                }
5880            default:
5881                {
5882                    if (radix) {
5883                        *radix = 10;
5884                    }
5885                    while (1) {
5886                        unsigned d = 0;
5887                        if ((cur_tok >= zero_token) && (cur_tok <= nine_token)) {
5888                            d = cur_tok - zero_token;
5889                        } else {
5890                            goto DONE;
5891                        }
5892                        vacuous = false;
5893                        if (ok_so_far) {
5894                            result = result * 10 + d;
5895                            if (result > max_integer) {
5896                                result = max_integer;
5897                                tex_aux_number_to_big_error();
5898                                ok_so_far = false;
5899                            }
5900                        }
5901                        tex_get_x_token();
5902                    }
5903                 // break;
5904                }
5905        }
5906      DONE:
5907        if (vacuous) {
5908            tex_aux_missing_number_error();
5909        } else {
5910            tex_push_back(cur_tok, cur_cmd, cur_chr);
5911        }
5912    }
5913    cur_val = (halfword) (negative ? - result : result);
5914    return cur_val;
5915}
5916
5917static halfword tex_scan_bit_dimension(int *has_fraction, int *has_unit)
5918{
5919    bool negative = false;
5920    int fraction = 0;
5921    *has_fraction = 0;
5922    *has_unit = 1;
5923    lmt_scanner_state.arithmic_error = 0;
5924    do {
5925        if (cur_tok == minus_token) {
5926            negative = ! negative;
5927            cur_tok = plus_token;
5928        }
5929    } while (cur_tok == plus_token);
5930    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
5931        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
5932        if (cur_val_level == dimension_val_level) {
5933            goto ATTACH_SIGN;
5934        }
5935    } else {
5936        *has_fraction = tex_token_is_seperator(cur_tok);
5937        if (*has_fraction) {
5938            /*tex We started with a |.| or |,|. */
5939            cur_val = 0;
5940        } else {
5941            int cur_radix = 10;
5942            cur_val = tex_scan_bit_int(&cur_radix);
5943            if (cur_radix == 10 && tex_token_is_seperator(cur_tok)) {
5944                *has_fraction = 1;
5945                tex_get_token();
5946            }
5947        }
5948        if (*has_fraction) {
5949            unsigned k = 0;
5950            unsigned char digits[18];
5951            while (1) {
5952                tex_get_x_token();
5953                if (cur_tok > nine_token || cur_tok < zero_token) {
5954                    break;
5955                } else if (k < 17) {
5956                    digits[k] = (unsigned char) (cur_tok - zero_token);
5957                    ++k;
5958                }
5959            }
5960            fraction = tex_round_decimals_digits(digits, k);
5961            if (cur_cmd != spacer_cmd) {
5962                /* we can avoid this when parsing a unit but not now */
5963                tex_back_input(cur_tok);
5964            }
5965        }
5966    }
5967    if (cur_val < 0) {
5968        negative = ! negative;
5969        cur_val = - cur_val;
5970    }
5971    cur_val = tex_aux_scan_unit_applied(cur_val, fraction, *has_fraction, has_unit);
5972  ATTACH_SIGN:
5973    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 010000000000)) { // 0x40000000
5974        tex_aux_scan_dimension_out_of_range_error();
5975        cur_val = max_dimension;
5976        lmt_scanner_state.arithmic_error = 0;
5977    }
5978    if (negative) {
5979        cur_val = -cur_val;
5980    }
5981    return cur_val;
5982}
5983
5984static void tex_aux_trace_expression(stack_info stack, halfword level, halfword n, int what)
5985{
5986    tex_begin_diagnostic();
5987    if (n > 0) {
5988        tex_print_format(level == dimension_val_level ? "[dimexpression rpn %i %s:" : "[numexpression rpn %i %s:", n, what ? "r" :"s");
5989        if (! stack.head) {
5990            tex_print_char(' ');
5991        }
5992    } else {
5993        tex_print_str(level == dimension_val_level ? "[dimexpression rpn:" : "[numexpression rpn:");
5994    }
5995    for (halfword current = stack.head; current; current = node_next(current)) {
5996        tex_print_char(' ');
5997        switch (node_subtype(current)) {
5998            case bit_expression_number:
5999                tex_print_int(scaledround((double) expression_entry(current) / factor));
6000                break;
6001            case bit_expression_float:
6002                tex_print_dimension(scaledround((double) expression_entry(current) / factor), no_unit);
6003                break;
6004            case bit_expression_dimension:
6005                tex_print_char('(');
6006                tex_print_dimension(scaledround((double) expression_entry(current) / factor), no_unit);
6007                tex_print_char(')');
6008                break;
6009            default:
6010                tex_print_str(bit_expression_names[expression_entry(current)]);
6011                break;
6012        }
6013    }
6014    tex_print_char(']');
6015    tex_end_diagnostic();
6016}
6017
6018/* This one is not yet okay ... work in progress. We might go for posits here. */
6019
6020static void tex_aux_scan_expression(int level)
6021{
6022    stack_info operators = tex_aux_new_stack();
6023    stack_info reverse = tex_aux_new_stack();
6024    stack_info stack = tex_aux_new_stack();
6025    halfword operation = bit_expression_none;
6026    bool alreadygotten = false;
6027    int braced = 0;
6028    int trace = tracing_expressions_par;
6029    while (1) {
6030        if (alreadygotten) {
6031            alreadygotten = false;
6032        } else {
6033            tex_get_x_token();
6034        }
6035        operation = bit_expression_none;
6036        switch (cur_cmd) {
6037            case relax_cmd:
6038                goto COLLECTED;
6039            case left_brace_cmd: 
6040                if (! braced) {
6041                    braced = 1;
6042                    continue;
6043                } else {
6044                    goto NUMBER;
6045                 // goto UNEXPECTED;
6046                }
6047            case right_brace_cmd:
6048                if (braced) {
6049                    goto COLLECTED;
6050                } else {
6051                    goto NUMBER;
6052                 // goto UNEXPECTED;
6053                }
6054            case spacer_cmd:
6055                continue;
6056            case superscript_cmd:
6057                switch (cur_chr) {
6058                    case '^':
6059                        operation = bit_expression_bxor;
6060                        goto OKAY;
6061                }
6062                goto UNEXPECTED;
6063            case alignment_tab_cmd:
6064                switch (cur_chr) {
6065                    case '&':
6066                        tex_get_x_token();
6067                        switch (cur_cmd) {
6068                            case letter_cmd:
6069                            case other_char_cmd:
6070                            case alignment_tab_cmd:
6071                                switch (cur_chr) {
6072                                    case '&':
6073                                        operation = bit_expression_and;
6074                                        goto OKAY;
6075                                    default:
6076                                        operation = bit_expression_band;
6077                                        alreadygotten = true;
6078                                        goto OKAY;
6079                                }
6080                        }
6081                }
6082                goto UNEXPECTED;
6083            case letter_cmd:
6084            case other_char_cmd:
6085                switch (cur_chr) {
6086                    case '(':
6087                        tex_push_stack_entry(&operators, bit_expression_open);
6088                        continue;
6089                    case ')':
6090                        while (operators.tail && expression_entry(operators.tail) != bit_expression_open) {
6091                            tex_move_stack_entry(&reverse, &operators);
6092                        }
6093                        tex_pop_stack_entry(&operators);
6094                        continue;
6095                    case '+':
6096                        operation = bit_expression_add;
6097                        break;
6098                    case '-':
6099                        operation = bit_expression_subtract;
6100                        break;
6101                    case '*':
6102                        operation = bit_expression_multiply;
6103                        break;
6104                    case '/':
6105                    case ':':
6106                        operation = bit_expression_divide;
6107                        break;
6108                    case '%':
6109                    case ';':
6110                        operation = bit_expression_mod;
6111                        break;
6112                    case '&':
6113                        tex_get_x_token();
6114                        switch (cur_cmd) {
6115                            case letter_cmd:
6116                            case other_char_cmd:
6117                            case alignment_tab_cmd:
6118                                switch (cur_chr) {
6119                                    case '&':
6120                                        operation = bit_expression_and;
6121                                        goto OKAY;
6122                                }
6123                        }
6124                        operation = bit_expression_band;
6125                        alreadygotten = true;
6126                        break;
6127                    case '^':
6128                        operation = bit_expression_bxor;
6129                        break;
6130                    case 'v':
6131                        operation = bit_expression_bor;
6132                        break;
6133                    case '|':
6134                        tex_get_x_token();
6135                        switch (cur_cmd) {
6136                            case letter_cmd:
6137                            case other_char_cmd:
6138                                switch (cur_chr) {
6139                                    case '|':
6140                                        operation = bit_expression_or;
6141                                        goto OKAY;
6142                                }
6143                        }
6144                        operation = bit_expression_bor;
6145                        alreadygotten = true;
6146                        break;
6147                    case '<':
6148                        tex_get_x_token();
6149                        switch (cur_cmd) {
6150                            case letter_cmd:
6151                            case other_char_cmd:
6152                                switch (cur_chr) {
6153                                    case '<':
6154                                        operation = bit_expression_bleft;
6155                                        goto OKAY;
6156                                    case '=':
6157                                        operation = bit_expression_lessequal;
6158                                        goto OKAY;
6159                                    case '>':
6160                                        operation = bit_expression_unequal;
6161                                        goto OKAY;
6162                                }
6163                        }
6164                        operation = bit_expression_less;
6165                        alreadygotten = true;
6166                        break;
6167                    case '>':
6168                        tex_get_x_token();
6169                        switch (cur_cmd) {
6170                            case letter_cmd:
6171                            case other_char_cmd:
6172                                switch (cur_chr) {
6173                                    case '>':
6174                                        operation = bit_expression_bright;
6175                                        goto OKAY;
6176                                    case '=':
6177                                        operation = bit_expression_moreequal;
6178                                        goto OKAY;
6179                                }
6180                        }
6181                        operation = bit_expression_more;
6182                        alreadygotten = true;
6183                        break;
6184                    case '=':
6185                        tex_get_x_token();
6186                        switch (cur_cmd) {
6187                            case letter_cmd:
6188                            case other_char_cmd:
6189                                switch (cur_chr) {
6190                                    case '=':
6191                                        break;
6192                                    default:
6193                                        alreadygotten = true;
6194                                        break;
6195                                }
6196                        }
6197                        operation = bit_expression_equal;
6198                        break;
6199                    case '~': case '!':
6200                        tex_get_x_token();
6201                        switch (cur_cmd) {
6202                            case letter_cmd:
6203                            case other_char_cmd:
6204                                switch (cur_chr) {
6205                                    case '=':
6206                                        operation = bit_expression_unequal;
6207                                        goto OKAY;
6208                                }
6209                        }
6210                        operation = bit_expression_not;
6211                        alreadygotten = true;
6212                        break;
6213                    case 'm': case 'M':
6214                        tex_get_x_token();
6215                        switch (cur_cmd) {
6216                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'o': case 'O':
6217                                tex_get_x_token();
6218                                switch (cur_cmd) {
6219                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'd': case 'D':
6220                                        operation = bit_expression_mod;
6221                                        goto OKAY;
6222                                    }
6223                                }
6224                            }
6225                        }
6226                        goto UNEXPECTED;
6227                    case 'n': case 'N':
6228                        tex_get_x_token();
6229                        switch (cur_cmd) {
6230                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'o': case 'O':
6231                                tex_get_x_token();
6232                                switch (cur_cmd) {
6233                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'o': case 'T':
6234                                        operation = bit_expression_not;
6235                                        goto OKAY;
6236                                    }
6237                                }
6238                            }
6239                        }
6240                        goto UNEXPECTED;
6241                    case 'a': case 'A':
6242                        tex_get_x_token();
6243                        switch (cur_cmd) {
6244                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'n': case 'N':
6245                                tex_get_x_token();
6246                                switch (cur_cmd) {
6247                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'd': case 'D':
6248                                        operation = bit_expression_and;
6249                                        goto OKAY;
6250                                    }
6251                                }
6252                            }
6253                        }
6254                        goto UNEXPECTED;
6255                    case 'b': case 'B':
6256                        tex_get_x_token();
6257                        switch (cur_cmd) {
6258                            case letter_cmd: case other_char_cmd:
6259                                switch (cur_chr) {
6260                                    case 'a': case 'A':
6261                                        tex_get_x_token();
6262                                        switch (cur_cmd) {
6263                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'n': case 'N':
6264                                                tex_get_x_token();
6265                                                switch (cur_cmd) {
6266                                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'd': case 'D':
6267                                                        operation = bit_expression_band;
6268                                                        goto OKAY;
6269                                                    }
6270                                                }
6271                                            }
6272                                        }
6273                                        break;
6274                                    case 'o': case 'O':
6275                                        tex_get_x_token();
6276                                        switch (cur_cmd) {
6277                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'r': case 'R':
6278                                                operation = bit_expression_bor;
6279                                                goto OKAY;
6280                                            }
6281                                        }
6282                                        break;
6283                                    case 'x': case 'X':
6284                                        tex_get_x_token();
6285                                        switch (cur_cmd) {
6286                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'o': case 'O':
6287                                                tex_get_x_token();
6288                                                switch (cur_cmd) {
6289                                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'r': case 'R':
6290                                                        operation = bit_expression_bxor;
6291                                                        goto OKAY;
6292                                                    }
6293                                                }
6294                                            }
6295                                        }
6296                                        break;
6297                                    case 's': case 'S':
6298                                        tex_get_x_token();
6299                                        switch (cur_cmd) {
6300                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'e': case 'S':
6301                                                tex_get_x_token();
6302                                                switch (cur_cmd) {
6303                                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 't': case 'T':
6304                                                        operation = bit_expression_bset;
6305                                                        goto OKAY;
6306                                                    }
6307                                                }
6308                                            }
6309                                        }
6310                                        break;
6311                                    case 'r': case 'R':
6312                                        tex_get_x_token();
6313                                        switch (cur_cmd) {
6314                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'e': case 'E':
6315                                                tex_get_x_token();
6316                                                switch (cur_cmd) {
6317                                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 's': case 'S':
6318                                                        tex_get_x_token();
6319                                                        switch (cur_cmd) {
6320                                                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'e': case 'S':
6321                                                                tex_get_x_token();
6322                                                                switch (cur_cmd) {
6323                                                                    case letter_cmd: case other_char_cmd: switch (cur_chr) { case 't': case 'T':
6324                                                                        operation = bit_expression_bset;
6325                                                                        goto OKAY;
6326                                                                    }
6327                                                                }
6328                                                            }
6329                                                        }
6330                                                    }
6331                                                }
6332                                            }
6333                                        }
6334                                        break;
6335                                }
6336                        }
6337                        goto UNEXPECTED;
6338                    case 'o': case 'O':
6339                        tex_get_x_token();
6340                        switch (cur_cmd) {
6341                            case letter_cmd: case other_char_cmd: switch (cur_chr) { case 'r': case 'R':
6342                                operation = bit_expression_or;
6343                                goto OKAY;
6344                            }
6345                        }
6346                        goto UNEXPECTED;
6347                    default:
6348                        goto NUMBER;
6349                }
6350              OKAY:
6351                while (operators.tail && bit_operator_precedence[expression_entry(operators.tail)] >= bit_operator_precedence[operation]) {
6352                 // tex_push_stack_entry(&reverse, tex_pop_stack_entry(&operators));
6353                    tex_move_stack_entry(&reverse, &operators);
6354                }
6355                tex_push_stack_entry(&operators, operation);
6356                break;
6357            default:
6358              NUMBER:
6359                /*tex These use |cur_tok|: */
6360                {
6361                    int has_fraction = 0;
6362                    int has_unit = 1;
6363                    operation = level == dimension_val_level ? tex_scan_bit_dimension(&has_fraction, &has_unit) : tex_scan_bit_int(NULL);
6364                    tex_push_stack_entry(&reverse, operation * factor);
6365                    if (level == dimension_val_level && has_unit) {
6366                        node_subtype(reverse.tail) = bit_expression_dimension;
6367                    } else if (has_fraction) {
6368                        node_subtype(reverse.tail) = bit_expression_float;
6369                    } else {
6370                        node_subtype(reverse.tail) = bit_expression_number;
6371                    }
6372                    continue;
6373                }
6374        }
6375    }
6376  COLLECTED:
6377    while (operators.tail) {
6378        tex_move_stack_entry(&reverse, &operators);
6379    }
6380    /*tex This is the reference: */
6381    /*
6382    {
6383        halfword current = reverse.head;
6384        while (current) {
6385            if (node_subtype(current) == bit_expression_number) {
6386                tex_push_stack_entry(&stack, expression_entry(current));
6387            } else {
6388                halfword token = expression_entry(current);
6389                long long v;
6390                if (token == bit_expression_not) {
6391                    v = ~ (long long) tex_pop_stack_entry(&stack);
6392                } else {
6393                    long long b = (long long) tex_pop_stack_entry(&stack);
6394                    long long a = (long long) tex_pop_stack_entry(&stack);
6395                    switch (token) {
6396                       // calculations, see below
6397                    }
6398                }
6399                // checks, see below
6400                tex_push_stack_entry(&stack, (halfword) v);
6401            }
6402            current = node_next(current);
6403        }
6404    }
6405    */
6406    if (trace == 1) {
6407        tex_aux_trace_expression(reverse, level, 0, 0);
6408    }
6409    {
6410        halfword current = reverse.head;
6411        int step = 0;
6412        while (current) {
6413            halfword next = node_next(current);
6414            halfword subtype = node_subtype(current);
6415            if (trace > 1) {
6416                step = step + 1;
6417                tex_aux_trace_expression(reverse, level, step, 0);
6418                tex_aux_trace_expression(stack, level, step, 1);
6419            }
6420            switch (subtype) {
6421                case bit_expression_number:
6422                case bit_expression_float:
6423                case bit_expression_dimension:
6424                    tex_take_stack_entry(&stack, &reverse, current);
6425                    break;
6426                default:
6427                    {
6428                        halfword token = (halfword) expression_entry(current);
6429                        long long v = 0;
6430                        if (token == bit_expression_not) {
6431                            v =~ stack.tail ? expression_entry(stack.tail) : 0;
6432                        } else {
6433                            quarterword sa, sb;
6434                            long long va, vb;
6435                            sb = node_subtype(stack.tail);
6436                            vb = tex_pop_stack_entry(&stack);
6437                            if (stack.tail) {
6438                                sa = node_subtype(stack.tail);
6439                                va = expression_entry(stack.tail);
6440                            } else {
6441                                sa = bit_expression_number;
6442                                va = 0;
6443                            }
6444                            switch (token) {
6445                                case bit_expression_bor:
6446                                    v = va | vb;
6447                                    break;
6448                                case bit_expression_band:
6449                                    v = va & vb;
6450                                    break;
6451                                case bit_expression_bxor:
6452                                    v = va ^ vb;
6453                                    break;
6454                                case bit_expression_bset:
6455                                    v = va | ((long long) 1 << (vb - 1));
6456                                    break;
6457                                case bit_expression_bunset:
6458                                    v = va & ~ ((long long) 1 << (vb - 1));
6459                                    break;
6460                                case bit_expression_bleft:
6461                                    v = va << vb;
6462                                    break;
6463                                case bit_expression_bright:
6464                                    v = va >> vb;
6465                                    break;
6466                                case bit_expression_less:
6467                                    v = va < vb;
6468                                    break;
6469                                case bit_expression_lessequal:
6470                                    v = va <= vb;
6471                                    break;
6472                                case bit_expression_equal:
6473                                    v = va == vb;
6474                                    break;
6475                                case bit_expression_moreequal:
6476                                    v = va >= vb;
6477                                    break;
6478                                case bit_expression_more:
6479                                    v = va > vb;
6480                                    break;
6481                                case bit_expression_unequal:
6482                                    v = va != vb;
6483                                    break;
6484                                case bit_expression_add:
6485                                    v = va + vb;
6486                                    break;
6487                                case bit_expression_subtract:
6488                                    v = va - vb;
6489                                    break;
6490                                case bit_expression_multiply:
6491                                    {
6492                                        double d = (double) va * (double) vb;
6493                                        if (sa == bit_expression_float) {
6494                                            d = d / (65536 * factor);
6495                                        } else if (sb == bit_expression_float) {
6496                                            d = d / (65536 * factor);
6497                                        } else {
6498                                            d = d / factor;
6499                                        }
6500                                        if (sa == bit_expression_dimension || sb == bit_expression_dimension) {
6501                                            node_subtype(stack.tail) = bit_expression_dimension;
6502                                        }
6503                                        v = longlonground(d);
6504                                    }
6505                                    break;
6506                                case bit_expression_divide:
6507                                    if (vb) {
6508                                        double d = (double) va / (double) vb;
6509                                        if (sa == bit_expression_float) {
6510                                        // d = d / (65536 * factor);
6511                                           d = d * (65536 * factor);
6512                                        } else if (sb == bit_expression_float) {
6513                                         // d = d / (65536 * factor);
6514                                            d = d * (65536 * factor);
6515                                        } else {
6516                                            d = d * factor;
6517                                        }
6518                                        if (sa == bit_expression_dimension || sb == bit_expression_dimension) {
6519                                            node_subtype(stack.tail) = bit_expression_dimension;
6520                                        }
6521                                        v = longlonground(d);
6522                                    } else {
6523                                        goto ZERO;
6524                                    }
6525                                    break;
6526                                case bit_expression_mod:
6527                                    v =  va % vb;
6528                                    break;
6529                                case bit_expression_or:
6530                                    v = (va || vb) ? 1 : 0;
6531                                    break;
6532                                case bit_expression_and:
6533                                    v = (va && vb) ? 1 : 0; 
6534                                    break;
6535                                default:
6536                                    v = 0;
6537                                    break;
6538                            }
6539                        }
6540                        if (v < min_integer) {
6541                            v = min_integer;
6542                        } else if (v > max_integer) {
6543                            v = max_integer;
6544                        }
6545                        expression_entry(stack.tail) = v;
6546                        break;
6547                    }
6548            }
6549            current = next;
6550        }
6551    }
6552    goto DONE;
6553  ZERO:
6554    tex_handle_error(
6555        back_error_type,
6556        "I can't divide by zero",
6557        "I was expecting to see a nonzero number. Didn't."
6558    );
6559    goto DONE;
6560  UNEXPECTED:
6561    tex_handle_error(
6562        back_error_type,
6563        "Premature end of bit expression",
6564        "I was expecting to see an integer or bitwise operator. Didn't."
6565    );
6566  DONE:
6567    cur_val = scaledround(((double) expression_entry(stack.tail)) / factor);
6568    cur_val_level = level;
6569    tex_aux_dispose_stack(&stack);
6570    tex_aux_dispose_stack(&reverse);
6571    tex_aux_dispose_stack(&operators);
6572}
6573
6574int tex_scanned_expression(int level)
6575{
6576    tex_aux_scan_expression(level);
6577    return cur_val;
6578}
6579
6580/*tex
6581    We used to only scale by 1000 when we had a fraction but that is kind of fuzzy so now we always
6582    assume a fraction.
6583*/
6584
6585halfword tex_scan_scale(int optional_equal)
6586{
6587    bool negative = false;
6588    lmt_scanner_state.arithmic_error = 0;
6589    do {
6590        while (1) {
6591            tex_get_x_token();
6592            if (cur_cmd != spacer_cmd) {
6593                if (optional_equal && (cur_tok == equal_token)) {
6594                    optional_equal = 0;
6595                } else {
6596                    break;
6597                }
6598            }
6599        }
6600        if (cur_tok == minus_token) {
6601            negative = ! negative;
6602            cur_tok = plus_token;
6603        }
6604    } while (cur_tok == plus_token);
6605    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
6606        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, integer_val_level, 0, 0);
6607    } else {
6608        int has_fraction = tex_token_is_seperator(cur_tok);
6609        if (has_fraction) {
6610            cur_val = 0;
6611        } else {
6612            int cur_radix;
6613            tex_back_input(cur_tok);
6614            cur_val = tex_scan_integer(0, &cur_radix);
6615            tex_get_token();
6616            if (cur_radix == 10 && tex_token_is_seperator(cur_tok)) {
6617                has_fraction = 1;
6618            }
6619        }
6620        cur_val = cur_val * 1000;
6621        if (has_fraction) {
6622            unsigned k = 4;
6623            while (1) {
6624                tex_get_x_token();
6625                if (cur_tok < zero_token || cur_tok > nine_token) {
6626                    break;
6627                } else if (k == 1) {
6628                    /* rounding */
6629                    if (cur_tok >= five_token && cur_tok <= nine_token) {
6630                        cur_val += 1;
6631                    }
6632                    --k;
6633                } else if (k) {
6634                    cur_val = cur_val + (k == 4 ? 100 : (k == 3 ? 10 : 1)) * (cur_tok - zero_token);
6635                    --k;
6636                }
6637            }
6638        }
6639        tex_push_back(cur_tok, cur_cmd, cur_chr);
6640    }
6641    if (negative) {
6642        cur_val = -cur_val;
6643    }
6644    if (lmt_scanner_state.arithmic_error || (abs(cur_val) >= 0x40000000)) {
6645     // scan_dimension_out_of_range_error();
6646        cur_val = max_dimension;
6647        lmt_scanner_state.arithmic_error = 0;
6648    }
6649    return cur_val;
6650}
6651
6652/* todo: share with lmttokenlib.scan_float */
6653
6654# define max_posit_size 60 
6655
6656halfword tex_scan_posit(int optional_equal)
6657{
6658    int hexadecimal = 1;
6659    int exponent = 1;
6660    bool negative = false;
6661    int b = 0;
6662    char buffer[max_posit_size+4] = { 0 };
6663    do {
6664        while (1) {
6665            tex_get_x_token();
6666            if (cur_cmd != spacer_cmd) {
6667                if (optional_equal && (cur_tok == equal_token)) {
6668                    optional_equal = 0;
6669                } else {
6670                    break;
6671                }
6672            }
6673        }
6674        if (cur_tok == minus_token) {
6675            negative = ! negative;
6676            cur_tok = plus_token;
6677        }
6678    } while (cur_tok == plus_token);
6679    if (cur_cmd >= min_internal_cmd && cur_cmd <= max_internal_cmd) {
6680        cur_val = tex_aux_scan_something_internal(cur_cmd, cur_chr, posit_val_level, 0, 0);
6681    } else {
6682        if (negative) {
6683            buffer[b++] = '-';
6684        }
6685        /*tex we accept |[.,]digits| */
6686        if (hexadecimal && (cur_tok == zero_token)) {
6687            buffer[b++] = '0';
6688            tex_get_x_token();
6689            if (tex_token_is_hexadecimal(cur_tok)) {
6690                buffer[b++] = 'x';
6691                goto SCANHEXADECIMAL;
6692            } else {
6693                goto PICKUPDECIMAL;
6694            }
6695        } else {
6696            goto SCANDECIMAL;
6697        }
6698      SCANDECIMAL:
6699        if (tex_token_is_seperator(cur_tok)) {
6700            buffer[b++] = '.';
6701            while (1) {
6702                tex_get_x_token();
6703                if (tex_token_is_digit(cur_tok)) {
6704                    buffer[b++] = (unsigned char) cur_chr;
6705                } else if (exponent) {
6706                    goto DECIMALEXPONENT;
6707                } else {
6708                    tex_back_input(cur_tok);
6709                    goto DONE;
6710                }
6711                if (b >= 60) {
6712                    goto TOOBIG;
6713                }
6714            }
6715        } else {
6716            goto PICKUPDECIMAL;
6717        }
6718        while (1) {
6719            tex_get_x_token();
6720          PICKUPDECIMAL:
6721            if (tex_token_is_digit(cur_tok)) {
6722                buffer[b++] = (unsigned char) cur_chr;
6723            } else if (tex_token_is_seperator(cur_tok)) {
6724                buffer[b++] = '.';
6725                while (1) {
6726                    tex_get_x_token();
6727                    if (tex_token_is_digit(cur_tok)) {
6728                        buffer[b++] = (unsigned char) cur_chr;
6729                    } else {
6730                        tex_back_input(cur_tok);
6731                        break;
6732                    }
6733                }
6734            } else if (exponent) {
6735                goto DECIMALEXPONENT;
6736            } else {
6737                tex_back_input(cur_tok);
6738                goto DONE;
6739            }
6740            if (b >= max_posit_size) {
6741                goto TOOBIG;
6742            }
6743        }
6744      DECIMALEXPONENT:
6745        if (tex_token_is_exponent(cur_tok)) {
6746            buffer[b++] = (unsigned char) cur_chr;
6747            tex_get_x_token();
6748            if (tex_token_is_sign(cur_tok)) {
6749                buffer[b++] = (unsigned char) cur_chr;
6750            } else if (tex_token_is_digit(cur_tok)) {
6751                buffer[b++] = (unsigned char) cur_chr;
6752            }
6753            while (1) {
6754                tex_get_x_token();
6755                if (tex_token_is_digit(cur_tok)) {
6756                    buffer[b++] = (unsigned char) cur_chr;
6757                } else {
6758                    break;
6759                }
6760                if (b >= max_posit_size) {
6761                    goto TOOBIG;
6762                }
6763            }
6764        }
6765        tex_back_input(cur_tok);
6766        goto DONE;
6767      SCANHEXADECIMAL:
6768        tex_get_x_token();
6769        if (tex_token_is_seperator(cur_tok)) {
6770            buffer[b++] = '.';
6771            while (1) {
6772                tex_get_x_token();
6773                if (tex_token_is_xdigit(cur_tok)) {
6774                    buffer[b++] = (unsigned char) cur_chr;
6775                } else if (exponent) {
6776                    goto HEXADECIMALEXPONENT;
6777                } else {
6778                    tex_back_input(cur_tok);
6779                    goto DONE;
6780                }
6781                if (b >= max_posit_size) {
6782                    goto TOOBIG;
6783                }
6784            }
6785        } else {
6786            /* hm, we could avoid this pushback */
6787            tex_back_input(cur_tok);
6788            while (1) {
6789                tex_get_x_token();
6790                if (tex_token_is_xdigit(cur_tok)) {
6791                    buffer[b++] = (unsigned char) cur_chr;
6792                } else if (tex_token_is_seperator(cur_tok)) {
6793                    buffer[b++] = '.';
6794                    while (1) {
6795                        tex_get_x_token();
6796                        if (tex_token_is_xdigit(cur_tok)) {
6797                            buffer[b++] = (unsigned char) cur_chr;
6798                        } else {
6799                            tex_back_input(cur_tok);
6800                            break;
6801                        }
6802                    }
6803                } else if (exponent) {
6804                    goto HEXADECIMALEXPONENT;
6805                } else {
6806                    tex_back_input(cur_tok);
6807                    goto DONE;
6808                }
6809                if (b >= max_posit_size) {
6810                    goto TOOBIG;
6811                }
6812            }
6813        }
6814      HEXADECIMALEXPONENT:
6815        if (tex_token_is_xexponent(cur_tok)) {
6816            buffer[b++] = (unsigned char) cur_chr;
6817            tex_get_x_token();
6818            if (tex_token_is_sign(cur_tok)) {
6819                buffer[b++] = (unsigned char) cur_chr;
6820            } else if (tex_token_is_xdigit(cur_tok)) {
6821                buffer[b++] = (unsigned char) cur_chr;
6822            }
6823            while (1) {
6824                tex_get_x_token();
6825                if (tex_token_is_xdigit(cur_tok)) {
6826                    buffer[b++] = (unsigned char) cur_chr;
6827                } else {
6828                    break;
6829                }
6830                if (b >= max_posit_size) {
6831                    goto TOOBIG;
6832                }
6833            }
6834        }
6835        tex_back_input(cur_tok);
6836      DONE:
6837        if (b) { 
6838            double d = strtod(buffer, NULL);
6839            cur_val = tex_double_to_posit(d).v;
6840            return cur_val;
6841        } else { 
6842            tex_aux_missing_number_error();
6843        }
6844      TOOBIG:
6845        cur_val = tex_integer_to_posit(0).v;
6846    }
6847    return cur_val;
6848}
6849
6850int tex_scan_tex_value(halfword level, halfword *value)
6851{
6852    tex_aux_scan_expr(level);
6853    *value = cur_val;
6854    return 1;
6855}
6856
6857quarterword tex_scan_direction(int optional_equal)
6858{
6859    int i = tex_scan_integer(optional_equal, NULL);
6860    return (quarterword) checked_direction_value(i);
6861}
6862
6863halfword tex_scan_geometry(int optional_equal)
6864{
6865    int i = tex_scan_integer(optional_equal, NULL);
6866    return checked_geometry_value(i);
6867}
6868
6869halfword tex_scan_orientation(int optional_equal)
6870{
6871    halfword i = tex_scan_integer(optional_equal, NULL);
6872    return checked_orientation_value(i);
6873}
6874
6875halfword tex_scan_anchor(int optional_equal)
6876{
6877    halfword a = tex_scan_integer(optional_equal, NULL);
6878    halfword l = (a >> 16) & 0xFFFF;
6879    halfword r =  a        & 0xFFFF;
6880    return (checked_anchor_value(l) << 16) + checked_anchor_value(r);
6881}
6882
6883halfword tex_scan_anchors(int optional_equal)
6884{
6885    halfword l = tex_scan_integer(optional_equal, NULL) & 0xFFFF;
6886    halfword r = tex_scan_integer(0, NULL)              & 0xFFFF;
6887    return (checked_anchor_value(l) << 16) + checked_anchor_value(r);
6888}
6889
6890halfword tex_scan_attribute(halfword attrlist)
6891{
6892    halfword i = tex_scan_toks_register_number();
6893    halfword v = tex_scan_integer(1, NULL);
6894    if (eq_value(register_attribute_location(i)) != v) {
6895        if (attrlist) {
6896            attrlist = tex_patch_attribute_list(attrlist, i, v);
6897        } else {
6898            attrlist = tex_copy_attribute_list_set(tex_current_attribute_list(), i, v);
6899        }
6900    }
6901    return attrlist;
6902}
6903