lmttokenlib.c /size: 195 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    The tokenlib started out as an expetiment. The first version provided a rough interface to the
8    internals but could only really be used for simple introspection and limited piping back. A major
9    step up came in a second version where Taco introduced a couple of scanners. During experiments
10    in \CONTEXT\ I added some more so now we have a reasonable repertoire of creators, accessors and
11    scanners. Piping back to \LUA\ happens in the |tex| library and that one also has been enhanced
12    and accepts tokens.
13
14    In \LUAMETATEX\ much got streamlined, partially rewritten and some more got added so we're now
15    actually at the third version. In the meantime the experimental status has been revoked. Also,
16    the internals that relate to tokens in \LUAMETATEX\ have been redone so that the interface to
17    \LUA\ feels more natural.
18
19    Tokens are numbers but these can best be treated as abstractions. The number can be split in
20    components that code some properties. However, these numbers can change depending on what
21    abstraction we decide to use. As with the nodes integers make for an efficient coding but are
22    effectively just black boxes. The Lua interface below communicates via such numbers but don't
23    make your code dependent of the values. The mentioned rework of the internals now makes sure
24    that we get less funny numbers. For instance all chr codes nor occupy proper ranges and names
25    are more meaningful.
26
27*/
28
29# include "luametatex.h"
30
31/* # define TOKEN_METATABLE_INSTANCE "luatex.token" */
32
33typedef struct lua_token_package {
34    struct {
35        quarterword level; /* not used but it reflects the original */
36        quarterword how;   /* global */
37    };
38    singleword cmd;
39    singleword flag;
40    halfword   chr;
41    halfword   cs;
42} lua_token_package;
43
44/*
45
46    So, given what is said above, the \LUA\ interface no longer really is about magic numbers
47    combined from cmd and chr codes, sometimes called modes, but consistently tries to use the
48    combination instead of the composed number. The number is still there (and available) but users
49    need to keep in mind that constructing them directly is bad idea: the internals and therefore
50    cmd and chr codes can change! We start with a table that defines all the properties.
51
52    It must be noticed that the codebase is now rather different from \LUATEX. Of course we still
53    have most of the original commands but new ones have been added, experimental one have been
54    dropped, some have been combined. One criterion for grouping commands is that such a group gets
55    a unique treatment in reading a follow up, serialization, expansion, the main loop, the
56    registers and variables it refers to, etc. There is some logic behind it!
57
58    command_item lmt_command_names[] = {
59        { .id = escape_cmd, .lua = 0, .name = NULL, .kind = character_command_item, .min = 0, .max = max_character_code, .base = 0,  .fixedvalue = too_big_char },
60        ....
61    } ;
62
63    has been replaced by a dynamic allocation and later assignment.
64
65    In principle we can add some more clever token definers for instance for integers but that will
66    be done when I need it. The special data / reference commands need some checking (min, max etc.)
67
68*/
69
70# define ignore_entry -1
71# define direct_entry -2
72
73void lmt_tokenlib_initialize(void)
74{
75
76    lmt_interface.command_names = lmt_memory_malloc((register_dimension_reference_cmd + 2) * sizeof(command_item));
77
78    lmt_interface.command_names[escape_cmd]                           = (command_item) { .id = escape_cmd,                         .lua = lua_key_index(escape),                       .name = lua_key(escape),                       .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = too_big_char };
79    lmt_interface.command_names[left_brace_cmd]                       = (command_item) { .id = left_brace_cmd,                     .lua = lua_key_index(left_brace),                   .name = lua_key(left_brace),                   .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
80    lmt_interface.command_names[right_brace_cmd]                      = (command_item) { .id = right_brace_cmd,                    .lua = lua_key_index(right_brace),                  .name = lua_key(right_brace),                  .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
81    lmt_interface.command_names[math_shift_cmd]                       = (command_item) { .id = math_shift_cmd,                     .lua = lua_key_index(math_shift),                   .name = lua_key(math_shift),                   .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
82    lmt_interface.command_names[alignment_tab_cmd]                    = (command_item) { .id = alignment_tab_cmd,                  .lua = lua_key_index(alignment_tab),                .name = lua_key(alignment_tab),                .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
83    lmt_interface.command_names[end_line_cmd]                         = (command_item) { .id = end_line_cmd,                       .lua = lua_key_index(end_line),                     .name = lua_key(end_line),                     .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
84    lmt_interface.command_names[parameter_cmd]                        = (command_item) { .id = parameter_cmd,                      .lua = lua_key_index(parameter),                    .name = lua_key(parameter),                    .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
85    lmt_interface.command_names[superscript_cmd]                      = (command_item) { .id = superscript_cmd,                    .lua = lua_key_index(superscript),                  .name = lua_key(superscript),                  .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
86    lmt_interface.command_names[subscript_cmd]                        = (command_item) { .id = subscript_cmd,                      .lua = lua_key_index(subscript),                    .name = lua_key(subscript),                    .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
87    lmt_interface.command_names[ignore_cmd]                           = (command_item) { .id = ignore_cmd,                         .lua = lua_key_index(ignore),                       .name = lua_key(ignore),                       .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
88    lmt_interface.command_names[spacer_cmd]                           = (command_item) { .id = spacer_cmd,                         .lua = lua_key_index(spacer),                       .name = lua_key(spacer),                       .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
89    lmt_interface.command_names[letter_cmd]                           = (command_item) { .id = letter_cmd,                         .lua = lua_key_index(letter),                       .name = lua_key(letter),                       .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
90    lmt_interface.command_names[other_char_cmd]                       = (command_item) { .id = other_char_cmd,                     .lua = lua_key_index(other_char),                   .name = lua_key(other_char),                   .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
91    lmt_interface.command_names[active_char_cmd]                      = (command_item) { .id = active_char_cmd,                    .lua = lua_key_index(active_char),                  .name = lua_key(active_char),                  .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = too_big_char };
92    lmt_interface.command_names[comment_cmd]                          = (command_item) { .id = comment_cmd,                        .lua = lua_key_index(comment),                      .name = lua_key(comment),                      .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
93    lmt_interface.command_names[invalid_char_cmd]                     = (command_item) { .id = invalid_char_cmd,                   .lua = lua_key_index(invalid_char),                 .name = lua_key(invalid_char),                 .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
94    lmt_interface.command_names[relax_cmd]                            = (command_item) { .id = relax_cmd,                          .lua = lua_key_index(relax),                        .name = lua_key(relax),                        .kind = regular_command_item,   .min = 0,                         .max = last_relax_code,              .base = 0,                       .fixedvalue = 0            };
95    lmt_interface.command_names[end_template_cmd]                     = (command_item) { .id = end_template_cmd,                   .lua = lua_key_index(alignment),                    .name = lua_key(alignment),                    .kind = regular_command_item,   .min = 0,                         .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
96    lmt_interface.command_names[alignment_cmd]                        = (command_item) { .id = alignment_cmd,                      .lua = lua_key_index(end_template),                 .name = lua_key(end_template),                 .kind = regular_command_item,   .min = 0,                         .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
97    lmt_interface.command_names[match_cmd]                            = (command_item) { .id = match_cmd,                          .lua = lua_key_index(match),                        .name = lua_key(match),                        .kind = regular_command_item,   .min = 0,                         .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
98    lmt_interface.command_names[end_match_cmd]                        = (command_item) { .id = end_match_cmd,                      .lua = lua_key_index(end_match),                    .name = lua_key(end_match),                    .kind = regular_command_item,   .min = 0,                         .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
99    lmt_interface.command_names[parameter_reference_cmd]              = (command_item) { .id = parameter_reference_cmd,            .lua = lua_key_index(parameter_reference),          .name = lua_key(parameter_reference),          .kind = regular_command_item,   .min = 0,                         .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
100    lmt_interface.command_names[end_paragraph_cmd]                    = (command_item) { .id = end_paragraph_cmd,                  .lua = lua_key_index(end_paragraph),                .name = lua_key(end_paragraph),                .kind = regular_command_item,   .min = 0,                         .max = last_end_paragraph_code,      .base = 0,                       .fixedvalue = 0            };
101    lmt_interface.command_names[end_job_cmd]                          = (command_item) { .id = end_job_cmd,                        .lua = lua_key_index(end_job),                      .name = lua_key(end_job),                      .kind = regular_command_item,   .min = 0,                         .max = last_end_job_code,            .base = 0,                       .fixedvalue = 0            };
102    lmt_interface.command_names[delimiter_number_cmd]                 = (command_item) { .id = delimiter_number_cmd,               .lua = lua_key_index(delimiter_number),             .name = lua_key(delimiter_number),             .kind = regular_command_item,   .min = 0,                         .max = last_math_delimiter_code,     .base = 0,                       .fixedvalue = 0            };
103    lmt_interface.command_names[char_number_cmd]                      = (command_item) { .id = char_number_cmd,                    .lua = lua_key_index(char_number),                  .name = lua_key(char_number),                  .kind = regular_command_item,   .min = 0,                         .max = last_char_number_code,        .base = 0,                       .fixedvalue = 0            };
104    lmt_interface.command_names[math_char_number_cmd]                 = (command_item) { .id = math_char_number_cmd,               .lua = lua_key_index(math_char_number),             .name = lua_key(math_char_number),             .kind = regular_command_item,   .min = 0,                         .max = last_math_char_number_code,   .base = 0,                       .fixedvalue = 0            };
105    lmt_interface.command_names[mark_cmd]                             = (command_item) { .id = mark_cmd,                           .lua = lua_key_index(mark),                         .name = lua_key(mark),                         .kind = regular_command_item,   .min = 0,                         .max = last_set_mark_code,           .base = 0,                       .fixedvalue = 0            };
106    lmt_interface.command_names[node_cmd]                             = (command_item) { .id = node_cmd,                           .lua = lua_key_index(node),                         .name = lua_key(node),                         .kind = node_command_item,      .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
107    lmt_interface.command_names[xray_cmd]                             = (command_item) { .id = xray_cmd,                           .lua = lua_key_index(xray),                         .name = lua_key(xray),                         .kind = regular_command_item,   .min = 0,                         .max = last_xray_code,               .base = 0,                       .fixedvalue = 0            };
108    lmt_interface.command_names[make_box_cmd]                         = (command_item) { .id = make_box_cmd,                       .lua = lua_key_index(make_box),                     .name = lua_key(make_box),                     .kind = regular_command_item,   .min = 0,                         .max = last_nu_box_code,             .base = 0,                       .fixedvalue = 0            };
109    lmt_interface.command_names[hmove_cmd]                            = (command_item) { .id = hmove_cmd,                          .lua = lua_key_index(hmove),                        .name = lua_key(hmove),                        .kind = regular_command_item,   .min = 0,                         .max = last_move_code,               .base = 0,                       .fixedvalue = 0            };
110    lmt_interface.command_names[vmove_cmd]                            = (command_item) { .id = vmove_cmd,                          .lua = lua_key_index(vmove),                        .name = lua_key(vmove),                        .kind = regular_command_item,   .min = 0,                         .max = last_move_code,               .base = 0,                       .fixedvalue = 0            };
111    lmt_interface.command_names[un_hbox_cmd]                          = (command_item) { .id = un_hbox_cmd,                        .lua = lua_key_index(un_hbox),                      .name = lua_key(un_hbox),                      .kind = regular_command_item,   .min = 0,                         .max = last_un_box_code,             .base = 0,                       .fixedvalue = 0            };
112    lmt_interface.command_names[un_vbox_cmd]                          = (command_item) { .id = un_vbox_cmd,                        .lua = lua_key_index(un_vbox),                      .name = lua_key(un_vbox),                      .kind = regular_command_item,   .min = 0,                         .max = last_un_box_code,             .base = 0,                       .fixedvalue = 0            };
113    lmt_interface.command_names[remove_item_cmd]                      = (command_item) { .id = remove_item_cmd,                    .lua = lua_key_index(remove_item),                  .name = lua_key(remove_item),                  .kind = regular_command_item,   .min = 0,                         .max = last_remove_item_code,        .base = 0,                       .fixedvalue = 0            };
114    lmt_interface.command_names[hskip_cmd]                            = (command_item) { .id = hskip_cmd,                          .lua = lua_key_index(hskip),                        .name = lua_key(hskip),                        .kind = regular_command_item,   .min = first_skip_code,           .max = last_skip_code,               .base = 0,                       .fixedvalue = 0            };
115    lmt_interface.command_names[vskip_cmd]                            = (command_item) { .id = vskip_cmd,                          .lua = lua_key_index(vskip),                        .name = lua_key(vskip),                        .kind = regular_command_item,   .min = first_skip_code,           .max = last_skip_code,               .base = 0,                       .fixedvalue = 0            };
116    lmt_interface.command_names[mskip_cmd]                            = (command_item) { .id = mskip_cmd,                          .lua = lua_key_index(mskip),                        .name = lua_key(mskip),                        .kind = regular_command_item,   .min = 0,                         .max = last_mskip_code,              .base = 0,                       .fixedvalue = 0            };
117    lmt_interface.command_names[kern_cmd]                             = (command_item) { .id = kern_cmd,                           .lua = lua_key_index(kern),                         .name = lua_key(kern),                         .kind = regular_command_item,   .min = 0,                         .max = last_kern_code,               .base = 0,                       .fixedvalue = 0            };
118    lmt_interface.command_names[mkern_cmd]                            = (command_item) { .id = mkern_cmd,                          .lua = lua_key_index(mkern),                        .name = lua_key(mkern),                        .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
119    lmt_interface.command_names[leader_cmd]                           = (command_item) { .id = leader_cmd,                         .lua = lua_key_index(leader),                       .name = lua_key(leader),                       .kind = regular_command_item,   .min = first_leader_code,         .max = last_leader_code,             .base = 0,                       .fixedvalue = 0            };
120    lmt_interface.command_names[legacy_cmd]                           = (command_item) { .id = legacy_cmd,                         .lua = lua_key_index(legacy),                       .name = lua_key(legacy),                       .kind = regular_command_item,   .min = first_legacy_code,         .max = last_legacy_code ,            .base = 0,                       .fixedvalue = 0            };
121    lmt_interface.command_names[local_box_cmd]                        = (command_item) { .id = local_box_cmd,                      .lua = lua_key_index(local_box),                    .name = lua_key(local_box),                    .kind = regular_command_item,   .min = first_local_box_code,      .max = last_local_box_code,          .base = 0,                       .fixedvalue = 0            };
122    lmt_interface.command_names[halign_cmd]                           = (command_item) { .id = halign_cmd,                         .lua = lua_key_index(halign),                       .name = lua_key(halign),                       .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
123    lmt_interface.command_names[valign_cmd]                           = (command_item) { .id = valign_cmd,                         .lua = lua_key_index(valign),                       .name = lua_key(valign),                       .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
124    lmt_interface.command_names[vrule_cmd]                            = (command_item) { .id = vrule_cmd,                          .lua = lua_key_index(vrule),                        .name = lua_key(vrule),                        .kind = regular_command_item,   .min = first_rule_code,           .max = last_rule_code,               .base = 0,                       .fixedvalue = 0            };
125    lmt_interface.command_names[hrule_cmd]                            = (command_item) { .id = hrule_cmd,                          .lua = lua_key_index(hrule),                        .name = lua_key(hrule),                        .kind = regular_command_item,   .min = first_rule_code,           .max = last_rule_code,               .base = 0,                       .fixedvalue = 0            };
126    lmt_interface.command_names[insert_cmd]                           = (command_item) { .id = insert_cmd,                         .lua = lua_key_index(insert),                       .name = lua_key(insert),                       .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
127    lmt_interface.command_names[vadjust_cmd]                          = (command_item) { .id = vadjust_cmd,                        .lua = lua_key_index(vadjust),                      .name = lua_key(vadjust),                      .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
128    lmt_interface.command_names[ignore_something_cmd]                 = (command_item) { .id = ignore_something_cmd,               .lua = lua_key_index(ignore_something),             .name = lua_key(ignore_something),             .kind = regular_command_item,   .min = 0,                         .max = last_ignore_something_code,   .base = 0,                       .fixedvalue = 0            };
129    lmt_interface.command_names[after_something_cmd]                  = (command_item) { .id = after_something_cmd,                .lua = lua_key_index(after_something),              .name = lua_key(after_something),              .kind = regular_command_item,   .min = 0,                         .max = last_after_something_code,    .base = 0,                       .fixedvalue = 0            };
130    lmt_interface.command_names[penalty_cmd]                          = (command_item) { .id = penalty_cmd,                        .lua = lua_key_index(penalty),                      .name = lua_key(penalty),                      .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
131    lmt_interface.command_names[begin_paragraph_cmd]                  = (command_item) { .id = begin_paragraph_cmd,                .lua = lua_key_index(begin_paragraph),              .name = lua_key(begin_paragraph),              .kind = regular_command_item,   .min = 0,                         .max = last_begin_paragraph_code,    .base = 0,                       .fixedvalue = 0            };
132    lmt_interface.command_names[italic_correction_cmd]                = (command_item) { .id = italic_correction_cmd,              .lua = lua_key_index(italic_correction),            .name = lua_key(italic_correction),            .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
133    lmt_interface.command_names[accent_cmd]                           = (command_item) { .id = accent_cmd,                         .lua = lua_key_index(accent),                       .name = lua_key(accent),                       .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
134    lmt_interface.command_names[math_accent_cmd]                      = (command_item) { .id = math_accent_cmd,                    .lua = lua_key_index(math_accent),                  .name = lua_key(math_accent),                  .kind = regular_command_item,   .min = 0,                         .max = last_math_accent_code,        .base = 0,                       .fixedvalue = 0            };
135    lmt_interface.command_names[discretionary_cmd]                    = (command_item) { .id = discretionary_cmd,                  .lua = lua_key_index(discretionary),                .name = lua_key(discretionary),                .kind = regular_command_item,   .min = 0,                         .max = last_discretionary_code,      .base = 0,                       .fixedvalue = 0            };
136    lmt_interface.command_names[equation_number_cmd]                  = (command_item) { .id = equation_number_cmd,                .lua = lua_key_index(equation_number),              .name = lua_key(equation_number),              .kind = regular_command_item,   .min = first_location_code,       .max = last_location_code,           .base = 0,                       .fixedvalue = 0            }; /* maybe dedicated codes */
137    lmt_interface.command_names[math_fence_cmd]                       = (command_item) { .id = math_fence_cmd,                     .lua = lua_key_index(math_fence),                   .name = lua_key(math_fence),                   .kind = regular_command_item,   .min = first_fence_code,          .max = last_fence_code,              .base = 0,                       .fixedvalue = 0            };
138    lmt_interface.command_names[math_component_cmd]                   = (command_item) { .id = math_component_cmd,                 .lua = lua_key_index(math_component),               .name = lua_key(math_component),               .kind = regular_command_item,   .min = first_math_component_type, .max = last_math_component_type,     .base = 0,                       .fixedvalue = 0            }; /* a bit too tolerant */
139    lmt_interface.command_names[math_modifier_cmd]                    = (command_item) { .id = math_modifier_cmd,                  .lua = lua_key_index(math_modifier),                .name = lua_key(math_modifier),                .kind = regular_command_item,   .min = first_math_modifier_code,  .max = last_math_modifier_code,      .base = 0,                       .fixedvalue = 0            }; /* a bit too tolerant */
140    lmt_interface.command_names[math_fraction_cmd]                    = (command_item) { .id = math_fraction_cmd,                  .lua = lua_key_index(math_fraction),                .name = lua_key(math_fraction),                .kind = regular_command_item,   .min = 0,                         .max = last_math_fraction_code,      .base = 0,                       .fixedvalue = 0            }; /* partial */
141    lmt_interface.command_names[math_style_cmd]                       = (command_item) { .id = math_style_cmd,                     .lua = lua_key_index(math_style),                   .name = lua_key(math_style),                   .kind = regular_command_item,   .min = 0,                         .max = last_math_style,              .base = 0,                       .fixedvalue = 0            }; /* partial */
142    lmt_interface.command_names[math_choice_cmd]                      = (command_item) { .id = math_choice_cmd,                    .lua = lua_key_index(math_choice),                  .name = lua_key(math_choice),                  .kind = regular_command_item,   .min = 0,                         .max = last_math_choice_code,        .base = 0,                       .fixedvalue = 0            };
143    lmt_interface.command_names[vcenter_cmd]                          = (command_item) { .id = vcenter_cmd,                        .lua = lua_key_index(vcenter),                      .name = lua_key(vcenter),                      .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
144    lmt_interface.command_names[case_shift_cmd]                       = (command_item) { .id = case_shift_cmd,                     .lua = lua_key_index(case_shift),                   .name = lua_key(case_shift),                   .kind = regular_command_item,   .min = 0,                         .max = last_case_shift_code,         .base = 0,                       .fixedvalue = 0            };
145    lmt_interface.command_names[message_cmd]                          = (command_item) { .id = message_cmd,                        .lua = lua_key_index(message),                      .name = lua_key(message),                      .kind = regular_command_item,   .min = 0,                         .max = last_message_code,            .base = 0,                       .fixedvalue = 0            };
146    lmt_interface.command_names[catcode_table_cmd]                    = (command_item) { .id = catcode_table_cmd,                  .lua = lua_key_index(catcode_table),                .name = lua_key(catcode_table),                .kind = regular_command_item,   .min = 0,                         .max = last_catcode_table_code,      .base = 0,                       .fixedvalue = 0            };
147    lmt_interface.command_names[end_local_cmd]                        = (command_item) { .id = end_local_cmd,                      .lua = lua_key_index(end_local),                    .name = lua_key(end_local),                    .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
148    lmt_interface.command_names[lua_function_call_cmd]                = (command_item) { .id = lua_function_call_cmd,              .lua = lua_key_index(lua_function_call),            .name = lua_key(lua_function_call),            .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
149    lmt_interface.command_names[lua_protected_call_cmd]               = (command_item) { .id = lua_protected_call_cmd,             .lua = lua_key_index(lua_protected_call),           .name = lua_key(lua_protected_call),           .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
150    lmt_interface.command_names[lua_semi_protected_call_cmd]          = (command_item) { .id = lua_semi_protected_call_cmd,        .lua = lua_key_index(lua_semiprotected_call),       .name = lua_key(lua_semiprotected_call),       .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
151    lmt_interface.command_names[begin_group_cmd]                      = (command_item) { .id = begin_group_cmd,                    .lua = lua_key_index(begin_group),                  .name = lua_key(begin_group),                  .kind = regular_command_item,   .min = 0,                         .max = last_begin_group_code,        .base = 0,                       .fixedvalue = 0            };
152    lmt_interface.command_names[end_group_cmd]                        = (command_item) { .id = end_group_cmd,                      .lua = lua_key_index(end_group),                    .name = lua_key(end_group),                    .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
153    lmt_interface.command_names[explicit_space_cmd]                   = (command_item) { .id = explicit_space_cmd,                 .lua = lua_key_index(explicit_space),               .name = lua_key(explicit_space),               .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
154    lmt_interface.command_names[boundary_cmd]                         = (command_item) { .id = boundary_cmd,                       .lua = lua_key_index(boundary),                     .name = lua_key(boundary),                     .kind = regular_command_item,   .min = 0,                         .max = last_boundary_code,           .base = 0,                       .fixedvalue = 0            };
155    lmt_interface.command_names[math_radical_cmd]                     = (command_item) { .id = math_radical_cmd,                   .lua = lua_key_index(math_radical),                 .name = lua_key(math_radical),                 .kind = regular_command_item,   .min = 0,                         .max = last_radical_code,            .base = 0,                       .fixedvalue = 0            };
156    lmt_interface.command_names[math_script_cmd]                      = (command_item) { .id = math_script_cmd,                    .lua = lua_key_index(math_script),                  .name = lua_key(math_script),                  .kind = regular_command_item,   .min = 0,                         .max = last_math_script_code,        .base = 0,                       .fixedvalue = 0            };
157    lmt_interface.command_names[math_shift_cs_cmd]                    = (command_item) { .id = math_shift_cs_cmd,                  .lua = lua_key_index(math_shift_cs),                .name = lua_key(math_shift_cs),                .kind = regular_command_item,   .min = 0,                         .max = last_math_shift_cs_code,      .base = 0,                       .fixedvalue = 0            }; /* a bit too tolerant */
158    lmt_interface.command_names[end_cs_name_cmd]                      = (command_item) { .id = end_cs_name_cmd,                    .lua = lua_key_index(end_cs_name),                  .name = lua_key(end_cs_name),                  .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
159    lmt_interface.command_names[char_given_cmd]                       = (command_item) { .id = char_given_cmd,                     .lua = lua_key_index(char_given),                   .name = lua_key(char_given),                   .kind = character_command_item, .min = 0,                         .max = max_character_code,           .base = 0,                       .fixedvalue = 0            };
160    lmt_interface.command_names[some_item_cmd]                        = (command_item) { .id = some_item_cmd,                      .lua = lua_key_index(some_item),                    .name = lua_key(some_item),                    .kind = regular_command_item,   .min = 0,                         .max = last_some_item_code,          .base = 0,                       .fixedvalue = 0            };
161    lmt_interface.command_names[internal_toks_cmd]                    = (command_item) { .id = internal_toks_cmd,                  .lua = lua_key_index(internal_toks),                .name = lua_key(internal_toks),                .kind = internal_command_item,  .min = first_toks_code,           .max = last_toks_code,               .base = internal_toks_base,      .fixedvalue = 0            };
162    lmt_interface.command_names[register_toks_cmd]                    = (command_item) { .id = register_toks_cmd,                  .lua = lua_key_index(register_toks),                .name = lua_key(register_toks),                .kind = register_command_item,  .min = 0,                         .max = biggest_reg,                  .base = register_toks_base,      .fixedvalue = 0            };
163    lmt_interface.command_names[internal_integer_cmd]                 = (command_item) { .id = internal_integer_cmd,               .lua = lua_key_index(internal_integer),             .name = lua_key(internal_integer),             .kind = internal_command_item,  .min = first_integer_code,        .max = last_integer_code,            .base = internal_integer_base,   .fixedvalue = 0            };
164    lmt_interface.command_names[register_integer_cmd]                 = (command_item) { .id = register_integer_cmd,               .lua = lua_key_index(register_integer),             .name = lua_key(register_integer),             .kind = register_command_item,  .min = 0,                         .max = max_integer_register_index,   .base = register_integer_base,   .fixedvalue = 0            };
165    lmt_interface.command_names[internal_attribute_cmd]               = (command_item) { .id = internal_attribute_cmd,             .lua = lua_key_index(internal_attribute),           .name = lua_key(internal_attribute),           .kind = unused_command_item,    .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
166    lmt_interface.command_names[register_attribute_cmd]               = (command_item) { .id = register_attribute_cmd,             .lua = lua_key_index(register_attribute),           .name = lua_key(register_attribute),           .kind = register_command_item,  .min = 0,                         .max = max_attribute_register_index, .base = register_attribute_base, .fixedvalue = 0            };
167    lmt_interface.command_names[internal_posit_cmd]                   = (command_item) { .id = internal_posit_cmd,                 .lua = lua_key_index(internal_posit),               .name = lua_key(internal_posit),               .kind = unused_command_item,    .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
168    lmt_interface.command_names[register_posit_cmd]                   = (command_item) { .id = register_posit_cmd,                 .lua = lua_key_index(register_posit),               .name = lua_key(register_posit),               .kind = register_command_item,  .min = 0,                         .max = max_posit_register_index,     .base = register_posit_base,     .fixedvalue = 0            };
169    lmt_interface.command_names[internal_dimension_cmd]               = (command_item) { .id = internal_dimension_cmd,             .lua = lua_key_index(internal_dimension),           .name = lua_key(internal_dimension),           .kind = internal_command_item,  .min = first_dimension_code,      .max = last_dimension_code,          .base = internal_dimension_base, .fixedvalue = 0            };
170    lmt_interface.command_names[register_dimension_cmd]               = (command_item) { .id = register_dimension_cmd,             .lua = lua_key_index(register_dimension),           .name = lua_key(register_dimension),           .kind = register_command_item,  .min = 0,                         .max = max_dimension_register_index, .base = register_dimension_base, .fixedvalue = 0            };
171    lmt_interface.command_names[internal_glue_cmd]                    = (command_item) { .id = internal_glue_cmd,                  .lua = lua_key_index(internal_glue),                .name = lua_key(internal_glue),                .kind = internal_command_item,  .min = first_glue_code,           .max = last_glue_code,               .base = internal_glue_base,      .fixedvalue = 0            };
172    lmt_interface.command_names[register_glue_cmd]                    = (command_item) { .id = register_glue_cmd,                  .lua = lua_key_index(register_glue),                .name = lua_key(register_glue),                .kind = register_command_item,  .min = 0,                         .max = max_glue_register_index,      .base = register_glue_base,      .fixedvalue = 0            };
173    lmt_interface.command_names[internal_muglue_cmd]                  = (command_item) { .id = internal_muglue_cmd,                .lua = lua_key_index(internal_muglue),              .name = lua_key(internal_muglue),              .kind = internal_command_item,  .min = first_muglue_code,         .max = last_muglue_code,             .base = internal_muglue_base,    .fixedvalue = 0            };
174    lmt_interface.command_names[register_muglue_cmd]                  = (command_item) { .id = register_muglue_cmd,                .lua = lua_key_index(register_muglue),              .name = lua_key(register_muglue),              .kind = register_command_item,  .min = 0,                         .max = max_muglue_register_index,    .base = register_muglue_base,    .fixedvalue = 0            };
175    lmt_interface.command_names[lua_value_cmd]                        = (command_item) { .id = lua_value_cmd,                      .lua = lua_key_index(lua_value),                    .name = lua_key(lua_value),                    .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
176    lmt_interface.command_names[iterator_value_cmd]                   = (command_item) { .id = iterator_value_cmd,                 .lua = lua_key_index(iterator_value),               .name = lua_key(iterator_value),               .kind = data_command_item,      .min = min_iterator_value,        .max = max_iterator_value,           .base = 0,                       .fixedvalue = 0            };
177    lmt_interface.command_names[font_property_cmd]                    = (command_item) { .id = font_property_cmd,                  .lua = lua_key_index(font_property),                .name = lua_key(font_property),                .kind = regular_command_item,   .min = 0,                         .max = last_font_property_code,      .base = 0,                       .fixedvalue = 0            };
178    lmt_interface.command_names[auxiliary_cmd]                        = (command_item) { .id = auxiliary_cmd,                      .lua = lua_key_index(auxiliary),                    .name = lua_key(auxiliary),                    .kind = regular_command_item,   .min = 0,                         .max = last_auxiliary_code,          .base = 0,                       .fixedvalue = 0            };
179    lmt_interface.command_names[page_property_cmd]                    = (command_item) { .id = page_property_cmd,                  .lua = lua_key_index(page_property),                .name = lua_key(page_property),                .kind = regular_command_item,   .min = 0,                         .max = last_page_property_code,      .base = 0,                       .fixedvalue = 0            };
180    lmt_interface.command_names[box_property_cmd]                     = (command_item) { .id = box_property_cmd,                   .lua = lua_key_index(box_property),                 .name = lua_key(box_property),                 .kind = regular_command_item,   .min = 0,                         .max = last_box_property_code,       .base = 0,                       .fixedvalue = 0            };
181    lmt_interface.command_names[specification_cmd]                    = (command_item) { .id = specification_cmd,                  .lua = lua_key_index(specification),                .name = lua_key(specification),                .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
182    lmt_interface.command_names[define_char_code_cmd]                 = (command_item) { .id = define_char_code_cmd,               .lua = lua_key_index(define_char_code),             .name = lua_key(define_char_code),             .kind = regular_command_item,   .min = 0,                         .max = last_charcode_code,           .base = 0,                       .fixedvalue = 0            };
183    lmt_interface.command_names[define_family_cmd]                    = (command_item) { .id = define_family_cmd,                  .lua = lua_key_index(define_family),                .name = lua_key(define_family),                .kind = regular_command_item,   .min = 0,                         .max = last_math_size,               .base = 0,                       .fixedvalue = 0            };
184    lmt_interface.command_names[math_parameter_cmd]                   = (command_item) { .id = math_parameter_cmd,                 .lua = lua_key_index(math_parameter),               .name = lua_key(math_parameter),               .kind = regular_command_item,   .min = 0,                         .max = last_math_parameter,          .base = 0,                       .fixedvalue = 0            };
185 // lmt_interface.command_names[set_font_cmd]                         = (command_item) { .id = set_font_cmd,                       .lua = lua_key_index(set_font),                     .name = lua_key(set_font),                     .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
186    lmt_interface.command_names[set_font_cmd]                         = (command_item) { .id = set_font_cmd,                       .lua = lua_key_index(set_font),                     .name = lua_key(set_font),                     .kind = data_command_item,      .min = 0,                         .max = max_font_size,                .base = 0,                       .fixedvalue = 0            };
187    lmt_interface.command_names[define_font_cmd]                      = (command_item) { .id = define_font_cmd,                    .lua = lua_key_index(define_font),                  .name = lua_key(define_font),                  .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
188    lmt_interface.command_names[integer_cmd]                          = (command_item) { .id = integer_cmd,                        .lua = lua_key_index(integer),                      .name = lua_key(integer),                      .kind = data_command_item,      .min = min_integer,               .max = max_integer,                  .base = direct_entry,            .fixedvalue = 0            };
189    lmt_interface.command_names[index_cmd]                            = (command_item) { .id = index_cmd,                          .lua = lua_key_index(index),                        .name = lua_key(index),                        .kind = data_command_item,      .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
190    lmt_interface.command_names[posit_cmd]                            = (command_item) { .id = posit_cmd,                          .lua = lua_key_index(posit),                        .name = lua_key(posit),                        .kind = data_command_item,      .min = min_posit,                 .max = max_posit,                    .base = direct_entry,            .fixedvalue = 0            };
191    lmt_interface.command_names[dimension_cmd]                        = (command_item) { .id = dimension_cmd,                      .lua = lua_key_index(dimension),                    .name = lua_key(dimension),                    .kind = data_command_item,      .min = min_dimension,             .max = max_dimension,                .base = direct_entry,            .fixedvalue = 0            };
192    lmt_interface.command_names[gluespec_cmd]                         = (command_item) { .id = gluespec_cmd,                       .lua = lua_key_index(gluespec),                     .name = lua_key(gluespec),                     .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
193    lmt_interface.command_names[mugluespec_cmd]                       = (command_item) { .id = mugluespec_cmd,                     .lua = lua_key_index(mugluespec),                   .name = lua_key(mugluespec),                   .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
194    lmt_interface.command_names[mathspec_cmd]                         = (command_item) { .id = mathspec_cmd,                       .lua = lua_key_index(mathspec),                     .name = lua_key(fontspec),                     .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
195    lmt_interface.command_names[fontspec_cmd]                         = (command_item) { .id = fontspec_cmd,                       .lua = lua_key_index(fontspec),                     .name = lua_key(fontspec),                     .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
196    lmt_interface.command_names[register_cmd]                         = (command_item) { .id = register_cmd,                       .lua = lua_key_index(register),                     .name = lua_key(register),                     .kind = regular_command_item,   .min = first_value_level,         .max = last_value_level,             .base = 0,                       .fixedvalue = 0            };
197    lmt_interface.command_names[combine_toks_cmd]                     = (command_item) { .id = combine_toks_cmd,                   .lua = lua_key_index(combine_toks),                 .name = lua_key(combine_toks),                 .kind = regular_command_item,   .min = 0,                         .max = last_combine_toks_code,       .base = 0,                       .fixedvalue = 0            };
198    lmt_interface.command_names[arithmic_cmd]                         = (command_item) { .id = arithmic_cmd,                       .lua = lua_key_index(arithmic),                     .name = lua_key(arithmic),                     .kind = regular_command_item,   .min = 0,                         .max = last_arithmic_code,           .base = 0,                       .fixedvalue = 0            };
199    lmt_interface.command_names[prefix_cmd]                           = (command_item) { .id = prefix_cmd,                         .lua = lua_key_index(prefix),                       .name = lua_key(prefix),                       .kind = regular_command_item,   .min = 0,                         .max = last_prefix_code,             .base = 0,                       .fixedvalue = 0            };
200    lmt_interface.command_names[let_cmd]                              = (command_item) { .id = let_cmd,                            .lua = lua_key_index(let),                          .name = lua_key(let),                          .kind = regular_command_item,   .min = 0,                         .max = last_let_code,                .base = 0,                       .fixedvalue = 0            };
201    lmt_interface.command_names[shorthand_def_cmd]                    = (command_item) { .id = shorthand_def_cmd,                  .lua = lua_key_index(shorthand_def),                .name = lua_key(shorthand_def),                .kind = regular_command_item,   .min = 0,                         .max = last_shorthand_def_code,      .base = 0,                       .fixedvalue = 0            };
202    lmt_interface.command_names[association_cmd]                      = (command_item) { .id = association_cmd,                    .lua = lua_key_index(association),                  .name = lua_key(association),                  .kind = regular_command_item,   .min = 0,                         .max = last_association_code,        .base = 0,                       .fixedvalue = 0            };
203    lmt_interface.command_names[def_cmd]                              = (command_item) { .id = def_cmd,                            .lua = lua_key_index(def),                          .name = lua_key(def),                          .kind = regular_command_item,   .min = 0,                         .max = last_def_code,                .base = 0,                       .fixedvalue = 0            };
204    lmt_interface.command_names[set_box_cmd]                          = (command_item) { .id = set_box_cmd,                        .lua = lua_key_index(set_box),                      .name = lua_key(set_box),                      .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
205    lmt_interface.command_names[hyphenation_cmd]                      = (command_item) { .id = hyphenation_cmd,                    .lua = lua_key_index(hyphenation),                  .name = lua_key(hyphenation),                  .kind = regular_command_item,   .min = 0,                         .max = last_hyphenation_code,        .base = 0,                       .fixedvalue = 0            };
206    lmt_interface.command_names[interaction_cmd]                      = (command_item) { .id = interaction_cmd,                    .lua = lua_key_index(interaction),                  .name = lua_key(interaction),                  .kind = regular_command_item,   .min = 0,                         .max = last_interaction_level,       .base = 0,                       .fixedvalue = 0            };
207    lmt_interface.command_names[undefined_cs_cmd]                     = (command_item) { .id = undefined_cs_cmd,                   .lua = lua_key_index(undefined_cs),                 .name = lua_key(undefined_cs),                 .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
208    lmt_interface.command_names[expand_after_cmd]                     = (command_item) { .id = expand_after_cmd,                   .lua = lua_key_index(expand_after),                 .name = lua_key(expand_after),                 .kind = regular_command_item,   .min = 0,                         .max = last_expand_after_code,       .base = 0,                       .fixedvalue = 0            };
209    lmt_interface.command_names[no_expand_cmd]                        = (command_item) { .id = no_expand_cmd,                      .lua = lua_key_index(no_expand),                    .name = lua_key(no_expand),                    .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
210    lmt_interface.command_names[input_cmd]                            = (command_item) { .id = input_cmd,                          .lua = lua_key_index(input),                        .name = lua_key(input),                        .kind = regular_command_item,   .min = 0,                         .max = last_input_code,              .base = 0,                       .fixedvalue = 0            };
211    lmt_interface.command_names[lua_call_cmd]                         = (command_item) { .id = lua_call_cmd,                       .lua = lua_key_index(lua_call),                     .name = lua_key(lua_call),                     .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
212    lmt_interface.command_names[lua_local_call_cmd]                   = (command_item) { .id = lua_local_call_cmd,                 .lua = lua_key_index(lua_local_call),               .name = lua_key(lua_local_call),               .kind = reference_command_item, .min = 0,                         .max = max_function_reference,       .base = 0,                       .fixedvalue = 0            };
213    lmt_interface.command_names[begin_local_cmd]                      = (command_item) { .id = begin_local_cmd,                    .lua = lua_key_index(begin_local),                  .name = lua_key(begin_local),                  .kind = regular_command_item,   .min = 0,                         .max = 0,                            .base = 0,                       .fixedvalue = 0            };
214    lmt_interface.command_names[if_test_cmd]                          = (command_item) { .id = if_test_cmd,                        .lua = lua_key_index(if_test),                      .name = lua_key(if_test),                      .kind = regular_command_item,   .min = first_if_test_code,        .max = last_if_test_code,            .base = 0,                       .fixedvalue = 0            };
215    lmt_interface.command_names[cs_name_cmd]                          = (command_item) { .id = cs_name_cmd,                        .lua = lua_key_index(cs_name),                      .name = lua_key(cs_name),                      .kind = regular_command_item,   .min = 0,                         .max = last_cs_name_code,            .base = 0,                       .fixedvalue = 0            };
216    lmt_interface.command_names[convert_cmd]                          = (command_item) { .id = convert_cmd,                        .lua = lua_key_index(convert),                      .name = lua_key(convert),                      .kind = regular_command_item,   .min = 0,                         .max = last_convert_code,            .base = 0,                       .fixedvalue = 0            };
217    lmt_interface.command_names[the_cmd]                              = (command_item) { .id = the_cmd,                            .lua = lua_key_index(the),                          .name = lua_key(the),                          .kind = regular_command_item,   .min = 0,                         .max = last_the_code,                .base = 0,                       .fixedvalue = 0            };
218    lmt_interface.command_names[get_mark_cmd]                         = (command_item) { .id = get_mark_cmd,                       .lua = lua_key_index(get_mark),                     .name = lua_key(get_mark),                     .kind = regular_command_item,   .min = 0,                         .max = last_get_mark_code,           .base = 0,                       .fixedvalue = 0            };
219    lmt_interface.command_names[call_cmd]                             = (command_item) { .id = call_cmd,                           .lua = lua_key_index(call),                         .name = lua_key(call),                         .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
220    lmt_interface.command_names[protected_call_cmd]                   = (command_item) { .id = protected_call_cmd,                 .lua = lua_key_index(protected_call),               .name = lua_key(protected_call),               .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
221    lmt_interface.command_names[semi_protected_call_cmd]              = (command_item) { .id = semi_protected_call_cmd,            .lua = lua_key_index(semi_protected_call),          .name = lua_key(semi_protected_call),          .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
222    lmt_interface.command_names[constant_call_cmd]                    = (command_item) { .id = constant_call_cmd,                  .lua = lua_key_index(constant_call),                .name = lua_key(constant_call),                .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
223    lmt_interface.command_names[tolerant_call_cmd]                    = (command_item) { .id = tolerant_call_cmd,                  .lua = lua_key_index(tolerant_call),                .name = lua_key(tolerant_call),                .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
224    lmt_interface.command_names[tolerant_protected_call_cmd]          = (command_item) { .id = tolerant_protected_call_cmd,        .lua = lua_key_index(tolerant_protected_call),      .name = lua_key(tolerant_protected_call),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
225    lmt_interface.command_names[tolerant_semi_protected_call_cmd]     = (command_item) { .id = tolerant_semi_protected_call_cmd,   .lua = lua_key_index(tolerant_semi_protected_call), .name = lua_key(tolerant_semi_protected_call), .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
226    lmt_interface.command_names[deep_frozen_end_template_cmd]         = (command_item) { .id = deep_frozen_end_template_cmd,       .lua = lua_key_index(deep_frozen_end_template),     .name = lua_key(deep_frozen_end_template),     .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
227    lmt_interface.command_names[deep_frozen_dont_expand_cmd]          = (command_item) { .id = deep_frozen_dont_expand_cmd,        .lua = lua_key_index(deep_frozen_dont_expand),      .name = lua_key(deep_frozen_dont_expand),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
228    lmt_interface.command_names[deep_frozen_keep_constant_cmd]        = (command_item) { .id = deep_frozen_keep_constant_cmd,      .lua = lua_key_index(deep_frozen_keep_constant),    .name = lua_key(deep_frozen_keep_constant),    .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
229    lmt_interface.command_names[internal_glue_reference_cmd]          = (command_item) { .id = internal_glue_reference_cmd,        .lua = lua_key_index(internal_glue_reference),      .name = lua_key(internal_glue_reference),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
230    lmt_interface.command_names[register_glue_reference_cmd]          = (command_item) { .id = register_glue_reference_cmd,        .lua = lua_key_index(register_glue_reference),      .name = lua_key(register_glue_reference),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
231    lmt_interface.command_names[internal_muglue_reference_cmd]        = (command_item) { .id = internal_muglue_reference_cmd,      .lua = lua_key_index(internal_muglue_reference),    .name = lua_key(internal_muglue_reference),    .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
232    lmt_interface.command_names[register_muglue_reference_cmd]        = (command_item) { .id = register_muglue_reference_cmd,      .lua = lua_key_index(register_muglue_reference),    .name = lua_key(register_muglue_reference),    .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
233    lmt_interface.command_names[internal_box_reference_cmd]           = (command_item) { .id = internal_box_reference_cmd,         .lua = lua_key_index(specification_reference),      .name = lua_key(specification_reference),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
234    lmt_interface.command_names[register_box_reference_cmd]           = (command_item) { .id = register_box_reference_cmd,         .lua = lua_key_index(internal_box_reference),       .name = lua_key(internal_box_reference),       .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
235    lmt_interface.command_names[internal_toks_reference_cmd]          = (command_item) { .id = internal_toks_reference_cmd,        .lua = lua_key_index(internal_toks_reference),      .name = lua_key(register_box_reference),       .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
236    lmt_interface.command_names[register_toks_reference_cmd]          = (command_item) { .id = register_toks_reference_cmd,        .lua = lua_key_index(register_toks_reference),      .name = lua_key(register_toks_reference),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
237    lmt_interface.command_names[specification_reference_cmd]          = (command_item) { .id = specification_reference_cmd,        .lua = lua_key_index(specification_reference),      .name = lua_key(specification_reference),      .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
238    lmt_interface.command_names[unit_reference_cmd]                   = (command_item) { .id = unit_reference_cmd,                 .lua = lua_key_index(unit_reference),               .name = lua_key(unit_reference),               .kind = token_command_item,     .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
239    lmt_interface.command_names[internal_integer_reference_cmd]       = (command_item) { .id = internal_integer_reference_cmd,     .lua = lua_key_index(internal_integer_reference),   .name = lua_key(internal_integer_reference),   .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
240    lmt_interface.command_names[register_integer_reference_cmd]       = (command_item) { .id = register_integer_reference_cmd,     .lua = lua_key_index(register_integer_reference),   .name = lua_key(register_integer_reference),   .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
241    lmt_interface.command_names[internal_attribute_reference_cmd]     = (command_item) { .id = internal_attribute_reference_cmd,   .lua = lua_key_index(internal_attribute_reference), .name = lua_key(internal_attribute_reference), .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
242    lmt_interface.command_names[register_attribute_reference_cmd]     = (command_item) { .id = register_attribute_reference_cmd,   .lua = lua_key_index(register_attribute_reference), .name = lua_key(register_attribute_reference), .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
243    lmt_interface.command_names[internal_posit_reference_cmd]         = (command_item) { .id = internal_posit_reference_cmd,       .lua = lua_key_index(internal_posit_reference),     .name = lua_key(internal_posit_reference),     .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
244    lmt_interface.command_names[register_posit_reference_cmd]         = (command_item) { .id = register_posit_reference_cmd,       .lua = lua_key_index(register_posit_reference),     .name = lua_key(register_posit_reference),     .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
245    lmt_interface.command_names[internal_dimension_reference_cmd]     = (command_item) { .id = internal_dimension_reference_cmd,   .lua = lua_key_index(internal_dimension_reference), .name = lua_key(internal_dimension_reference), .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
246    lmt_interface.command_names[register_dimension_reference_cmd]     = (command_item) { .id = register_dimension_reference_cmd,   .lua = lua_key_index(register_dimension_reference), .name = lua_key(register_dimension_reference), .kind = regular_command_item,   .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
247    lmt_interface.command_names[register_dimension_reference_cmd + 1] = (command_item) { .id = unknown_value,                      .lua = 0,                                           .name = NULL,                                  .kind = unused_command_item,    .min = ignore_entry,              .max = ignore_entry,                 .base = ignore_entry,            .fixedvalue = 0            };
248
249    if (lmt_interface.command_names[last_cmd].id != last_cmd) {
250        tex_fatal_error("mismatch between tex and lua command name tables");
251    }
252}
253
254typedef struct saved_tex_scanner {
255    int cmd;
256    int chr;
257    int cs;
258    int tok;
259} saved_tex_scanner;
260
261inline static saved_tex_scanner tokenlib_aux_save_tex_scanner(void) {
262    return (saved_tex_scanner) {
263        .cmd = cur_cmd,
264        .chr = cur_chr,
265        .cs  = cur_cs,
266        .tok = cur_tok
267    };
268}
269
270inline static void tokenlib_aux_unsave_tex_scanner(saved_tex_scanner a)
271{
272    cur_cmd = a.cmd;
273    cur_chr = a.chr;
274    cur_cs  = a.cs;
275    cur_tok = a.tok;
276}
277
278static int tokenlib_aux_get_command_id(const char *s)
279{
280    for (int i = 0; lmt_interface.command_names[i].id != -1; i++) {
281        if (s == lmt_interface.command_names[i].name) {
282            return i;
283        }
284    }
285    return -1;
286}
287
288/*tex
289    We have some checkers that use the information from |command_names|:
290
291    \startitemize
292    \startitem the 0..64K counter, dimen, token etc registers \stopitem
293    \startitem the predefined internal quantities \stopitem
294    \stopitemize
295*/
296
297/*
298inline static int tokenlib_valid_cmd(int cmd)
299{
300    return cmd >= first_cmd && cmd <= last_cmd;
301}
302*/
303
304inline static int tokenlib_aux_valid_chr(int cmd, int chr)
305{
306    command_item item = lmt_interface.command_names[cmd];
307    if (chr > 0) {
308        switch (item.base) {
309            case ignore_entry:
310            case direct_entry:
311                break;
312            default:
313                if (chr >= item.min && chr <= item.max) {
314                    return item.base + chr;
315                }
316        }
317    } else if (chr == item.fixedvalue) {
318        return chr;
319    }
320    return 0;
321}
322
323inline static int tokenlib_aux_valid_cs(int cs)
324{
325    return (cs >= 0 && cs <= lmt_token_memory_state.tokens_data.allocated) ? cs : -1;
326}
327
328// not ok
329
330inline static int tokenlib_aux_valid_token(int cmd, int chr, int cs)
331{
332    if (cs) {
333        cs = tokenlib_aux_valid_cs(cs);
334        if (cs >= 0) {
335            return cs_token_flag + cs;
336        }
337    } if (cmd >= first_cmd && cmd <= last_cmd) {
338        chr = tokenlib_aux_valid_chr(cmd, chr);
339        if (chr >= 0) {
340            return token_val(cmd, chr);
341        }
342    }
343    return -1;
344}
345
346inline static int tokenlib_aux_to_valid_index(int cmd, int chr)
347{
348    if (cmd >= 0 && cmd <= last_cmd) {
349        command_item item = lmt_interface.command_names[cmd];
350        switch (item.kind) {
351            case unused_command_item:
352                 return 0;
353            case regular_command_item:
354            case character_command_item:
355                 return chr;
356            case register_command_item:
357            case internal_command_item:
358            case reference_command_item:
359            case data_command_item:
360                switch (item.base) {
361                    case ignore_entry:
362                        return 0;
363                    case direct_entry:
364                        break;
365                    default:
366                        chr -= item.base;
367                        break;
368                }
369                return (chr >= item.min && chr <= item.max) ? chr : item.min;
370            case token_command_item:
371            case node_command_item:
372                return item.fixedvalue;
373        }
374    }
375    return 0;
376}
377
378inline static void tokenlib_aux_make_token_table(lua_State *L, int cmd, int chr, int cs)
379{
380    lua_createtable(L, 3, 0);
381    lua_pushinteger(L, cmd);
382    lua_rawseti(L, -2, 1);
383    lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr)); /* index or value */
384    lua_rawseti(L, -2, 2);
385    lua_pushinteger(L, cs);
386    lua_rawseti(L, -2, 3);
387}
388
389/*tex
390
391    Takes a table |{ cmd, chr, cs }| where either the first two are taken or the last one. This is
392    something historic. So we have either |{ cmd, chr, - }| or |{ -, -, cs}| to deal with. This
393    might change in the future but we then need to check all usage in \CONTEXT\ first.
394*/
395
396inline static int lmt_token_from_lua(lua_State *L)
397{
398    int cmd, chr, cs;
399    lua_rawgeti(L, -1, 1);
400    cmd = lmt_tointeger(L, -1);
401    lua_rawgeti(L, -2, 2);
402    chr = lmt_optinteger(L, -1, 0);
403    lua_rawgeti(L, -3, 3);
404    cs = lmt_optinteger(L, -1, 0);
405    lua_pop(L, 3);
406    return tokenlib_aux_valid_token(cmd, chr, cs); /* just the token value */
407}
408
409void lmt_token_list_to_lua(lua_State *L, halfword p)
410{
411    int i = 1;
412    int v = p;
413    int max = lmt_token_memory_state.tokens_data.top; /*tex It doesn't change here. */
414    while (v && v < max) {
415        i++;
416        v = token_link(v);
417    }
418    lua_createtable(L, i, 0);
419    i = 1;
420    while (p && p < max) {
421        int cmd, chr, cs;
422        if (token_info(p) >= cs_token_flag) {
423            cs = token_info(p) - cs_token_flag;
424            cmd = eq_type(cs);
425            chr = eq_value(cs);
426        } else {
427            cs = 0;
428            cmd = token_cmd(token_info(p));
429            chr = token_chr(token_info(p));
430        }
431        tokenlib_aux_make_token_table(L, cmd, chr, cs);
432        lua_rawseti(L, -2, i++);
433        p = token_link(p);
434    }
435}
436
437void lmt_token_list_to_luastring(lua_State *L, halfword p, int nospace, int strip, int wipe)
438{
439    int l;
440 // char *s = tex_tokenlist_to_tstring(p, 1, &l, 0, nospace, strip, wipe, 0); /* nasty ... preambles or not, could have been endmatchtoken  */
441    char *s = tex_tokenlist_to_tstring(p, 1, &l, 0, nospace, strip, wipe, 1); /* nasty ... preambles or not, could have been endmatchtoken  */
442    if (l) {
443        lua_pushlstring(L, s, (size_t) l);
444    } else {
445        lua_pushliteral(L, "");
446    }
447}
448
449static lua_token *tokenlib_aux_check_istoken(lua_State *L, int ud);
450
451halfword lmt_token_list_from_lua(lua_State *L, int slot)
452{
453    halfword h = tex_get_available_token(null);
454    halfword p = h;
455    token_link(h) = null;
456    switch (lua_type(L, slot)) {
457        case LUA_TTABLE:
458            {
459                int j = (int) lua_rawlen(L, slot);
460                if (j > 0) {
461                    for (int i = 1; i <= j; i++) {
462                        int tok;
463                        lua_rawgeti(L, slot, (int) i);
464                        tok = lmt_token_from_lua(L);
465                        if (tok >= 0) {
466                            p = tex_store_new_token(p, tok);
467                        }
468                        lua_pop(L, 1);
469                    };
470                }
471                return h;
472            }
473        case LUA_TSTRING:
474            {
475                size_t j;
476                const char *s = lua_tolstring(L, slot, &j);
477                for (size_t i = 0; i < j; i++) {
478                    int tok;
479                    if (s[i] == ascii_space) {
480                        tok = token_val(spacer_cmd, s[i]);
481                    } else {
482                        int kl; 
483                        int k = (int) aux_str2uni_len((const unsigned char *) (s + i), &kl);
484                        i = i + kl - 1;
485                        tok = token_val(other_char_cmd, k);
486                    }
487                    p = tex_store_new_token(p, tok);
488                }
489                return h;
490            }
491        case LUA_TUSERDATA:
492            {
493                lua_token *t = tokenlib_aux_check_istoken(L, slot);
494                p = tex_store_new_token(p, t->token);
495                return h;
496            }
497        default:
498            {
499                tex_put_available_token(h);
500                return null;
501            }
502    }
503}
504
505halfword lmt_token_code_from_lua(lua_State *L, int slot)
506{
507    lua_token *t = tokenlib_aux_check_istoken(L, slot);
508    return t->token;
509}
510
511# define DEFAULT_SCAN_CODE_SET (2048 + 4096) /*tex default: letter and other */
512
513/*tex two core helpers .. todo: combine active*/
514
515# define is_active_string(s) (strlen(s) > 3 && *s == 0xEF && *(s+1) == 0xBF && *(s+2) == 0xBF)
516
517static unsigned char *tokenlib_aux_get_cs_text(int cs, int *allocated)
518{
519    if (cs == null_cs) {
520        return (unsigned char *) "\\csname\\endcsname";
521    } else if ((cs_text(cs) < 0) || (cs_text(cs) >= lmt_string_pool_state.string_pool_data.ptr)) {
522        return (unsigned char *) "";
523 // } else {
524 //     return (unsigned char *) tex_makecstring(cs_text(cs));
525    } else if (cs_text(cs) < cs_offset_value) {
526        *allocated = 1;
527        return (unsigned char *) aux_uni2str((unsigned) cs_text(cs));
528    } else {
529        return (unsigned char *) (str_length(cs_text(cs)) > 0 ?  (unsigned char *) str_string(cs_text(cs)) :  (unsigned char *) "");
530    }
531}
532
533static lua_token *tokenlib_aux_maybe_istoken(lua_State *L, int ud)
534{
535    lua_token *t = lua_touserdata(L, ud);
536    if (t && lua_getmetatable(L, ud)) {
537        lua_get_metatablelua(token_instance);
538        if (! lua_rawequal(L, -1, -2)) {
539            t = NULL;
540        }
541        lua_pop(L, 2);
542    }
543    return t;
544}
545
546static lua_token_package *tokenlib_aux_maybe_ispackage(lua_State *L, int ud)
547{
548    lua_token_package *t = lua_touserdata(L, ud);
549    if (t && lua_getmetatable(L, ud)) {
550        lua_get_metatablelua(token_package);
551        if (! lua_rawequal(L, -1, -2)) {
552            t = NULL;
553        }
554        lua_pop(L, 2);
555    }
556    return t;
557}
558
559/*tex we could make the message a function and just inline the rest (via a macro) */
560
561lua_token *tokenlib_aux_check_istoken(lua_State *L, int ud)
562{
563    lua_token *t = tokenlib_aux_maybe_istoken(L, ud);
564    if (! t) {
565        tex_formatted_error("token lib", "lua <token> expected, not an object with type %s", luaL_typename(L, ud));
566    }
567    return t;
568}
569
570static lua_token_package *tokenlib_aux_check_ispackage(lua_State *L, int ud)
571{
572    lua_token_package *t = tokenlib_aux_maybe_ispackage(L, ud);
573    if (! t) {
574        tex_formatted_error("token lib", "lua <token package> expected, not an object with type %s", luaL_typename(L, ud));
575    }
576    return t;
577}
578
579/*tex token library functions */
580
581static void tokenlib_aux_make_new_token(lua_State *L, int cmd, int chr, int cs)
582{
583    int tok = tokenlib_aux_valid_token(cmd, chr, cs);
584    if (tok >= 0) {
585        lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
586        thetok->token = tex_get_available_token(tok);
587        thetok->origin = token_origin_lua;
588        lua_get_metatablelua(token_instance);
589        lua_setmetatable(L, -2);
590    } else {
591        lua_pushnil(L);
592    }
593}
594
595static void tokenlib_aux_make_new_token_tok(lua_State *L, int tok)
596{
597    if (tok >= 0) {
598        lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
599        thetok->token = tex_get_available_token(tok);
600        thetok->origin = token_origin_lua;
601        lua_get_metatablelua(token_instance);
602        lua_setmetatable(L, -2);
603    } else {
604        lua_pushnil(L);
605    }
606}
607
608static void tokenlib_aux_make_new_package(lua_State *L, singleword cmd, singleword flag, int chr, int cs, quarterword how)
609{
610    lua_token_package *package = (lua_token_package *) lua_newuserdatauv(L, sizeof(lua_token_package), 0);
611    package->cmd = cmd;
612    package->flag = flag;
613    package->chr = chr;
614    package->cs = cs;
615    package->how = how;
616    lua_get_metatablelua(token_package);
617    lua_setmetatable(L, -2);
618}
619
620static void tokenlib_aux_push_token(lua_State *L, int tok)
621{
622    lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
623    thetok->token = tok;
624    thetok->origin = token_origin_lua;
625    lua_get_metatablelua(token_instance);
626    lua_setmetatable(L, -2);
627}
628
629static int tokenlib_getcommandid(lua_State *L)
630{
631    int id = -1;
632    switch (lua_type(L, 1)) {
633        case LUA_TSTRING:
634            id = tokenlib_aux_get_command_id(lua_tostring(L, 1));
635            break;
636        case LUA_TNUMBER:
637            id = lmt_tointeger(L, 1);
638            break;
639    }
640    if (id >= 0 && id < number_glue_pars) {
641        lua_pushinteger(L, id);
642    } else {
643        lua_pushnil(L);
644    }
645    return 1;
646}
647
648static int tokenlib_scan_next(lua_State *L)
649{
650    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
651    halfword tok = tex_get_token();
652    tokenlib_aux_make_new_token_tok(L, tok);
653    tokenlib_aux_unsave_tex_scanner(texstate);
654    return 1;
655}
656
657static int tokenlib_scan_next_expanded(lua_State *L)
658{
659    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
660    halfword tok = tex_get_x_token();
661    tokenlib_aux_make_new_token_tok(L, tok);
662    tokenlib_aux_unsave_tex_scanner(texstate);
663    return 1;
664}
665
666static int tokenlib_skip_next(lua_State *L)
667{
668    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
669    (void) L;
670    tex_get_token();
671    tokenlib_aux_unsave_tex_scanner(texstate);
672    return 0;
673}
674
675static int tokenlib_skip_next_expanded(lua_State *L)
676{
677    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
678    (void) L;
679    tex_get_x_token();
680    tokenlib_aux_unsave_tex_scanner(texstate);
681    return 0;
682}
683
684/*tex
685
686    This is experimental code:
687
688        \starttyping
689        local t1 = token.get_next()
690        local t2 = token.get_next()
691        local t3 = token.get_next()
692        local t4 = token.get_next()
693        -- watch out, we flush in sequence
694        token.put_next { t1, t2 }
695        -- but this one gets pushed in front
696        token.put_next ( t3, t4 )
697        -- so when we get wxyz we put yzwx!
698        \stoptyping
699
700    At some point we can consider a token.print that delays and goes via the same rope mechanism as
701    |texio.print| and friends but then one can as well serialize the tokens and do a normal print so
702    there is no real gain in it. After all, the tokenlib operates at the input level so we might as
703    well keep it there.
704
705*/
706
707inline static int tokenlib_aux_to_token_val(int chr)
708{
709    switch (chr) {
710        case '\n':
711        case '\r':
712        case ' ':
713            return token_val(spacer_cmd, ' ');
714        default:
715            {
716                int cmd = tex_get_cat_code(cat_code_table_par, chr);
717                switch (cmd) {
718                    case escape_cmd:
719                    case ignore_cmd:
720                    case comment_cmd:
721                    case invalid_char_cmd:
722                    case active_char_cmd:
723                        cmd = other_char_cmd;
724                        break;
725                }
726                return token_val(cmd, chr);
727            }
728    }
729}
730
731/*tex
732    The original implementation was a bit different in the sense that I distinguished between one or
733    more arguments with the one argument case handling a table. The reason was that I considered
734    having an optional second argument that could control the catcode table.
735
736    In the end this function is not used that often (of at all), so after checking the manual, I
737    decided not to provide that feature so the code could be simplified a bit. But, as compensation,
738    nested tables became possible.
739*/
740
741static void tokenlib_aux_to_token(lua_State *L, int i, int m, int *head, int *tail)
742{
743    switch (lua_type(L, i)) {
744        case LUA_TSTRING:
745            /*tex More efficient is to iterate (but then we also need to know the length). */
746            {
747                size_t l = 0;
748                const char *s = lua_tolstring(L, i, &l);
749                const unsigned char *p = (const unsigned char *) s;
750                size_t n = aux_utf8len(s, l);
751                for (size_t j = 0; j < n; j++) {
752                    int xl; 
753                    halfword x = tex_get_available_token(tokenlib_aux_to_token_val(aux_str2uni_len(p, &xl)));
754                    if (*head) {
755                        token_link(*tail) = x;
756                    } else {
757                        *head = x;
758                    }
759                    *tail = x;
760                    p += xl;
761                }
762                break;
763            }
764        case LUA_TNUMBER:
765            {
766                halfword t = tex_get_available_token(tokenlib_aux_to_token_val((int) lua_tointeger(L, i)));
767                if (*head) {
768                    token_link(*tail) = t;
769                } else {
770                    *head = t;
771                }
772                *tail = t;
773                break;
774            }
775        case LUA_TTABLE:
776            {
777                size_t n = lua_rawlen(L, i);
778                for (size_t j = 1; j <= n; j++) {
779                    lua_rawgeti(L, i, j);
780                    tokenlib_aux_to_token(L, -1, m, head, tail);
781                    lua_pop(L, 1);
782                }
783                break;
784            }
785        case LUA_TUSERDATA:
786            {
787                /* todo: like nodelib: |maybe_is_token|. */
788                lua_token *p = lua_touserdata(L, i);
789                halfword t, q;
790                if (p && lua_getmetatable(L, i)) {
791                    t = lua_rawequal(L, m, -1) ? token_info(p->token) : tokenlib_aux_to_token_val(0xFFFD);
792                    lua_pop(L, 1); /* The metatable. */
793                } else {
794                    t = tokenlib_aux_to_token_val(0xFFFD);
795                }
796                q = tex_get_available_token(t);
797                if (*head) {
798                    token_link(*tail) = q;
799                } else {
800                    *head = q;
801                }
802                *tail = q;
803                break;
804            }
805        default:
806            /*tex Just ignore it. */
807            break;
808    }
809}
810
811inline static int tokenlib_put_next(lua_State *L)
812{
813    int top = lua_gettop(L);
814    if (top > 0) {
815        halfword h = null;
816        halfword t = null;
817        int m = top + 1;
818        lua_get_metatablelua(token_instance);
819        for (int i = 1; i <= top; i++) {
820            tokenlib_aux_to_token(L, i, m, &h, &t);
821        }
822        if (h) {
823            tex_begin_inserted_list(h);
824        }
825        lua_settop(L, top);
826    }
827    return 0;
828}
829
830inline static int tokenlib_put_back(lua_State *L)
831{
832    lua_token *t = tokenlib_aux_check_istoken(L, 1);
833    if (t) {
834        tex_back_input(token_info(t->token));
835    }
836    return 0;
837}
838
839static int tokenlib_scan_keyword(lua_State *L)
840{
841    const char *s = lua_tostring(L, 1);
842    int v = 0;
843    if (s) {
844        saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
845        v = tex_scan_keyword(s);
846        tokenlib_aux_unsave_tex_scanner(texstate);
847    }
848    lua_pushboolean(L, v);
849    return 1;
850}
851
852static int tokenlib_scan_keyword_cs(lua_State *L)
853{
854    const char *s = lua_tostring(L, 1);
855    int v = 0;
856    if (s) {
857        saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
858        v = tex_scan_keyword_case_sensitive(s);
859        tokenlib_aux_unsave_tex_scanner(texstate);
860    }
861    lua_pushboolean(L, v);
862    return 1;
863}
864
865static int tokenlib_scan_csname(lua_State *L)
866{
867    int t;
868    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
869    if (lua_toboolean(L, 1)) {
870        /*tex Not here: |tex_get_next_non_spacer()| unless we adapt more later on. */
871        do {
872            tex_get_token();
873        } while (cur_tok == space_token);
874    } else {
875        /*tex checked */
876        tex_get_next();
877    }
878    t = cur_cs ? cs_token_flag + cur_cs : token_val(cur_cmd, cur_chr);
879    if (t >= cs_token_flag) {
880        int allocated = 0;
881        unsigned char *s = tokenlib_aux_get_cs_text(t - cs_token_flag, &allocated);
882        if (s) {
883            if (tex_is_active_cs(cs_text(t - cs_token_flag))) {
884                lua_pushstring(L, (char *) (s + 3));
885            } else {
886                lua_pushstring(L, (char *) s);
887            }
888            if (allocated) {
889                lmt_memory_free(s);
890            }
891        } else {
892            lua_pushnil(L);
893        }
894    } else {
895        lua_pushnil(L);
896    }
897    tokenlib_aux_unsave_tex_scanner(texstate);
898    return 1;
899}
900
901static int tokenlib_scan_integer(lua_State *L)
902{
903    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
904    int eq = lua_toboolean(L, 1);
905    halfword v = tex_scan_integer(eq, NULL);
906    lua_pushinteger(L, (int) v);
907    tokenlib_aux_unsave_tex_scanner(texstate);
908    return 1;
909}
910
911static int tokenlib_scan_cardinal(lua_State *L)
912{
913    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
914    int eq = lua_toboolean(L, 1);
915    unsigned int v = 0;
916    tex_scan_cardinal(eq, &v, 0);
917    lua_pushinteger(L, (unsigned int) v);
918    tokenlib_aux_unsave_tex_scanner(texstate);
919    return 1;
920}
921
922static int tokenlib_gobble_integer(lua_State *L)
923{
924    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
925    int eq = lua_toboolean(L, 1);
926    lmt_error_state.intercept = 1;
927    lmt_error_state.last_intercept = 0;
928    tex_scan_integer(eq, NULL);
929    lua_pushboolean(L, ! lmt_error_state.last_intercept);
930    lmt_error_state.intercept = 0;
931    lmt_error_state.last_intercept = 0;
932    tokenlib_aux_unsave_tex_scanner(texstate);
933    return 1;
934}
935
936inline static void tokenlib_aux_goto_first_candidate(void)
937{
938    do {
939        tex_get_token();
940    } while (cur_cmd == spacer_cmd);
941}
942
943inline static void tokenlib_aux_goto_first_candidate_x(void)
944{
945    do {
946        tex_get_x_token();
947    } while (cur_cmd == spacer_cmd);
948}
949
950inline static void tokenlib_aux_add_utf_char_to_buffer(luaL_Buffer *b, halfword chr)
951{
952    if (chr <= ascii_max) {
953        luaL_addchar(b, (unsigned char) chr);
954    } else {
955        /*
956        unsigned char word[5 + 1];
957        char *uindex = aux_uni2string((char *) word, (unsigned int) chr);
958        *uindex = '\0';
959        luaL_addstring(b, (char *) word);
960        */
961        unsigned char word[5 + 1];
962        aux_uni2string((char *) word, (unsigned int) chr);
963        luaL_addlstring(b, (char *) word, utf8_size(chr));
964    }
965}
966
967/*tex
968    We could of course work with sets or ranges but the bit of duplicate code doesn't harm that
969    much. The hexadecimal variant also deals with \LUA\ serialized numbers like |123.345E67| being
970    equivalent to |0x1.6e0276db950fp+229| (as output by the |q| formatter option).
971
972    Nota Bene: |DECIMAL| can be defined as macro or whatever else; the ms compiler reports an error,
973    so we use |SCANDECIMAL| instead.
974*/
975
976static int tokenlib_scan_float_indeed(lua_State *L, int exponent, int hexadecimal)
977{
978    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
979    int negative = 0;
980    luaL_Buffer b;
981    luaL_buffinit(L, &b);
982    tokenlib_aux_goto_first_candidate_x();
983    if (lua_toboolean(L, 1) && (cur_tok == equal_token)) {
984        tokenlib_aux_goto_first_candidate_x();
985    }
986    /*tex we collapse as in |scan_dimension| */
987    while(1) {
988        if (cur_tok == minus_token) {
989            negative = ! negative;
990        } else if (cur_tok != plus_token) {
991            break;
992        }
993        tokenlib_aux_goto_first_candidate_x();
994    }
995    if (negative) {
996        luaL_addchar(&b, '-');
997    }
998    /*tex we accept |[.,]digits| */
999    if (hexadecimal && (cur_tok == zero_token)) {
1000        luaL_addchar(&b, '0');
1001        tex_get_x_token();
1002        if (tex_token_is_hexadecimal(cur_tok)) {
1003            luaL_addchar(&b, 'x');
1004            goto SCANHEXADECIMAL;
1005        } else {
1006            goto PICKUPDECIMAL;
1007        }
1008    } else {
1009        goto SCANDECIMAL;
1010    }
1011  SCANDECIMAL:
1012    if (tex_token_is_seperator(cur_tok)) {
1013        luaL_addchar(&b, '.');
1014        while (1) {
1015            tex_get_x_token();
1016            if (tex_token_is_digit(cur_tok)) {
1017                luaL_addchar(&b, (unsigned char) cur_chr);
1018            } else if (exponent) {
1019                goto DECIMALEXPONENT;
1020            } else {
1021                tex_back_input(cur_tok);
1022                goto DONE;
1023            }
1024        }
1025    } else {
1026        goto PICKUPDECIMAL;
1027    }
1028    while (1) {
1029        tex_get_x_token();
1030      PICKUPDECIMAL:
1031        if (tex_token_is_digit(cur_tok)) {
1032            luaL_addchar(&b, (unsigned char) cur_chr);
1033        } else if (tex_token_is_seperator(cur_tok)) {
1034            luaL_addchar(&b, '.');
1035            while (1) {
1036                tex_get_x_token();
1037                if (tex_token_is_digit(cur_tok)) {
1038                    luaL_addchar(&b, (unsigned char) cur_chr);
1039                } else {
1040                    tex_back_input(cur_tok);
1041                    break;
1042                }
1043            }
1044        } else if (exponent) {
1045            goto DECIMALEXPONENT;
1046        } else {
1047            tex_back_input(cur_tok);
1048            goto DONE;
1049        }
1050    }
1051  DECIMALEXPONENT:
1052    if (tex_token_is_exponent(cur_tok)) {
1053        luaL_addchar(&b, (unsigned char) cur_chr);
1054        tex_get_x_token();
1055        if (tex_token_is_sign(cur_tok)) {
1056            luaL_addchar(&b, (unsigned char) cur_chr);
1057        } else if (tex_token_is_digit(cur_tok)) {
1058            luaL_addchar(&b, (unsigned char) cur_chr);
1059        }
1060        while (1) {
1061            tex_get_x_token();
1062            if (tex_token_is_digit(cur_tok)) {
1063                luaL_addchar(&b, (unsigned char) cur_chr);
1064            } else {
1065                break;
1066            }
1067        }
1068    }
1069    tex_back_input(cur_tok);
1070    goto DONE;
1071  SCANHEXADECIMAL:
1072    tex_get_x_token();
1073    if (tex_token_is_seperator(cur_tok)) {
1074       luaL_addchar(&b, '.');
1075        while (1) {
1076            tex_get_x_token();
1077            if (tex_token_is_xdigit(cur_tok)) {
1078                luaL_addchar(&b, (unsigned char) cur_chr);
1079            } else if (exponent) {
1080                goto HEXADECIMALEXPONENT;
1081            } else {
1082                tex_back_input(cur_tok);
1083                goto DONE;
1084            }
1085        }
1086    } else {
1087        /* hm, we could avoid this pushback */
1088        tex_back_input(cur_tok);
1089        while (1) {
1090            tex_get_x_token();
1091            if (tex_token_is_xdigit(cur_tok)) {
1092                luaL_addchar(&b, (unsigned char) cur_chr);
1093            } else if (tex_token_is_seperator(cur_tok)) {
1094                luaL_addchar(&b, '.');
1095                while (1) {
1096                    tex_get_x_token();
1097                    if (tex_token_is_xdigit(cur_tok)) {
1098                        luaL_addchar(&b, (unsigned char) cur_chr);
1099                    } else {
1100                        tex_back_input(cur_tok);
1101                        break;
1102                    }
1103                }
1104            } else if (exponent) {
1105                goto HEXADECIMALEXPONENT;
1106            } else {
1107                tex_back_input(cur_tok);
1108                goto DONE;
1109            }
1110        }
1111    }
1112  HEXADECIMALEXPONENT:
1113    if (tex_token_is_xexponent(cur_tok)) {
1114        luaL_addchar(&b, (unsigned char) cur_chr);
1115        tex_get_x_token();
1116        if (tex_token_is_sign(cur_tok)) {
1117            /*
1118            tex_normal_warning("scanner", "no negative hexadecimal exponent permitted, ignoring minus sign");
1119            */
1120            luaL_addchar(&b, (unsigned char) cur_chr);
1121        } else if (tex_token_is_xdigit(cur_tok)) {
1122            luaL_addchar(&b, (unsigned char) cur_chr);
1123        }
1124        while (1) {
1125            tex_get_x_token();
1126            if (tex_token_is_xdigit(cur_tok)) {
1127                luaL_addchar(&b, (unsigned char) cur_chr);
1128            } else {
1129                break;
1130            }
1131        }
1132    }
1133    tex_back_input(cur_tok);
1134  DONE:
1135    luaL_pushresult(&b);
1136    {
1137        int ok = 0;
1138        double d = lua_tonumberx(L, -1, &ok);
1139        if (ok) {
1140            lua_pushnumber(L, d);
1141        } else {
1142            lua_pushnil(L);
1143        }
1144    }
1145    tokenlib_aux_unsave_tex_scanner(texstate);
1146    return 1;
1147}
1148
1149static int tokenlib_scan_integer_indeed(lua_State *L, int cardinal)
1150{
1151    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1152    int negative = 0;
1153    luaL_Buffer b;
1154    luaL_buffinit(L, &b);
1155    tokenlib_aux_goto_first_candidate_x();
1156    if (lua_toboolean(L, 1) && (cur_tok == equal_token)) {
1157        tokenlib_aux_goto_first_candidate_x();
1158    }
1159    /*tex we collapse as in |scan_dimension| */
1160//    if (! cardinal) {
1161//        while(1) {
1162//            if (cur_tok == minus_token) {
1163//                negative = ! negative;
1164//            } else if (cur_tok != plus_token) {
1165//                break;
1166//            }
1167//            tokenlib_aux_goto_first_candidate_x();
1168//        }
1169//        if (negative) {
1170//            luaL_addchar(&b, '-');
1171//        }
1172//    } else if (cur_tok == minus_token) {
1173//        tex_normal_warning("scanner", "positive number expected, ignoring minus sign");
1174//        tokenlib_aux_goto_first_candidate_x();
1175//    }
1176    while(1) {
1177        if (cur_tok == minus_token) {
1178            negative = ! negative;
1179        } else if (cur_tok != plus_token) {
1180            break;
1181        }
1182        tokenlib_aux_goto_first_candidate_x();
1183    }
1184    if (negative) {
1185        if (cardinal) { 
1186            tex_normal_warning("scanner", "positive number expected, ignoring minus sign");
1187        } else {
1188            luaL_addchar(&b, '-');
1189        }
1190        tokenlib_aux_goto_first_candidate_x();
1191    }
1192    if (cur_tok == zero_token) {
1193        luaL_addchar(&b, '0');
1194        tex_get_x_token();
1195        if (tex_token_is_hexadecimal(cur_tok)) {
1196            luaL_addchar(&b, 'x');
1197            goto HEXADECIMAL;
1198        } else {
1199            goto PICKUPDECIMAL;
1200        }
1201    } else {
1202        goto PICKUPDECIMAL;
1203    }
1204    while (1) {
1205        tex_get_x_token();
1206      PICKUPDECIMAL:
1207        if (tex_token_is_digit(cur_tok)) {
1208            luaL_addchar(&b, (unsigned char) cur_chr);
1209        } else {
1210            tex_back_input(cur_tok);
1211            goto DONE;
1212        }
1213    }
1214  HEXADECIMAL:
1215    while (1) {
1216        tex_get_x_token();
1217        if (tex_token_is_xdigit(cur_tok)) {
1218            luaL_addchar(&b, (unsigned char) cur_chr);
1219        } else {
1220            tex_back_input(cur_tok);
1221            goto DONE;
1222        }
1223    }
1224  DONE:
1225    luaL_pushresult(&b);
1226    if (cardinal) {
1227        int ok = 0;
1228        lua_Unsigned c = lua_tointegerx(L, -1, &ok);
1229        if (ok) {
1230            lua_pushinteger(L, c);
1231        } else {
1232            lua_pushnil(L);
1233        }
1234    } else {
1235        int ok = 0;
1236        lua_Integer i = lua_tointegerx(L, -1, &ok);
1237        if (ok) {
1238            lua_pushinteger(L, i);
1239        } else {
1240            lua_pushnil(L);
1241        }
1242    }
1243    tokenlib_aux_unsave_tex_scanner(texstate);
1244    return 1;
1245}
1246
1247static int tokenlib_scan_float(lua_State *L)
1248{
1249    return tokenlib_scan_float_indeed(L, 1, 0);
1250}
1251
1252static int tokenlib_scan_real(lua_State *L)
1253{
1254    return tokenlib_scan_float_indeed(L, 0, 0);
1255}
1256
1257static int tokenlib_scan_luanumber(lua_State* L)
1258{
1259    return tokenlib_scan_float_indeed(L, 1, 1);
1260}
1261
1262static int tokenlib_scan_luainteger(lua_State* L)
1263{
1264    return tokenlib_scan_integer_indeed(L, 0);
1265}
1266
1267static int tokenlib_scan_luacardinal(lua_State* L)
1268{
1269    return tokenlib_scan_integer_indeed(L, 1);
1270}
1271
1272static int tokenlib_scan_scale(lua_State *L)
1273{
1274    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1275    int eq = lua_toboolean(L, 1);
1276    halfword val = tex_scan_scale(eq);
1277    lua_pushinteger(L, val);
1278    tokenlib_aux_unsave_tex_scanner(texstate);
1279    return 1;
1280}
1281
1282static int tokenlib_scan_dimension(lua_State *L)
1283{
1284    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1285    int inf = lua_toboolean(L, 1);
1286    int mu = lua_toboolean(L, 2);
1287    int eq = lua_toboolean(L, 3);
1288    halfword order;
1289    halfword val = tex_scan_dimension(mu, inf, 0, eq, &order);
1290    lua_pushinteger(L, val);
1291    tokenlib_aux_unsave_tex_scanner(texstate);
1292    if (inf) {
1293        lua_pushinteger(L, order);
1294        return 2;
1295    } else {
1296        return 1;
1297    }
1298}
1299
1300static int tokenlib_scan_posit(lua_State *L)
1301{
1302    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1303    int eq = lua_toboolean(L, 1);
1304    int fl = lua_toboolean(L, 1);
1305    halfword val = tex_scan_posit(eq);
1306    if (fl) { 
1307        lua_pushnumber(L, tex_posit_to_double(val));
1308    } else { 
1309        lua_pushinteger(L, val);
1310    }
1311    tokenlib_aux_unsave_tex_scanner(texstate);
1312    return 1;
1313}
1314
1315static int tokenlib_gobble_dimension(lua_State *L)
1316{
1317    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1318    int inf = lua_toboolean(L, 1);
1319    int mu = lua_toboolean(L, 2);
1320    int eq = lua_toboolean(L, 3);
1321    lmt_error_state.intercept = 1;
1322    lmt_error_state.last_intercept = 0;
1323    tex_scan_dimension(mu, inf, 0, eq, NULL);
1324    lua_pushboolean(L, ! lmt_error_state.last_intercept);
1325    lmt_error_state.intercept = 0;
1326    lmt_error_state.last_intercept = 0;
1327    tokenlib_aux_unsave_tex_scanner(texstate);
1328    return 1;
1329}
1330
1331static int tokenlib_scan_skip(lua_State *L)
1332{
1333    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1334    int mu = lua_toboolean(L, 1) ? muglue_val_level : glue_val_level;
1335    int eq = lua_toboolean(L, 2);
1336    halfword v = tex_scan_glue(mu, eq, 0);
1337    lmt_push_node_fast(L, v);
1338    tokenlib_aux_unsave_tex_scanner(texstate);
1339    return 1;
1340}
1341
1342static int tokenlib_scan_glue(lua_State *L)
1343{
1344    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1345    int mu = lua_toboolean(L, 1) ? muglue_val_level : glue_val_level;
1346    int eq = lua_toboolean(L, 2);
1347    int t  = lua_toboolean(L, 3);
1348    halfword v = tex_scan_glue(mu, eq, 0);
1349    tokenlib_aux_unsave_tex_scanner(texstate);
1350    if (t) {
1351        lua_createtable(L, 5, 0);
1352        lua_pushinteger(L, glue_amount(v));
1353        lua_rawseti(L, -2, 1);
1354        lua_pushinteger(L, glue_stretch(v));
1355        lua_rawseti(L, -2, 2);
1356        lua_pushinteger(L, glue_shrink(v));
1357        lua_rawseti(L, -2, 3);
1358        lua_pushinteger(L, glue_stretch_order(v));
1359        lua_rawseti(L, -2, 4);
1360        lua_pushinteger(L, glue_shrink_order(v));
1361        lua_rawseti(L, -2, 5);
1362        return 1;
1363    } else {
1364        lua_pushinteger(L, glue_amount(v));
1365        lua_pushinteger(L, glue_stretch(v));
1366        lua_pushinteger(L, glue_shrink(v));
1367        lua_pushinteger(L, glue_stretch_order(v));
1368        lua_pushinteger(L, glue_shrink_order(v));
1369        return 5;
1370    }
1371}
1372
1373inline static void lmt_token_list_to_lua_tokens(lua_State *L, halfword t)
1374{
1375    int i = 1;
1376    lua_newtable(L);
1377    while (t) {
1378        halfword n = token_link(t);
1379        token_link(t) = null;
1380        tokenlib_aux_push_token(L, t);
1381        lua_rawseti(L, -2, i++);
1382        t = n;
1383    }
1384}
1385
1386void lmt_token_register_to_lua(lua_State *L, halfword h, int originals)
1387{
1388    int i = 1;
1389    lua_newtable(L);
1390    if (h) {
1391        halfword t = token_link(h);
1392        while (t) {
1393            halfword m = tex_get_available_token(token_info(t));
1394            tokenlib_aux_push_token(L, m);
1395            lua_rawseti(L, -2, i++);
1396            t = token_link(t);
1397        }
1398    }
1399    if (originals) { 
1400        lua_createtable(L, i, 0);
1401        if (h) {
1402            halfword t = token_link(h);
1403            i = 1;
1404            while (t) {
1405                lua_push_integer(L, t);
1406                lua_rawseti(L, -2, i++);
1407                t = token_link(t);
1408            }
1409        } else { 
1410            lua_newtable(L);
1411        }
1412    }
1413}
1414
1415static int tokenlib_scan_toks(lua_State *L)
1416{
1417    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1418    int macro = lua_toboolean(L, 1);
1419    int expand = lua_toboolean(L, 2);
1420    halfword defref = lmt_input_state.def_ref;
1421    halfword result, t;
1422    if (macro) {
1423        result = expand ? tex_scan_macro_expand() : tex_scan_macro_normal();
1424    } else {
1425        result = expand ? tex_scan_toks_expand(0, NULL, 0, 0) : tex_scan_toks_normal(0, NULL);
1426    }
1427    tokenlib_aux_unsave_tex_scanner(texstate);
1428    lmt_input_state.def_ref = defref;
1429    t = token_link(result);
1430    token_link(result) = null;
1431    tex_put_available_token(result);
1432    lmt_token_list_to_lua_tokens(L, t);
1433    return 1;
1434}
1435
1436static int tokenlib_scan_tokenlist(lua_State *L)
1437{
1438    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1439    int macro = lua_toboolean(L, 1);
1440    int expand = lua_toboolean(L, 2);
1441    halfword result;
1442    halfword defref = lmt_input_state.def_ref;
1443    if (macro) {
1444        result = expand ? tex_scan_macro_expand() : tex_scan_macro_normal();
1445    } else {
1446        result = expand ? tex_scan_toks_expand(0, NULL, 0, 0) : tex_scan_toks_normal(0, NULL);
1447    }
1448    tokenlib_aux_push_token(L, result);
1449    tokenlib_aux_unsave_tex_scanner(texstate);
1450    lmt_input_state.def_ref = defref;
1451    return 1;
1452}
1453
1454static int tokenlib_scan_detokened(lua_State *L)
1455{
1456    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1457    int expand = lua_toboolean(L, 1);
1458    halfword defref = lmt_input_state.def_ref;
1459    halfword result = null;
1460    int macro = 0;
1461    tokenlib_aux_goto_first_candidate(); /*tex We don't expand the next token! */
1462    switch (cur_cmd) {
1463        case left_brace_cmd:
1464            result = expand ? tex_scan_toks_expand(1, NULL, 0, 0) : tex_scan_toks_normal(1, NULL);
1465            break;
1466        case call_cmd:
1467        case protected_call_cmd:
1468        case semi_protected_call_cmd:
1469        case constant_call_cmd:
1470        case tolerant_call_cmd:
1471        case tolerant_protected_call_cmd:
1472        case tolerant_semi_protected_call_cmd:
1473            result = cur_chr;
1474            macro = 1;
1475            break;
1476        default:
1477            tex_back_input(cur_tok);
1478            break;
1479    }
1480    tokenlib_aux_unsave_tex_scanner(texstate);
1481    lmt_input_state.def_ref = defref;
1482    if (result) { 
1483        if (token_link(result)) {
1484            tex_detokenize_list(token_link(result));
1485            if (lmt_string_pool_state.string_temp && lmt_string_pool_state.string_temp_top) {
1486                lua_pushlstring(L, (char *) lmt_string_pool_state.string_temp, lmt_string_pool_state.string_temp_top);
1487            } else {
1488                lua_pushliteral(L,"");
1489            }
1490            tex_reset_cur_string();
1491        } else {
1492            lua_pushliteral(L,"");
1493        }
1494        if (! macro) {
1495            tex_flush_token_list(result);
1496        }
1497    } else { 
1498        lua_pushnil(L);
1499    }
1500    return 1;
1501}
1502
1503/* todo: other call_cmd */
1504
1505static int tokenlib_scan_tokenstring(lua_State *L) /* noexpand noexpandconstant noexpandparameters */
1506{
1507    /*tex is saving really needed here? */
1508    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1509    halfword defref = lmt_input_state.def_ref;
1510    halfword result = lua_toboolean(L, 1) ? tex_scan_toks_normal(0, NULL) : tex_scan_toks_expand(0, NULL, ! lua_toboolean(L, 2), ! lua_toboolean(L, 3));
1511 // halfword result = tex_scan_toks_expand(0, NULL, 1, 1);
1512    lmt_token_list_to_luastring(L, result, 0, 0, 1);
1513    lmt_input_state.def_ref = defref;
1514    tokenlib_aux_unsave_tex_scanner(texstate);
1515    return 1;
1516}
1517
1518static int tokenlib_scan_string(lua_State *L)
1519{
1520    /*tex can be simplified, no need for intermediate list */
1521    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1522    tokenlib_aux_goto_first_candidate_x(); /* actually this expands a following macro*/
1523    switch (cur_cmd) {
1524        case left_brace_cmd:
1525            {
1526                halfword defref = lmt_input_state.def_ref;
1527                halfword result = tex_scan_toks_expand(1, NULL, 0, 0);
1528                lmt_token_list_to_luastring(L, result, 0, 0, 1);
1529                lmt_input_state.def_ref = defref;
1530                break;
1531            }
1532        case call_cmd:
1533        case protected_call_cmd:
1534        case semi_protected_call_cmd:
1535        case constant_call_cmd:
1536        case tolerant_call_cmd:
1537        case tolerant_protected_call_cmd:
1538        case tolerant_semi_protected_call_cmd:
1539            {
1540                /*tex It is pretty unlikely to end up here, because we expand. */
1541                halfword t = token_link(cur_chr);
1542                lmt_token_list_to_luastring(L, t, 0, 0, 1);
1543                break;
1544            }
1545        case letter_cmd:
1546        case other_char_cmd:
1547            {
1548                luaL_Buffer b;
1549                luaL_buffinit(L, &b);
1550                while (1) {
1551                    tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
1552                    tex_get_x_token();
1553                    if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd ) {
1554                        break ;
1555                    }
1556                }
1557                tex_back_input(cur_tok);
1558                luaL_pushresult(&b);
1559                break;
1560            }
1561        default:
1562            {
1563                tex_back_input(cur_tok);
1564                lua_pushnil(L);
1565                break;
1566            }
1567    }
1568    tokenlib_aux_unsave_tex_scanner(texstate);
1569    return 1;
1570}
1571
1572static int tokenlib_scan_argument(lua_State *L)
1573{
1574    /*tex can be simplified, no need for intermediate list */
1575    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1576    tokenlib_aux_goto_first_candidate();
1577    switch (cur_cmd) {
1578        case left_brace_cmd:
1579            {
1580                halfword defref = lmt_input_state.def_ref;
1581                int expand = lua_type(L, 1) == LUA_TBOOLEAN ? lua_toboolean(L, 1) : 1;
1582                halfword result = expand ? tex_scan_toks_expand(1, NULL, 0, 0) : tex_scan_toks_normal(1, NULL);
1583                lmt_token_list_to_luastring(L, result, 0, 0, 1);
1584                lmt_input_state.def_ref = defref;
1585                break;
1586            }
1587        case call_cmd:
1588        case protected_call_cmd:
1589        case semi_protected_call_cmd:
1590     // case constant_call_cmd:
1591        case tolerant_call_cmd:
1592        case tolerant_protected_call_cmd:
1593        case tolerant_semi_protected_call_cmd:
1594            {
1595                halfword result;
1596                halfword defref = lmt_input_state.def_ref;
1597                tex_back_input(right_brace_token + '}');
1598                if (lua_type(L, 1) == LUA_TBOOLEAN && ! lua_toboolean(L, 1)) {
1599                    tex_expand_current_token();
1600                    result = tex_scan_toks_normal(1, NULL);
1601                } else {
1602                    tex_back_input(cur_tok);
1603                    result = tex_scan_toks_expand(1, NULL, 0, 0);
1604                }
1605                lmt_token_list_to_luastring(L, result, 0, 0, 1);
1606                lmt_input_state.def_ref = defref;
1607                break;
1608            }
1609        case constant_call_cmd:
1610            {
1611                halfword h = token_link(cur_chr);
1612                if (h) { 
1613                    luaL_Buffer b;
1614                    luaL_buffinit(L, &b);
1615                    while (h) {
1616                        tokenlib_aux_add_utf_char_to_buffer(&b, token_chr(token_info(h)));
1617                        h = token_link(h);
1618                    }
1619                    luaL_pushresult(&b);
1620                } else { 
1621                    lua_pushliteral(L, "");
1622                }
1623                break;
1624            }
1625        case letter_cmd:
1626        case other_char_cmd:
1627            {
1628                luaL_Buffer b;
1629                luaL_buffinit(L, &b);
1630             //  while (1) {
1631                    tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
1632             //      get_x_token();
1633             //      if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd ) {
1634             //          break ;
1635             //      }
1636             //  }
1637             // back_input(cur_tok);
1638                luaL_pushresult(&b);
1639                break;
1640            }
1641        default:
1642            {
1643                tex_back_input(cur_tok);
1644                lua_pushnil(L);
1645                break;
1646            }
1647    }
1648    tokenlib_aux_unsave_tex_scanner(texstate);
1649    return 1;
1650}
1651
1652static void show_right_brace_error(void)
1653{
1654    tex_handle_error(
1655        normal_error_type,
1656        "Unbalanced value parsing (in Lua call)",
1657        "A { has to be matched by a }."
1658    );
1659}
1660
1661static int tokenlib_scan_integer_argument(lua_State *L)
1662{
1663    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1664    int wrapped = 0;
1665    tokenlib_aux_goto_first_candidate();
1666    if (cur_cmd != left_brace_cmd) {
1667        tex_back_input(cur_tok);
1668    } else {
1669        wrapped = 1;
1670    }
1671    lua_pushinteger(L, (int) tex_scan_integer(0, NULL));
1672    if (wrapped) {
1673        tokenlib_aux_goto_first_candidate();
1674        if (cur_cmd != right_brace_cmd) {
1675            show_right_brace_error();
1676        }
1677    }
1678    tokenlib_aux_unsave_tex_scanner(texstate);
1679    return 1;
1680}
1681
1682static int tokenlib_scan_dimension_argument(lua_State *L)
1683{
1684    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1685    int wrapped = 0;
1686    halfword order = 0;
1687    int inf = lua_toboolean(L, 1);
1688    int mu = lua_toboolean(L, 2);
1689    int eq = lua_toboolean(L, 3);
1690    tokenlib_aux_goto_first_candidate();
1691    if (cur_cmd != left_brace_cmd) {
1692        tex_back_input(cur_tok);
1693    } else {
1694        wrapped = 1;
1695    }
1696    lua_pushinteger(L, tex_scan_dimension(mu, inf, 0, eq, &order));
1697    if (wrapped) {
1698        tokenlib_aux_goto_first_candidate();
1699        if (cur_cmd != right_brace_cmd) {
1700            show_right_brace_error();
1701        }
1702    }
1703    tokenlib_aux_unsave_tex_scanner(texstate);
1704    if (inf) {
1705        lua_pushinteger(L, order);
1706        return 2;
1707    } else {
1708        return 1;
1709    }
1710}
1711
1712static int tokenlib_scan_delimited(lua_State *L)
1713{
1714    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1715    halfword left = lua_type(L, 1) == LUA_TNUMBER ? lmt_tohalfword(L, 1) : 0;
1716    halfword right = lua_type(L, 2) == LUA_TNUMBER ? lmt_tohalfword(L, 2) : 0;
1717    int expand = (lua_type(L, 3) == LUA_TBOOLEAN) ? expand = lua_toboolean(L, 3) : 1;
1718    /* Maybe some more? */
1719    if (left) {
1720        left  = token_val(left  == 32 ? spacer_cmd : other_char_cmd, left);
1721    }
1722    if (right) {
1723        right = token_val(right == 32 ? spacer_cmd : other_char_cmd, right);
1724    } else {
1725        /* actually an error as we now get a runaway argument */
1726    }
1727    if (expand) {
1728        tokenlib_aux_goto_first_candidate_x();
1729    } else {
1730        tokenlib_aux_goto_first_candidate();
1731    }
1732    if (! left || cur_tok == left) {
1733        halfword defref = lmt_input_state.def_ref;
1734        halfword result = get_reference_token();
1735        halfword unbalance = 0;
1736        halfword p = result;
1737        lmt_input_state.def_ref = result;
1738        /* */
1739        if (expand) {
1740            /* like scan_toks_expand, maybe use |get_x_or_protected|.  */
1741            if (! left) {
1742                goto INITIAL1; /* ugly but saved a |back_input| */
1743            }
1744            while (1) {
1745              PICKUP:
1746                tex_get_next();
1747              INITIAL1:
1748                switch (cur_cmd) {
1749                    case call_cmd:
1750                    case tolerant_call_cmd:
1751                    case constant_call_cmd:
1752                        tex_expand_current_token();
1753                        goto PICKUP;
1754                    case protected_call_cmd:
1755                    case semi_protected_call_cmd:
1756                    case tolerant_protected_call_cmd:
1757                    case tolerant_semi_protected_call_cmd:
1758                        cur_tok = cs_token_flag + cur_cs;
1759                        goto APPENDTOKEN;
1760                    case the_cmd:
1761                        {
1762                            halfword t = null;
1763                            halfword h = tex_the_toks(cur_chr, &t);
1764                            if (h) {
1765                                set_token_link(p, h);
1766                                p = t;
1767                            }
1768                            goto PICKUP;
1769                        }
1770                    default:
1771                        if (cur_cmd > max_command_cmd) {
1772                            tex_expand_current_token();
1773                            goto PICKUP;
1774                        } else {
1775                            goto DONEEXPANDING;
1776                        }
1777                }
1778              DONEEXPANDING:
1779                tex_x_token();
1780             // if (cur_tok == right) {
1781             //     break;
1782             // } else if (cur_tok < right_brace_limit) {
1783             //  /* if (cur_cmd < right_brace_cmd) { */
1784             //     if (cur_cmd == left_brace_cmd) { // } || cur_cmd == relax_cmd) {
1785             //         ++unbalance;
1786             //     } else if (unbalance) {
1787             //         --unbalance;
1788             //     } else {
1789             //         goto FINALYDONE;
1790             //     }
1791             // }
1792
1793                if (cur_tok == right) {
1794                    break;
1795                } else if (! cur_cs) { 
1796                    switch (cur_cmd) {
1797                        case left_brace_cmd:
1798                            ++unbalance;
1799                            break;
1800                        case right_brace_cmd:
1801                            if (unbalance) {
1802                                --unbalance;
1803                                break;
1804                            } else {
1805                                goto FINALYDONE;
1806                            }
1807                    }
1808                }
1809
1810              APPENDTOKEN:
1811                p = tex_store_new_token(p, cur_tok);
1812            }
1813        } else {
1814            /* like scan_toks_normal */
1815            if (! left) {
1816                goto INITIAL2; /* ugly but saved a |back_input| */
1817            }
1818            while (1) {
1819                tex_get_token();
1820              INITIAL2:
1821             // if (cur_tok == right) {
1822             //     break;
1823             // } else if (cur_tok < right_brace_limit) {
1824             //  /* if (cur_cmd < right_brace_cmd) { */
1825             //     if (cur_cmd == left_brace_cmd) { // } || cur_cmd == relax_cmd) {
1826             //         ++unbalance;
1827             //     } else if (unbalance) {
1828             //         --unbalance;
1829             //     } else {
1830             //         break;
1831             //     }
1832             // }
1833
1834                if (cur_tok == right) {
1835                    break;
1836                } else if (! cur_cs) { 
1837                    switch (cur_cmd) {
1838                        case left_brace_cmd:
1839                            ++unbalance;
1840                            break;
1841                        case right_brace_cmd:
1842                            if (unbalance) {
1843                                --unbalance;
1844                                break;
1845                            } else { 
1846                                goto FINALYDONE;
1847                            }
1848                    }
1849                }
1850
1851                p = tex_store_new_token(p, cur_tok);
1852            }
1853        }
1854      FINALYDONE:
1855        /* */
1856        lmt_input_state.def_ref = defref;
1857        lmt_token_list_to_luastring(L, result, 0, 0, 1);
1858    } else {
1859        tex_back_input(cur_tok);
1860        lua_pushnil(L);
1861    }
1862    tokenlib_aux_unsave_tex_scanner(texstate);
1863    return 1;
1864}
1865
1866static int tokenlib_gobble_until(lua_State *L) /* not ok because we can have different cs's */
1867{
1868    lua_token *left = tokenlib_aux_check_istoken(L, 1);
1869    lua_token *right = tokenlib_aux_check_istoken(L, 2);
1870    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1871    int level = 1;
1872    int l = token_info(left->token);
1873    int r = token_info(right->token);
1874    int cmd, chr, lcmd, lchr, rcmd, rchr;
1875    if (l >= cs_token_flag) {
1876        lcmd = eq_type(l - cs_token_flag);
1877        lchr = eq_value(l - cs_token_flag);
1878    } else {
1879        lcmd = token_cmd(l);
1880        lchr = token_chr(l);
1881    }
1882    if (r >= cs_token_flag) {
1883        rcmd = eq_type(r - cs_token_flag);
1884        rchr = eq_value(r - cs_token_flag);
1885    } else {
1886        rcmd = token_cmd(l);
1887        rchr = token_chr(l);
1888    }
1889    while (1) {
1890        tex_get_token();
1891        if (cur_tok >= cs_token_flag) {
1892            cmd = eq_type(cur_cs);
1893            chr = eq_value(cur_cs);
1894        } else {
1895            cmd = cur_cmd;
1896            chr = cur_chr;
1897        }
1898        if (cmd == lcmd && chr == lchr) {
1899            ++level;
1900        } else if (cmd == rcmd && chr == rchr) {
1901            --level;
1902            if (level == 0) {
1903                break;
1904            }
1905        }
1906    }
1907    tokenlib_aux_unsave_tex_scanner(texstate);
1908    return 0;
1909}
1910
1911/* only csnames, todo: no need for a token list .. make a direct tostring  */
1912
1913static int tokenlib_grab_until(lua_State *L)
1914{
1915    lua_token *left = tokenlib_aux_check_istoken(L, 1);
1916    lua_token *right = tokenlib_aux_check_istoken(L, 2);
1917    int l = token_info(left->token);
1918    int r = token_info(right->token);
1919    int lstr = 0;
1920    int rstr = 0;
1921    if (l >= cs_token_flag) {
1922        lstr = cs_text(l - cs_token_flag);
1923    }
1924    if (r >= cs_token_flag) {
1925        rstr = cs_text(r - cs_token_flag);
1926    }
1927    if (lstr && rstr) {
1928        saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1929        halfword defref = lmt_input_state.def_ref;
1930        halfword result = get_reference_token();
1931        halfword p = result;
1932        int level = 1;
1933        int nospace = lua_toboolean(L, 3);
1934        int strip = lmt_optinteger(L, 4, -1);
1935        while (1) {
1936            tex_get_token();
1937            if (cur_tok >= cs_token_flag) {
1938                int str = cs_text(cur_tok - cs_token_flag);
1939                if (str == lstr) {
1940                    ++level;
1941                } else if (str == rstr) {
1942                    --level;
1943                    if (level == 0) {
1944                        break;
1945                    }
1946                }
1947            }
1948            p = tex_store_new_token(p, cur_tok);
1949        }
1950        tokenlib_aux_unsave_tex_scanner(texstate);
1951        lmt_input_state.def_ref = defref;
1952        lmt_token_list_to_luastring(L, result, nospace, strip, 1);
1953    } else { 
1954        lua_pushnil(L);
1955    }
1956    return 1;
1957}
1958
1959static int tokenlib_scan_word(lua_State *L)
1960{
1961    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1962    tokenlib_aux_goto_first_candidate_x();
1963    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
1964        luaL_Buffer b;
1965        luaL_buffinit(L, &b);
1966        while (1) {
1967            tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
1968            tex_get_x_token();
1969            if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd) {
1970                break;
1971            }
1972        }
1973        if (! (lua_toboolean(L, 1) && ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)))) {
1974            tex_back_input(cur_tok);
1975        }
1976        luaL_pushresult(&b);
1977    } else {
1978        tex_back_input(cur_tok);
1979        lua_pushnil(L);
1980    }
1981    tokenlib_aux_unsave_tex_scanner(texstate);
1982    return 1;
1983}
1984
1985static int tokenlib_scan_letters(lua_State *L)
1986{
1987    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
1988    tokenlib_aux_goto_first_candidate_x();
1989    if (cur_cmd == letter_cmd) {
1990        luaL_Buffer b;
1991        luaL_buffinit(L, &b);
1992        while (1) {
1993            tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
1994            tex_get_x_token();
1995            if (cur_cmd != letter_cmd) {
1996                break ;
1997            }
1998        }
1999        if (! (lua_toboolean(L, 1) && ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)))) {
2000            tex_back_input(cur_tok);
2001        }
2002        luaL_pushresult(&b);
2003    } else {
2004        tex_back_input(cur_tok);
2005        lua_pushnil(L);
2006    }
2007    tokenlib_aux_unsave_tex_scanner(texstate);
2008    return 1;
2009}
2010
2011static int tokenlib_scan_char(lua_State *L)
2012{
2013    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2014    tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
2015    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
2016        int c = lmt_tointeger(L, 1);
2017        if (c == cur_chr) {
2018            lua_pushboolean(L, 1);
2019        } else {
2020            lua_pushboolean(L, 0);
2021            tex_back_input(cur_tok);
2022        }
2023    } else {
2024        lua_pushboolean(L, 0);
2025        tex_back_input(cur_tok);
2026    }
2027    tokenlib_aux_unsave_tex_scanner(texstate);
2028    return 1;
2029}
2030
2031static const char *token_cmd_to_string[15] = { 
2032    "\\", /*  0 escape_cmd        */
2033    "{",  /*  1 left_brace_cmd    */
2034    "}",  /*  2 right_brace_cmd   */
2035    "$",  /*  3 math_shift_cmd    */
2036    "&",  /*  4 alignment_tab_cmd */
2037    "\n", /*  5 end_line_cmd      */
2038    "#",  /*  6 parameter_cmd     */
2039    "^",  /*  7 superscript_cmd   */
2040    "_",  /*  8 subscript_cmd     */
2041    "",   /*  9 ignore_cmd        */
2042    " ",  /* 10 spacer_cmd        */
2043    "",   /* 11 letter_cmd        */
2044    "",   /* 12 other_char_cmd    */
2045    "",   /* 13 active_char_cmd   */
2046    "%"   /* 14 comment_cmd       */
2047};
2048
2049static int tokenlib_scan_next_char(lua_State *L)
2050{
2051    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2052    tex_get_token();
2053    switch (cur_cmd) {
2054        case escape_cmd:
2055        case left_brace_cmd:
2056        case right_brace_cmd:
2057        case math_shift_cmd:
2058        case alignment_tab_cmd:
2059        case end_line_cmd:
2060        case parameter_cmd:
2061        case superscript_cmd:
2062        case subscript_cmd:
2063        case ignore_cmd:
2064        case spacer_cmd:
2065        case comment_cmd:
2066            lua_pushstring(L, token_cmd_to_string[cur_cmd]);
2067            break;
2068        case letter_cmd:
2069        case other_char_cmd:
2070        case active_char_cmd: /* needs testing */
2071            {
2072                char buffer[6];
2073                char *uindex = aux_uni2string((char *) buffer, (unsigned int) cur_chr);
2074                *uindex = '\0';
2075                lua_pushstring(L, buffer);
2076                break;
2077            }
2078        default:
2079            lua_pushstring(L, "");
2080            break;
2081    }
2082    tokenlib_aux_unsave_tex_scanner(texstate);
2083    return 1;
2084}
2085
2086static int tokenlib_is_next_char(lua_State *L)
2087{
2088    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2089    tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
2090    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd ) {
2091        int c = lmt_tointeger(L, 1);
2092        lua_pushboolean(L, c == cur_chr);
2093    } else {
2094        lua_pushboolean(L, 0);
2095    }
2096    tex_back_input(cur_tok);
2097    tokenlib_aux_unsave_tex_scanner(texstate);
2098    return 1;
2099}
2100
2101static int tokenlib_peek_next(lua_State *L)
2102{
2103    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2104    if (lua_toboolean(L, 1)) {
2105        tokenlib_aux_goto_first_candidate();
2106    } else {
2107        tex_get_token();
2108    }
2109 // make_new_token(L, cur_cmd, cur_chr, cur_cs);
2110    tokenlib_aux_make_new_token_tok(L, cur_tok);
2111    tex_back_input(cur_tok);
2112    tokenlib_aux_unsave_tex_scanner(texstate);
2113    return 1;
2114}
2115
2116static int tokenlib_peek_next_expanded(lua_State *L)
2117{
2118    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2119    if (lua_toboolean(L, 1)) {
2120        tokenlib_aux_goto_first_candidate_x();
2121    } else {
2122        tex_get_x_token();
2123    }
2124 // make_new_token(L, cur_cmd, cur_chr, cur_cs);
2125    tokenlib_aux_make_new_token_tok(L, cur_tok);
2126    tex_back_input(cur_tok);
2127    tokenlib_aux_unsave_tex_scanner(texstate);
2128    return 1;
2129}
2130
2131static int tokenlib_peek_next_char(lua_State *L)
2132{
2133    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2134    tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
2135    if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd ) {
2136        lua_pushinteger(L, cur_chr);
2137    } else {
2138        lua_pushnil(L);
2139    }
2140    tex_back_input(cur_tok);
2141    tokenlib_aux_unsave_tex_scanner(texstate);
2142    return 1;
2143}
2144
2145/*tex
2146
2147    This next two are experimental and might evolve. It will take a while before
2148    I decide if this is the way to go. They are not used in critical code so we
2149    have all time of the world.
2150
2151*/
2152
2153static int tokenlib_scan_key(lua_State *L)
2154{
2155    int c1 = lmt_optinteger(L, 1, '\0');
2156    int c2 = lmt_optinteger(L, 2, '\0');
2157    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2158    tokenlib_aux_goto_first_candidate_x();
2159    if ((cur_cmd == letter_cmd || cur_cmd == other_char_cmd) && (cur_chr != c1) && (cur_chr != c2)) {
2160        luaL_Buffer b;
2161        luaL_buffinit(L, &b);
2162        while (1) {
2163            tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
2164            tex_get_x_token();
2165            if ((cur_cmd != letter_cmd && cur_cmd != other_char_cmd) || (cur_chr == c1) || (cur_chr == c2)) {
2166                break ;
2167            }
2168        }
2169        /*
2170        if (! (lua_toboolean(L, 1) && (cur_cmd == spacer_cmd || cur_cmd == relax_cmd))) {
2171            back_input(cur_tok);
2172        }
2173        */
2174        tex_back_input(cur_tok);
2175        luaL_pushresult(&b);
2176    } else {
2177        tex_back_input(cur_tok);
2178        lua_pushnil(L);
2179    }
2180    tokenlib_aux_unsave_tex_scanner(texstate);
2181    return 1;
2182}
2183
2184/* todo: other call_cmd */
2185/* todo: non expandable option */
2186
2187static int tokenlib_scan_value(lua_State *L)
2188{
2189    /*tex can be simplified, no need for intermediate list */
2190    int c1 = lmt_optinteger(L, 1, '\0');
2191    int c2 = lmt_optinteger(L, 2, '\0');
2192    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2193    tokenlib_aux_goto_first_candidate_x(); /* no _x */
2194    switch (cur_cmd) {
2195        case left_brace_cmd:
2196            {
2197                halfword result;
2198                halfword defref = lmt_input_state.def_ref;
2199                result = tex_scan_toks_expand(1, NULL, 0, 0);
2200                lmt_input_state.def_ref = defref;
2201                lmt_token_list_to_luastring(L, result, 0, 0, 1);
2202            }
2203            break;
2204        /*
2205        case call_cmd:
2206        case constant_call_cmd:
2207            {
2208                halfword t = cur_cs ? cs_token_flag + cur_cs : token_val(cur_cmd, cur_chr);
2209                if (t >= cs_token_flag) {
2210                    unsigned char *s = get_cs_text(t - cs_token_flag);
2211                   if (s) {
2212                     // if (is_active_cs(cs_text(t - cs_token_flag))) {
2213                        luaL_Buffer b;
2214                        luaL_buffinit(L, &b);
2215                        cs_name_to_buffer(s);
2216                        luaL_pushresult(&b);
2217                        lmt_memory_free(s);
2218                    } else {
2219                        lua_pushnil(L);
2220                    }
2221                } else {
2222                    lua_pushnil(L);
2223                }
2224            }
2225            break;
2226        */
2227        case letter_cmd:
2228        case other_char_cmd:
2229            {
2230                luaL_Buffer b;
2231                luaL_buffinit(L, &b);
2232                while (1) {
2233                    switch (cur_cmd) {
2234                        case left_brace_cmd:
2235                            {
2236                                halfword result;
2237                                halfword defref = lmt_input_state.def_ref;
2238                                result = tex_scan_toks_expand(1, NULL, 0, 0);
2239                                lmt_input_state.def_ref = defref;
2240                                lmt_token_list_to_luastring(L, result, 0, 0, 1);
2241                                luaL_addchar(&b, '{');
2242                                luaL_addvalue(&b);
2243                                luaL_addchar(&b, '}');
2244                            }
2245                            break;
2246                        case call_cmd:
2247                        case protected_call_cmd:
2248                        case semi_protected_call_cmd:
2249                        case constant_call_cmd:
2250                        case tolerant_call_cmd:
2251                        case tolerant_protected_call_cmd:
2252                        case tolerant_semi_protected_call_cmd:
2253                            {
2254                                /*tex We need to add a space. */
2255                                halfword t = cur_cs ? cs_token_flag + cur_cs : token_val(cur_cmd, cur_chr);
2256                                if (t >= cs_token_flag) {
2257                                    int allocated = 0;
2258                                    unsigned char *s = tokenlib_aux_get_cs_text(t - cs_token_flag, &allocated);
2259                                    if (s) {
2260                                        if (tex_is_active_cs(cs_text(t - cs_token_flag))) {
2261                                            lua_pushstring(L, (char *) (s + 3));
2262                                            luaL_addvalue(&b);
2263                                        } else {
2264                                            luaL_addchar(&b, '\\');
2265                                            lua_pushstring(L, (char *) s);
2266                                            luaL_addvalue(&b);
2267                                            luaL_addchar(&b, ' ');
2268                                        }
2269                                        if (allocated) {
2270                                            lmt_memory_free(s);
2271                                        }
2272                                    }
2273                                }
2274                            }
2275                            break;
2276                        case letter_cmd:
2277                        case other_char_cmd:
2278                            if (cur_chr == c1 || cur_chr == c2) {
2279                                goto DONE;
2280                            } else {
2281                                tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
2282                            }
2283                            break;
2284                        default:
2285                            /* what to do */
2286                            tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
2287                            break;
2288                    }
2289                    tex_get_x_token();
2290                }
2291              DONE:
2292                tex_back_input(cur_tok);
2293                luaL_pushresult(&b);
2294            }
2295            break;
2296        default:
2297            {
2298                tex_back_input(cur_tok);
2299                lua_pushnil(L);
2300            }
2301            break;
2302    }
2303    tokenlib_aux_unsave_tex_scanner(texstate);
2304    return 1;
2305}
2306
2307/*tex Till here. */
2308
2309static int tokenlib_future_expand(lua_State *L)
2310{
2311    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2312    halfword spa = null;
2313    halfword yes = tex_get_token(); /* no expansion */
2314    halfword nop = tex_get_token(); /* no expansion */
2315    while (1) {
2316        halfword t = tex_get_token();
2317        switch (t) {
2318            case spacer_cmd:
2319                spa = t; /* preserves spaces */
2320                break;
2321            case letter_cmd:
2322            case other_char_cmd:
2323                if (lua_tointeger(L, 1) == cur_chr) {
2324                    tex_back_input(t);
2325                    tex_back_input(yes);
2326                    tokenlib_aux_unsave_tex_scanner(texstate);
2327                    return 0;
2328                }
2329            default:
2330                tex_back_input(t);
2331                if (spa && lua_toboolean(L, 2)) {
2332                    tex_back_input(spa);
2333                }
2334                tex_back_input(nop);
2335                tokenlib_aux_unsave_tex_scanner(texstate);
2336                return 0;
2337        }
2338    }
2339 // return 0;
2340}
2341
2342static int tokenlib_scan_code(lua_State *L)
2343{
2344    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2345    tex_get_x_token();
2346    if (cur_cmd <= max_char_code_cmd) {
2347        int cc = lmt_optinteger(L, 1, DEFAULT_SCAN_CODE_SET);
2348        if (cc & (1 << (cur_cmd))) {
2349            lua_pushinteger(L, (int) cur_chr);
2350        } else {
2351            lua_pushnil(L);
2352            tex_back_input(cur_tok);
2353        }
2354    } else {
2355        lua_pushnil(L);
2356        tex_back_input(cur_tok);
2357    }
2358    tokenlib_aux_unsave_tex_scanner(texstate);
2359    return 1;
2360}
2361
2362static int tokenlib_scan_token_code(lua_State *L)
2363{
2364    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
2365    halfword t = tex_get_token();
2366    /* maybe treat spaces as such */
2367    if (cur_cmd <= max_char_code_cmd) {
2368        if (DEFAULT_SCAN_CODE_SET & (1 << (cur_cmd))) {
2369            lua_pushinteger(L, (int) cur_chr);
2370        } else {
2371            lua_pushnil(L);
2372            tex_back_input(t);
2373        }
2374    } else {
2375        lua_pushnil(L);
2376        tex_back_input(t);
2377    }
2378    tokenlib_aux_unsave_tex_scanner(texstate);
2379    return 1;
2380}
2381
2382static int tokenlib_is_token(lua_State *L)
2383{
2384    lua_pushboolean(L, tokenlib_aux_maybe_istoken(L, 1) ? 1 : 0);
2385    return 1;
2386}
2387
2388static int tokenlib_expand(lua_State *L)
2389{
2390    (void) L;
2391    tex_expand_current_token();
2392    /* should we push back? */
2393    return 0;
2394}
2395
2396static int tokenlib_is_defined(lua_State *L)
2397{
2398    int b = 0;
2399    if (lua_type(L, 1) == LUA_TSTRING) {
2400        size_t l;
2401        const char *s = lua_tolstring(L, 1, &l);
2402        if (l > 0) {
2403            int cs = tex_string_locate_only(s, l);
2404            b = (cs != undefined_control_sequence) && (eq_type(cs) != undefined_cs_cmd);
2405        }
2406    }
2407    lua_pushboolean(L, b);
2408    return 1;
2409}
2410
2411/*tex
2412    The next two will be redone so that they check if valid tokens are created. For that I need to
2413    clean up the \TEX\ end a bit more so that we can do proper cmd checking.
2414*/
2415
2416static int tokenlib_create(lua_State *L)
2417{
2418    switch (lua_type(L, 1)) {
2419        case LUA_TNUMBER:
2420            {
2421                int cs = 0;
2422                int chr = (int) lua_tointeger(L, 1);
2423                int cmd = (int) luaL_optinteger(L, 2, tex_get_cat_code(cat_code_table_par, chr));
2424                switch (cmd) {
2425                    case escape_cmd:
2426                    case ignore_cmd:
2427                    case comment_cmd:
2428                    case invalid_char_cmd:
2429                     /* tex_formatted_warning("token lib","not a good token, catcode %i can not be returned, so 12 will be used",(int) cmd); */
2430                        cmd = other_char_cmd;
2431                        break;
2432                    case active_char_cmd:
2433                        cs = tex_active_to_cs(chr, ! lmt_hash_state.no_new_cs);
2434                        cmd = eq_type(cs);
2435                        chr = eq_value(cs);
2436                        break;
2437                }
2438                tokenlib_aux_make_new_token(L, cmd, chr, cs);
2439                break;
2440            }
2441        case LUA_TSTRING:
2442            {
2443                size_t l;
2444                const char *s = lua_tolstring(L, 1, &l);
2445                if (l > 0) {
2446                    int cs = tex_string_locate(s, l, lua_toboolean(L, 2));
2447                    int cmd = eq_type(cs);
2448                    int chr = eq_value(cs);
2449                    tokenlib_aux_make_new_token(L, cmd, chr, cs);
2450                } else {
2451                    lua_pushnil(L);
2452                }
2453                break;
2454            }
2455        default:
2456            {
2457                lua_pushnil(L);
2458                break;
2459            }
2460    }
2461    return 1;
2462}
2463
2464/*tex
2465    The order of arguments is somewhat strange but it comes from \LUATEX.
2466*/
2467
2468static int tokenlib_new(lua_State *L)
2469{
2470    int chr = 0;
2471    int cmd = 0;
2472    switch (lua_type(L, 1)) {
2473        case LUA_TSTRING:
2474            cmd = (int) tokenlib_aux_get_command_id(lua_tostring(L, 1));
2475            chr = (int) luaL_optinteger(L, 2, 0);
2476            break;
2477        case LUA_TNUMBER:
2478            chr = (int) lua_tointeger(L, 1);
2479            cmd = (int) luaL_optinteger(L, 2, 0);
2480            break;
2481        default:
2482            break;
2483    }
2484    tokenlib_aux_make_new_token(L, cmd, chr, 0);
2485    return 1;
2486}
2487
2488/*tex
2489    The next few are more test functions and at some point they will replace the above or at least
2490    be combined so that we do proper checking.
2491*/
2492
2493static int tokenlib_get_cmdchrcs(lua_State* L)
2494{
2495    size_t l;
2496    const char *s = lua_tolstring(L, 1, &l);
2497    if (l > 0) {
2498        int cs = tex_string_locate_only(s, l);
2499        int cmd = eq_type(cs);
2500        int chr = eq_value(cs);
2501        if (! lua_toboolean(L, 2)) {
2502            /*tex This option is only for diagnostics! */
2503            chr = tokenlib_aux_to_valid_index(cmd, chr);
2504        }
2505        lua_pushinteger(L, cmd);
2506        lua_pushinteger(L, chr); /* or index */
2507        lua_pushinteger(L, cs);
2508        return 3;
2509    }
2510    return 0;
2511}
2512
2513static int tokenlib_scan_cmdchr(lua_State *L)
2514{
2515    int cmd, chr;
2516    halfword tok = tex_get_token();
2517    if (tok >= cs_token_flag) {
2518        tok -= cs_token_flag;
2519        cmd = eq_type(tok);
2520        chr = eq_value(tok);
2521    } else {
2522        cmd = token_cmd(tok);
2523        chr = token_chr(tok);
2524    }
2525    lua_pushinteger(L, cmd);
2526    lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
2527    return 2;
2528}
2529
2530static int tokenlib_scan_cmdchr_expanded(lua_State *L)
2531{
2532    int cmd, chr;
2533    halfword tok = tex_get_x_token();
2534    if (tok >= cs_token_flag) {
2535        tok -= cs_token_flag;
2536        cmd = eq_type(tok);
2537        chr = eq_value(tok);
2538    } else {
2539        cmd = token_cmd(tok);
2540        chr = token_chr(tok);
2541    }
2542    lua_pushinteger(L, cmd);
2543    lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
2544    return 2;
2545}
2546
2547
2548static int tokenlib_get_cstoken(lua_State* L)
2549{
2550    size_t l;
2551    const char *s = lua_tolstring(L, 1, &l);
2552    if (l > 0) {
2553        lua_pushinteger(L, (lua_Integer) tex_string_locate_only(s, l) + cs_token_flag);
2554        return 1;
2555    }
2556    return 0;
2557}
2558
2559static int tokenlib_getprimitives(lua_State *L)
2560{
2561    int cs = 0;
2562    int nt = 0;
2563    int raw = lua_toboolean(L, 1);
2564    lua_createtable(L, prim_size, 0);
2565    while (cs < prim_size) {
2566        strnumber s = get_prim_text(cs);
2567        if (s > 0 && (get_prim_origin(cs) != no_command)) {
2568            const char *ss = tex_to_cstring(s);
2569            int cmd = prim_eq_type(cs);
2570            int chr = prim_equiv(cs);
2571            if (! raw) {
2572                chr = tokenlib_aux_to_valid_index(cmd, chr);
2573            }
2574            lua_createtable(L, 4, 0);
2575            lua_pushinteger(L, cmd);
2576            lua_rawseti(L, -2, 1);
2577            lua_pushinteger(L, chr);
2578            lua_rawseti(L, -2, 2);
2579            lua_pushstring(L, ss);
2580            lua_rawseti(L, -2, 3);
2581            lua_pushinteger(L, prim_origin(cs));
2582            lua_rawseti(L, -2, 4);
2583            lua_rawseti(L, -2, ++nt);
2584        }
2585        cs++;
2586    }
2587    return 1;
2588}
2589
2590/*tex token instance functions */
2591
2592static int tokenlib_free(lua_State *L)
2593{
2594 /* lua_token *n = check_istoken(L, 1); */
2595    lua_token *n = lua_touserdata(L, 1);
2596    if (n->origin == token_origin_lua) {
2597        if (token_link(n->token)) {
2598            tex_flush_token_list(n->token);
2599        } else {
2600            tex_put_available_token(n->token);
2601        }
2602    } else {
2603        /*tex This can't happen (yet). */
2604    }
2605    return 1;
2606}
2607
2608/*tex fast accessors */
2609
2610inline static int tokenlib_get_command(lua_State *L)
2611{
2612    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2613    halfword t = token_info(n->token);
2614    lua_pushinteger(L, (t >= cs_token_flag) ? (int) eq_type(t - cs_token_flag) : token_cmd(t));
2615    return 1;
2616}
2617
2618inline static int tokenlib_get_index(lua_State *L)
2619{
2620    int cmd, chr;
2621    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2622    halfword tok = token_info(n->token);
2623    if (tok >= cs_token_flag) {
2624        tok -= cs_token_flag;
2625        cmd = eq_type(tok);
2626        chr = eq_value(tok);
2627    } else {
2628        cmd = token_cmd(tok);
2629        chr = token_chr(tok);
2630    }
2631    lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
2632    return 1;
2633}
2634
2635inline static int tokenlib_get_range(lua_State *L)
2636{
2637    int cmd;
2638    if (lua_type(L, 1) == LUA_TNUMBER) {
2639        cmd = (int) lua_tointeger(L, 1);
2640    } else {
2641        lua_token *n = tokenlib_aux_check_istoken(L, 1);
2642        halfword tok = token_info(n->token);
2643        cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2644    }
2645    if (cmd >= 0 && cmd <= last_cmd) {
2646        command_item item = lmt_interface.command_names[cmd];
2647        lua_pushinteger(L, item.kind);
2648        switch (item.kind) {
2649            case unused_command_item:
2650                lua_pushboolean(L, 0);
2651                lua_pushboolean(L, 0);
2652                break;
2653            case regular_command_item:
2654            case character_command_item:
2655            case register_command_item:
2656            case internal_command_item:
2657            case reference_command_item:
2658            case data_command_item:
2659                lua_pushinteger(L, item.min);
2660                lua_pushinteger(L, item.max);
2661                break;
2662            case token_command_item:
2663            case node_command_item:
2664                lua_pushboolean(L, 0);
2665                lua_pushboolean(L, 0);
2666                break;
2667        }
2668        lua_pushinteger(L, item.fixedvalue);
2669        return 4;
2670    } else {
2671        return 0;
2672    }
2673}
2674
2675inline static int tokenlib_get_cmdname(lua_State *L)
2676{
2677    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2678    halfword tok = token_info(n->token);
2679    int cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2680    lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
2681    return 1;
2682}
2683
2684void lmt_push_cmd_name(lua_State *L, int cmd)
2685{
2686    if (cmd >= 0) {
2687        lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
2688    } else {
2689        lua_pushnil(L);
2690    }
2691}
2692
2693inline static int tokenlib_get_csname(lua_State *L)
2694{
2695    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2696    halfword tok = token_info(n->token);
2697    if (tok >= cs_token_flag) {
2698        int allocated = 0;
2699        unsigned char *s = tokenlib_aux_get_cs_text(tok - cs_token_flag, &allocated);
2700        if (s) {
2701            if (tex_is_active_cs(cs_text(tok - cs_token_flag))) {
2702                lua_pushstring(L, (char *) (s + 3));
2703            } else {
2704                lua_pushstring(L, (char *) s);
2705            }
2706            if (allocated) {
2707                lmt_memory_free(s);
2708            }
2709            return 1;
2710        }
2711    }
2712    lua_pushnil(L);
2713    return 1;
2714}
2715
2716inline static int tokenlib_get_id(lua_State *L)
2717{
2718    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2719    lua_pushinteger(L, n->token);
2720    return 1;
2721}
2722
2723inline static int tokenlib_get_tok(lua_State *L)
2724{
2725    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2726    halfword tok = token_info(n->token);
2727    lua_pushinteger(L, tok);
2728    return 1;
2729}
2730
2731inline static int tokenlib_get_active(lua_State *L)
2732{
2733    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2734    halfword tok = token_info(n->token);
2735    int result = 0;
2736    if (tok >= cs_token_flag) {
2737        int allocated = 0;
2738        unsigned char *s = tokenlib_aux_get_cs_text(tok - cs_token_flag, &allocated);
2739        if (s) {
2740            result = tex_is_active_cs(cs_text(tok - cs_token_flag));
2741            if (allocated) {
2742                lmt_memory_free(s);
2743            }
2744        }
2745    }
2746    lua_pushboolean(L, result);
2747    return 1;
2748}
2749
2750inline static int tokenlib_get_expandable(lua_State *L)
2751{
2752    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2753    halfword tok = token_info(n->token);
2754    halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2755    lua_pushboolean(L, cmd > max_command_cmd);
2756    return 1;
2757}
2758
2759inline static int tokenlib_get_protected(lua_State *L)
2760{
2761    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2762    halfword tok = token_info(n->token);
2763    halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2764    lua_pushboolean(L, is_protected_cmd(cmd));
2765    return 1;
2766}
2767
2768inline static int tokenlib_get_semiprotected(lua_State *L)
2769{
2770    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2771    halfword tok = token_info(n->token);
2772    halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2773    lua_pushboolean(L, is_semi_protected_cmd(cmd));
2774    return 1;
2775}
2776inline static int tokenlib_get_tolerant(lua_State *L)
2777{
2778    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2779    halfword tok = token_info(n->token);
2780    halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
2781    lua_pushboolean(L, is_tolerant_cmd(cmd));
2782    return 1;
2783}
2784
2785inline static int tokenlib_get_noaligned(lua_State *L)
2786{
2787    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2788    halfword tok = token_info(n->token);
2789    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, noaligned_flag_bit));
2790    return 1;
2791}
2792
2793inline static int tokenlib_get_primitive(lua_State *L)
2794{
2795    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2796    halfword tok = token_info(n->token);
2797    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, primitive_flag_bit));
2798    return 1;
2799}
2800
2801inline static int tokenlib_get_permanent(lua_State *L)
2802{
2803    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2804    halfword tok = token_info(n->token);
2805    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, permanent_flag_bit));
2806    return 1;
2807}
2808
2809inline static int tokenlib_get_immutable(lua_State *L)
2810{
2811    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2812    halfword tok = token_info(n->token);
2813    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, immutable_flag_bit));
2814    return 1;
2815}
2816
2817inline static int tokenlib_get_mutable(lua_State *L)
2818{
2819    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2820    halfword tok = token_info(n->token);
2821    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, mutable_flag_bit));
2822    return 1;
2823}
2824
2825inline static int tokenlib_get_frozen(lua_State *L)
2826{
2827    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2828    halfword tok = token_info(n->token);
2829    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, frozen_flag_bit));
2830    return 1;
2831}
2832
2833inline static int tokenlib_get_instance(lua_State *L)
2834{
2835    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2836    halfword tok = token_info(n->token);
2837    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, instance_flag_bit));
2838    return 1;
2839}
2840
2841
2842inline static int tokenlib_get_untraced(lua_State *L)
2843{
2844    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2845    halfword tok = token_info(n->token);
2846    lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, untraced_flag_bit));
2847    return 1;
2848}
2849
2850
2851inline static int tokenlib_get_flags(lua_State *L)
2852{
2853    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2854    halfword tok = token_info(n->token);
2855    lua_pushboolean(L, tok >= cs_token_flag ? eq_flag(tok - cs_token_flag) : 0);
2856    return 1;
2857}
2858
2859inline static int tokenlib_get_parameters(lua_State *L)
2860{
2861    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2862    halfword tok = token_info(n->token);
2863    if (tok >= cs_token_flag && is_call_cmd(eq_type(tok - cs_token_flag))) {
2864        halfword v = eq_value(tok - cs_token_flag);
2865        if (v && token_link(v)) {
2866            lua_pushinteger(L, get_token_parameters(v));
2867            return 1;
2868        }
2869    }
2870    lua_pushnil(L);
2871    return 0;
2872}
2873
2874inline static int tokenlib_get_constant(lua_State *L)
2875{
2876    lua_token *n = tokenlib_aux_check_istoken(L, 1);
2877    halfword tok = token_info(n->token);
2878    lua_pushboolean(L, tok >= cs_token_flag && (eq_type(tok - cs_token_flag) == constant_call_cmd));
2879    return 1;
2880}
2881
2882static int tokenlib_getfield(lua_State *L)
2883{
2884    const char *s = lua_tostring(L, 2);
2885    if (lua_key_eq(s, command)) {
2886        return tokenlib_get_command(L);
2887    } else if (lua_key_eq(s, index)) {
2888        return tokenlib_get_index(L);
2889    } else if (lua_key_eq(s, cmdname)) {
2890        return tokenlib_get_cmdname(L);
2891    } else if (lua_key_eq(s, csname)) {
2892        return tokenlib_get_csname(L);
2893    } else if (lua_key_eq(s, id)) {
2894        return tokenlib_get_id(L);
2895    } else if (lua_key_eq(s, tok)) {
2896        return tokenlib_get_tok(L);
2897    } else if (lua_key_eq(s, active)) {
2898        return tokenlib_get_active(L);
2899    } else if (lua_key_eq(s, expandable)) {
2900        return tokenlib_get_expandable(L);
2901    } else if (lua_key_eq(s, protected)) {
2902        return tokenlib_get_protected(L);
2903    } else if (lua_key_eq(s, semiprotected)) {
2904        return tokenlib_get_semiprotected(L);
2905    } else if (lua_key_eq(s, frozen)) {
2906        return tokenlib_get_frozen(L);
2907    } else if (lua_key_eq(s, tolerant)) {
2908        return tokenlib_get_tolerant(L);
2909    } else if (lua_key_eq(s, noaligned)) {
2910        return tokenlib_get_noaligned(L);
2911    } else if (lua_key_eq(s, permanent)) {
2912        return tokenlib_get_permanent(L);
2913    } else if (lua_key_eq(s, immutable)) {
2914        return tokenlib_get_immutable(L);
2915    } else if (lua_key_eq(s, mutable)) {
2916        return tokenlib_get_mutable(L);
2917    } else if (lua_key_eq(s, primitive)) {
2918        return tokenlib_get_primitive(L);
2919    } else if (lua_key_eq(s, instance)) {
2920        return tokenlib_get_instance(L);
2921    } else if (lua_key_eq(s, untraced)) {
2922        return tokenlib_get_untraced(L);
2923    } else if (lua_key_eq(s, flags)) {
2924        return tokenlib_get_flags(L);
2925    } else if (lua_key_eq(s, parameters)) {
2926        return tokenlib_get_parameters(L);
2927    } else if (lua_key_eq(s, constant)) {
2928        return tokenlib_get_constant(L);
2929    } else {
2930        lua_pushnil(L);
2931    }
2932    return 1;
2933}
2934
2935static int tokenlib_get_fields(lua_State *L)
2936{
2937    halfword cmd = null;
2938    halfword chr = null;
2939    int flags = 0;
2940    int onlyflags = lua_toboolean(L, 2);
2941    switch (lua_type(L, 1)) {
2942        case LUA_TSTRING:
2943            {
2944                size_t l;
2945                const char *str = lua_tolstring(L, 1, &l);
2946                if (l > 0) {
2947                    halfword cs; 
2948                    lua_createtable(L, 0, onlyflags ? 0 : 5);
2949                    cs = tex_string_locate_only(str, l);
2950                    cmd = eq_type(cs);
2951                    chr = eq_value(cs);
2952                    flags = eq_flag(cs);
2953                    if (! onlyflags) {
2954                        lua_push_key(csname);
2955                        lua_pushstring(L, str);
2956                        lua_rawset(L, -3);
2957                    }
2958                    break;
2959                } else {
2960                    return 0;
2961                }
2962            }
2963        case LUA_TUSERDATA:
2964            {
2965                lua_token *n = tokenlib_aux_check_istoken(L, 1);
2966                halfword tok = token_info(n->token);
2967                lua_createtable(L, 0, onlyflags ? 0 : 5);
2968                if (tok >= cs_token_flag) {
2969                    int t = tok - cs_token_flag;
2970                    int allocated = 0;
2971                    unsigned char* str = tokenlib_aux_get_cs_text(t, &allocated);
2972                    if (str) {
2973                        if (! onlyflags) {
2974                            lua_push_key(csname);
2975                            if (tex_is_active_cs(cs_text(t))) {
2976                                lua_push_key(active);
2977                                lua_pushboolean(L, 1);
2978                                lua_rawset(L, -3);
2979                                lua_pushstring(L, (char*) (str + 3));
2980                            } else {
2981                                lua_pushstring(L, (char*) str);
2982                            }
2983                            lua_rawset(L, -3);
2984                        }
2985                        if (allocated) {
2986                            lmt_memory_free(str);
2987                        }
2988                    }
2989                    cmd = eq_type(t);
2990                    chr = eq_value(t);
2991                } else {
2992                    cmd = token_cmd(tok);
2993                    chr = token_chr(tok);
2994                }
2995                break;
2996            }
2997        default:
2998            return 0;
2999
3000    }
3001    if (flags) {
3002        if (is_frozen   (flags))   { lua_push_key(frozen);        lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3003        if (is_noaligned(flags))   { lua_push_key(noaligned);     lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3004        if (is_permanent(flags))   { lua_push_key(permanent);     lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3005        if (is_immutable(flags))   { lua_push_key(immutable);     lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3006        if (is_mutable  (flags))   { lua_push_key(mutable);       lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3007        if (is_primitive(flags))   { lua_push_key(primitive);     lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3008        if (is_instance (flags))   { lua_push_key(instance);      lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3009        if (is_untraced (flags))   { lua_push_key(untraced);      lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3010        if (flags)                 { lua_push_key(flags);         lua_pushinteger(L, flags); lua_rawset(L, -3); }
3011        if (is_protected(cmd))     { lua_push_key(protected);     lua_pushboolean(L, 1);     lua_rawset(L, -3); } else {
3012        if (is_semiprotected(cmd)) { lua_push_key(semiprotected); lua_pushboolean(L, 1);     lua_rawset(L, -3); } }
3013        if (is_tolerant (cmd))     { lua_push_key(tolerant);      lua_pushboolean(L, 1);     lua_rawset(L, -3); }
3014    }
3015    if (! onlyflags) {
3016        lua_push_key(command);
3017        lua_pushinteger(L, cmd);
3018        lua_rawset(L, -3);
3019        lua_push_key(cmdname);
3020        lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
3021        lua_rawset(L, -3);
3022        lua_push_key(index); /* or value */
3023        lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
3024        lua_rawset(L, -3);
3025        if (is_call_cmd(cmd) && chr && token_link(chr)) {
3026            lua_push_key(parameters);
3027            lua_pushinteger(L, get_token_parameters(token_link(chr)));
3028            lua_rawset(L, -3);
3029        }
3030    }
3031    return 1;
3032}
3033
3034/*tex end */
3035
3036static int tokenlib_equal(lua_State* L)
3037{
3038    lua_token* n = tokenlib_aux_check_istoken(L, 1);
3039    lua_token* m = tokenlib_aux_check_istoken(L, 2);
3040    lua_pushboolean(L, token_info(n->token) == token_info(m->token));
3041    return 1;
3042}
3043
3044static int tokenlib_tostring(lua_State* L)
3045{
3046    lua_token* n = tokenlib_aux_maybe_istoken(L, 1);
3047    if (n) {
3048        halfword id = n->token;
3049        halfword tok = token_info(id);
3050        halfword lnk = token_link(id);
3051        char* ori = (n->origin == token_origin_lua) ? "lua" : "tex";
3052        halfword cmd, chr;
3053        unsigned char* csn = NULL;
3054        unsigned char* csp = NULL;
3055        const char* cmn = NULL;
3056        if (tok >= cs_token_flag) {
3057            int allocated = 0;
3058            tok -= cs_token_flag;
3059            csn = tokenlib_aux_get_cs_text(tok, &allocated);
3060            if (allocated) {
3061                csp = csn;
3062            }
3063            if (csn && tex_is_active_cs(cs_text(tok))) {
3064                csn += 3;
3065            }
3066            cmd = eq_type(tok);
3067            chr = eq_value(tok);
3068        } else {
3069            cmd = token_cmd(tok);
3070            chr = token_chr(tok);
3071        }
3072        if (! cmn) {
3073            if (cmd >= first_cmd && cmd <= last_cmd) {
3074                cmn = lmt_interface.command_names[cmd].name;
3075                switch (lmt_interface.command_names[cmd].base) {
3076                    case ignore_entry:
3077                    case direct_entry:
3078                        break;
3079                    default:
3080                        chr -= lmt_interface.command_names[cmd].base;
3081                }
3082            } else {
3083                cmn = "bad_token";
3084            }
3085        }
3086        if (csn && csn[0] != '\0') {
3087            if (lnk) {
3088                lua_pushfstring(L, "<%s token : %d => %d : %s : %s %d>", ori, id, lnk, (char *) csn, cmn, chr);
3089            } else {
3090                lua_pushfstring(L, "<%s token : %d == %s : %s %d>", ori, id, (char *) csn, cmn, chr);
3091            }
3092        } else {
3093            if (! lnk) {
3094                lua_pushfstring(L, "<%s token : %d == %s %d>", ori, id, cmn, chr);
3095            } else if (cmd == 0 && chr == 0) {
3096                /*tex A zero escape token is less likely than an initial list refcount token. */
3097                lua_pushfstring(L, "<%s token : %d => %d : refcount>", ori, id, lnk);
3098            } else {
3099                lua_pushfstring(L, "<%s token : %d => %d : %s %d>", ori, id, lnk, cmn, chr);
3100            }
3101        }
3102        if (csp) {
3103            lmt_memory_free(csp);
3104        }
3105    } else {
3106        lua_pushnil(L);
3107    }
3108    return 1;
3109}
3110
3111static int tokenlib_package_tostring(lua_State *L)
3112{
3113    lua_token_package *n = tokenlib_aux_check_ispackage(L, 1);
3114    if (n) {
3115        if (is_call_cmd(n->cmd)) {
3116            lua_pushfstring(L, "<tex token package %d: %d %d %d>", n->cs, n->cmd, n->chr, get_token_reference(n->chr));
3117        } else {
3118            lua_pushfstring(L, "<tex token package %d: %d %d>", n->cs, n->cmd, n->chr);
3119        }
3120        return 1;
3121    } else {
3122        return 0;
3123    }
3124}
3125
3126static int tokenlib_type(lua_State *L)
3127{
3128    if (tokenlib_aux_maybe_istoken(L, 1)) {
3129        lua_push_key(token);
3130    } else {
3131        lua_pushnil(L);
3132    }
3133    return 1;
3134}
3135
3136static int tokenlib_scan_token(lua_State *L) /*tex Similer to |get_next_expanded|, expands and no skips. */
3137{
3138    saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
3139    tex_get_x_token();
3140 // make_new_token(L, cur_cmd, cur_chr, cur_cs);
3141    tokenlib_aux_make_new_token_tok(L, cur_tok);
3142    tokenlib_aux_unsave_tex_scanner(texstate);
3143    return 1;
3144}
3145
3146/*tex This is always a copy! */
3147
3148static int tokenlib_scan_box(lua_State *L)
3149{
3150    saved_tex_scanner texstate;
3151    if (lua_gettop(L) > 0) {
3152        const char *s = lua_tostring(L, 1);
3153        halfword code = -1 ;
3154        if (lua_key_eq(s, hbox)) {
3155            code = hbox_code;
3156        } else if (lua_key_eq(s, vbox)) {
3157            code = vbox_code;
3158        } else if (lua_key_eq(s, vtop)) {
3159            code = vtop_code;
3160        } else if (lua_key_eq(s, dbox)) {
3161            code = dbox_code;
3162        }
3163        if (code >= 0) {
3164            tex_back_input(token_val(make_box_cmd, code));
3165        }
3166    }
3167    /*tex
3168        This is a tricky call as we are in \LUA\ and therefore mess with the main loop.
3169    */
3170    texstate = tokenlib_aux_save_tex_scanner();
3171    lmt_push_node_fast(L, tex_local_scan_box());
3172    tokenlib_aux_unsave_tex_scanner(texstate);
3173    return 1;
3174}
3175
3176/* experiment */
3177
3178/* [catcodetable] csname content        : \def\csname{content}  */
3179/* [catcodetable] csname content global : \gdef\csname{content} */
3180/* [catcodetable] csname                : \def\csname{}         */
3181
3182/* TODO: check for a quick way to set a macro to empty (HH) */
3183
3184static int tokenlib_get_meaning(lua_State *L)
3185{
3186    if (lua_type(L, 1) == LUA_TSTRING) {
3187        size_t lname = 0;
3188        const char *name = lua_tolstring(L, 1, &lname);
3189        halfword cs = tex_string_locate_only(name, lname);
3190        halfword cmd = eq_type(cs);
3191        if (is_call_cmd(cmd)) {
3192            int chr = eq_value(cs);
3193            if (lua_toboolean(L, 2)) {
3194                if (lua_toboolean(L, 3)) {
3195                    lmt_token_list_to_lua(L, token_link(chr)); /* makes table sub tables */
3196                } else {
3197                    int originals = lua_toboolean(L, 4);
3198                    lmt_token_register_to_lua(L, chr, originals); /* makes table */
3199                    if (originals) {
3200                        return 2;
3201                    }
3202                }
3203            } else {
3204                char *str = tex_tokenlist_to_tstring(chr, 1, NULL, 0, 0, 0, 0, 0); /* double hashes */
3205                lua_pushstring(L, str ? str : "");
3206            }
3207            return 1;
3208        }
3209    }
3210    return 0;
3211}
3212
3213/*tex
3214
3215    The final line of this routine is slightly subtle; at least, the author didn't think about it
3216    until getting burnt! There is a used-up token list on the stack, namely the one that contained
3217    |end_write_token|. We insert this artificial |\endwrite| to prevent runaways, as explained
3218    above.) If it were not removed, and if there were numerous writes on a single page, the stack
3219    would overflow.
3220
3221*/
3222
3223static halfword tokenlib_aux_expand_macros_in_tokenlist(halfword p)
3224{
3225    halfword old_mode;
3226    halfword q = tex_get_available_token(right_brace_token + '}');
3227    halfword r = tex_get_available_token(deep_frozen_end_write_token);
3228    token_link(q) = r;
3229    tex_begin_inserted_list(q);
3230    tex_begin_token_list(p, write_text);
3231    /*tex Now we're ready to scan |{<token list>}| |\endwrite|. */
3232    old_mode = cur_list.mode;
3233    cur_list.mode = 0;
3234    /*tex Disable |\prevdepth|, |\spacefactor|, |\lastskip|, |\prevgraf|. */
3235    cur_cs = 0; /* was write_loc i.e. eq of \write */
3236    /*tex Expand macros, etc. */
3237    ++lmt_input_state.align_state; /* emulates the { for the } above */
3238    tex_scan_toks_expand(1, NULL, 0, 0);
3239    tex_get_token();
3240    if (cur_tok != deep_frozen_end_write_token) {
3241        /*tex Recover from an unbalanced write command */
3242        tex_handle_error(
3243            normal_error_type,
3244            "Unbalanced token list expansion",
3245            "On this page there's a token list expansion with fewer real {'s than }'s. I can't\n"
3246            "handle that very well; good luck."
3247        );
3248        do {
3249            tex_get_token();
3250        } while (cur_tok != deep_frozen_end_write_token);
3251    }
3252    cur_list.mode = old_mode;
3253    /*tex Conserve stack space. */
3254    tex_end_token_list();
3255    return lmt_input_state.def_ref;
3256}
3257
3258/* token.getmacro(t[,true][,true] : [also preamble] [only preamble] */
3259
3260static int tokenlib_get_macro(lua_State *L)
3261{
3262    if (lua_type(L, 1) == LUA_TSTRING) {
3263        size_t lname = 0;
3264        const char *name = lua_tolstring(L, 1, &lname);
3265        halfword cs = tex_string_locate_only(name, lname);
3266        halfword cmd = eq_type(cs);
3267        if (is_call_cmd(cmd)) {
3268            halfword chr = eq_value(cs);
3269            char *str = NULL;
3270            if (lua_toboolean(L, 2)) {
3271                chr = tokenlib_aux_expand_macros_in_tokenlist(chr);
3272                str = tex_tokenlist_to_tstring(chr, 1, NULL, 0, 0, 0, 1, 0); /* single hashes ? */
3273            } else {
3274                str = tex_tokenlist_to_tstring(chr, 1, NULL, lua_toboolean(L, 3) ? 2 : 1, 0, 0, 0, 0); /* double hashes */
3275            }
3276            lua_pushstring(L, str ? str : "");
3277            return 1;
3278        }
3279    }
3280    return 0;
3281}
3282
3283/* maybe just memoryword */
3284
3285// todo: node lists:
3286//
3287// [internal|register]_[glue|muglue]_reference_cmd
3288// specification_reference_cmd
3289// box_reference_cmd
3290
3291static int tokenlib_push_macro(lua_State *L) // todo: just store cmd and flag together
3292{
3293    /*tex
3294        We need to check for a valid hit, but what is best here, for instance using |(cmd >= call_cmd)|
3295        is not okay as we miss a lot then.
3296
3297        Active characters: maybe when we pass a number ... 
3298    */
3299    if (lua_type(L, 1) == LUA_TSTRING) {
3300        size_t lname = 0;
3301        const char *name = lua_tolstring(L, 1, &lname);
3302        if (lname > 0) {
3303            halfword cs = tex_string_locate_only(name, lname);
3304            singleword cmd = eq_type(cs);
3305            halfword chr = eq_value(cs);
3306            quarterword global = lua_toboolean(L, 2) ? add_global_flag(0) : 0; /* how */
3307            if (is_call_cmd(cmd)) {
3308                tex_add_token_reference(chr);
3309            }
3310            tokenlib_aux_make_new_package(L, cmd, eq_flag(cs), chr, cs, global);
3311            return 1;
3312        }
3313    }
3314    return 0;
3315}
3316
3317static int tokenlib_pop_macro(lua_State *L)
3318{
3319    lua_token_package *p = tokenlib_aux_check_ispackage(L, 1);
3320    if (p) {
3321        tex_forced_define(p->how, p->cs, p->flag, p->cmd, p->chr);
3322    }
3323    return 0;
3324}
3325
3326char *lmt_get_expansion(halfword head, int *len)
3327{
3328    char *str = NULL;
3329    halfword t; 
3330    halfword ref = get_reference_token();
3331    set_token_link(ref, head);
3332    t = tokenlib_aux_expand_macros_in_tokenlist(ref); 
3333    str = tex_tokenlist_to_tstring(t, 1, len, 0, 0, 0, 1, 1); /* single hashes */
3334    tex_flush_token_list(ref);
3335    return str;
3336}
3337
3338static int tokenlib_get_expansion(lua_State* L)
3339{
3340    const char *str;
3341    size_t len;
3342    int slot = 1;
3343    halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
3344    if (! tex_valid_catcode_table(ct)) {
3345        ct = cat_code_table_par;
3346    }
3347    str = lua_tolstring(L, 1, &len);
3348    if (len > 0) {
3349        halfword h = get_reference_token();
3350        halfword t = h;
3351        char *s;
3352        int l;
3353        tex_parse_str_to_tok(h, &t, ct, str, len, 2); /* ignore unknown */
3354        t = tokenlib_aux_expand_macros_in_tokenlist(h); 
3355        s = tex_tokenlist_to_tstring(t, 1, &l, 0, 0, 0, 1, 1); /* single hashes */
3356        tex_flush_token_list(h);
3357        if (l > 0) {
3358            lua_pushlstring(L, (const char *) s, (size_t) l);
3359            return 1;
3360        }
3361    }
3362    lua_pushliteral(L, "");
3363    return 1;
3364}
3365
3366static int tokenlib_save_lua(lua_State *L)
3367{
3368    halfword f = lmt_tohalfword(L, 1);
3369    if (lua_toboolean(L, 2) && cur_level > 0) {
3370        /* use with care */
3371        halfword ptr = lmt_save_state.save_stack_data.ptr;
3372        while (1) {
3373            --ptr;
3374            switch (save_type(ptr)) {
3375                case level_boundary_save_type:
3376                    goto SAVE;
3377                case restore_lua_save_type:
3378                    if (save_value(ptr) == f) {
3379                        return 0;
3380                    } else {
3381                        break;
3382                    }
3383            }
3384        }
3385    }
3386  SAVE:
3387    tex_save_halfword_on_stack(restore_lua_save_type, f);
3388    return 0;
3389}
3390
3391static int tokenlib_set_lua(lua_State *L)
3392{
3393    int top = lua_gettop(L);
3394    if (top >= 2) {
3395        size_t lname = 0;
3396        const char *name = lua_tolstring(L, 1, &lname);
3397        if (name) {
3398            halfword cs; 
3399            int flags = 0;
3400            int funct = lmt_tointeger(L, 2); /*tex todo: check range */
3401            lmt_check_for_flags(L, 3, &flags, 1, 1);
3402            cs = tex_string_locate(name, lname, 1);
3403            if (tex_define_permitted(cs, flags)) {
3404                if (is_value(flags)) {
3405                    tex_define(flags, cs, lua_value_cmd, funct);
3406                } else if (is_conditional(flags)) {
3407                    tex_define(flags, cs, if_test_cmd, last_if_test_code + funct);
3408                /* with some effort we could combine these two and use the flag */
3409                } else if (is_protected(flags)) {
3410                    tex_define(flags, cs, lua_protected_call_cmd, funct);
3411                } else if (is_semiprotected(flags)) {
3412                    tex_define(flags, cs, lua_semi_protected_call_cmd, funct);
3413                } else {
3414                    tex_define(flags, cs, lua_call_cmd, funct);
3415                }
3416            }
3417        }
3418    }
3419    return 0;
3420}
3421
3422/* [catcodes,]name,data[,global,frozen,protected]* */
3423
3424static int tokenlib_undefine_macro(lua_State *L) /* todo: protected */
3425{
3426    size_t lname = 0;
3427    const char *name = lua_tolstring(L, 1, &lname);
3428    if (name) {
3429        halfword cs = tex_string_locate(name, lname, 1);
3430        int flags = 0;
3431        lmt_check_for_flags(L, 2, &flags, 1, 1);
3432        tex_define(flags, cs, undefined_cs_cmd, null);
3433    }
3434    return 0;
3435}
3436
3437static int tokenlib_set_macro(lua_State *L) /* todo: protected */
3438{
3439    int top = lua_gettop(L);
3440    if (top > 0) {
3441        const char *name = NULL;
3442        size_t lname = 0;
3443        int slot = 1;
3444        halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
3445        if (! tex_valid_catcode_table(ct)) {
3446            ct = cat_code_table_par;
3447        }
3448        name = lua_tolstring(L, slot++, &lname);
3449        if (name) {
3450            size_t lstr = 0;
3451            const char *str = lua_tolstring(L, slot++, &lstr);
3452            halfword cs = tex_string_locate(name, lname, 1);
3453            int flags = 0;
3454            if (slot <= top) {
3455                slot = lmt_check_for_flags(L, slot, &flags, 1, 1);
3456            }
3457            if (tex_define_permitted(cs, flags)) { /* we check before we allocate */
3458                halfword h;
3459                if (lstr > 0) {
3460                    h = get_reference_token();
3461                    /*tex Options: 1=create (will trigger an error), 2=ignore. */
3462                    tex_parse_str_to_tok(h, null, ct, str, lstr, lua_toboolean(L, slot++) ? 2 : 1);
3463                } else { 
3464                    h = lmt_token_state.empty;
3465                 // tex_add_token_reference(h);
3466                }
3467                tex_define(flags, cs, tex_flags_to_cmd(flags), h);
3468            }
3469        }
3470    }
3471    return 0;
3472}
3473
3474// todo: use: is_call_cmd(cmd)
3475
3476halfword lmt_macro_to_tok(lua_State *L, int slot, halfword *tail)
3477{
3478    halfword tok = 0;
3479    switch (lua_type(L, slot)) {
3480        case LUA_TSTRING:
3481            {
3482                size_t lname = 0;
3483                const char *name = lua_tolstring(L, slot, &lname);
3484                int cs = tex_string_locate_only(name, lname);
3485                int cmd = eq_type(cs);
3486                if (is_call_cmd(cmd)) {
3487                    tok = cs_token_flag + cs;
3488                } else if (cmd != undefined_cs_cmd) {
3489                    /*tex Bonus: not really a macro! */
3490                    tok = token_val(cmd, eq_value(cs));
3491                }
3492                break;
3493            }
3494        case LUA_TUSERDATA:
3495            tok = token_info(lmt_token_code_from_lua(L, slot));
3496            if (! is_call_cmd(tok >= cs_token_flag ? eq_type(tok - cs_token_flag) : token_cmd(tok))) {
3497                tok = 0;
3498            }
3499            break;
3500    }
3501    if (tok) {
3502        int top = lua_gettop(L);
3503        halfword m = tex_get_available_token(tok);
3504        halfword a = m;
3505        halfword c = cat_code_table_par;
3506        if (top > slot) {
3507            int arg = 0;
3508            for (int i = slot + 1; i <= top; i++) {
3509                switch (lua_type(L, i)) {
3510                    case LUA_TBOOLEAN:
3511                        {
3512                             arg = lua_toboolean(L, i);
3513                             break;
3514                        }
3515                    case LUA_TSTRING:
3516                        {
3517                            size_t l;
3518                            const char *s = lua_tolstring(L, i, &l);
3519                            if (arg) {
3520                                a = tex_store_new_token(a, left_brace_token + '{');
3521                            }
3522                            /*tex We use option 1 so we get an undefined error. */
3523                            tex_parse_str_to_tok(a, &a, c, s, l, 1);
3524                            if (arg) {
3525                                a = tex_store_new_token(a, right_brace_token + '}');
3526                            }
3527                            break;
3528                        }
3529                    case LUA_TNUMBER:
3530                        {
3531                            /* catcode table */
3532                            c = lmt_tohalfword(L, i);
3533                            break;
3534                        }
3535                    case LUA_TTABLE:
3536                        {
3537                            size_t l;
3538                            const char *s ;
3539                            int j = (int) lua_rawlen(L, i);
3540                            for (int k = 1; k <= j; k++) {
3541                                lua_rawgeti(L, i, k);
3542                                s = lua_tolstring(L, -1, &l);
3543                                a = tex_store_new_token(a, left_brace_token + '{');
3544                                /*tex We use option 1 so we get an udndefined error. */
3545                                tex_parse_str_to_tok(a, &a, c, s, l, 1);
3546                                a = tex_store_new_token(a, right_brace_token + '}');
3547                                lua_pop(L, 1);
3548                            };
3549                            break;
3550                        }
3551                    case LUA_TUSERDATA:
3552                        {
3553                            a = tex_store_new_token(a, lmt_token_code_from_lua(L, i));
3554                            break;
3555                        }
3556                }
3557            }
3558        }
3559        if (tail) {
3560            *tail = a;
3561        }
3562        return m;
3563    } else {
3564        if (tail) {
3565            *tail = null;
3566        }
3567        return null;
3568    }
3569}
3570
3571static int tokenlib_expand_macro(lua_State *L)
3572{
3573    halfword tail = null;
3574    halfword tok = lmt_macro_to_tok(L, 1, &tail);
3575    if (tok) {
3576        /* todo: append to tail */
3577        tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
3578        tex_begin_inserted_list(tok);
3579     // halfword h = tex_get_available_token(token_val(end_local_cmd, 0));
3580     // token_link(tail) = h;
3581     // tex_begin_inserted_list(tok);
3582        if (lmt_token_state.luacstrings > 0) {
3583            tex_lua_string_start();
3584        }
3585        if (tracing_nesting_par > 2) {
3586            tex_local_control_message("entering local control via (run) macro");
3587        }
3588        tex_local_control(1);
3589    } else {
3590        tex_local_control_message("invalid (run) macro");
3591    }
3592    return 0;
3593}
3594
3595/* a weird place, should be in tex */
3596
3597static int tokenlib_set_char(lua_State *L) /* also in texlib */
3598{
3599    int top = lua_gettop(L);
3600    if (top >= 2) {
3601        size_t lname = 0;
3602        const char *name = lua_tolstring(L, 1, &lname);
3603        if (name) {
3604            int value = lmt_tointeger(L, 2);
3605            if (value >= 0 && value <= max_character_code) {
3606                int flags = 0;
3607                int cs = tex_string_locate(name, lname, 1);
3608                if (top > 2) {
3609                    lmt_check_for_flags(L, 3, &flags, 1, 0);
3610                }
3611                tex_define(flags, cs, char_given_cmd, value);
3612            }
3613        }
3614    }
3615    return 0;
3616}
3617
3618/* a weird place, these should be in tex */
3619
3620static int tokenlib_set_constant_value(lua_State *L, singleword cmd, halfword min, halfword max)
3621{
3622    int top = lua_gettop(L);
3623    if (top >= 2) {
3624        size_t lname = 0;
3625        const char *name = lua_tolstring(L, 1, &lname);
3626        if (name) {
3627            halfword value = lmt_tohalfword(L, 2);
3628            if (value >= min && value <= max) {
3629                int flags = 0;
3630                int cs = tex_string_locate(name, lname, 1);
3631                if (top > 2) {
3632                    lmt_check_for_flags(L, 3, &flags, 1, 0);
3633                }
3634                tex_define(flags, cs, cmd, value);
3635            }
3636        }
3637    }
3638    return 0;
3639}
3640
3641static int tokenlib_get_constant_value(lua_State *L, halfword cmd)
3642{
3643    if (lua_type(L, 1) == LUA_TSTRING) {
3644        size_t l;
3645        const char *s = lua_tolstring(L, 1, &l);
3646        if (l > 0) {
3647            int cs = tex_string_locate_only(s, l);
3648            if (eq_type(cs) == cmd) {
3649                lua_pushinteger(L, eq_value(cs));
3650                return 1;
3651            }
3652        }
3653    }
3654    lua_pushnil(L);
3655    return 1;
3656}
3657
3658static int tokenlib_set_integer(lua_State *L)
3659{
3660    return tokenlib_set_constant_value(L, integer_cmd, min_integer, max_integer);
3661}
3662
3663static int tokenlib_set_dimension(lua_State *L)
3664{
3665    return tokenlib_set_constant_value(L, dimension_cmd, min_dimension, max_dimension);
3666}
3667
3668// static int tokenlib_set_gluespec(lua_State *L)
3669// {
3670//     return tokenlib_set_constant_value(L, gluespec_cmd, min_dimension, max_dimension);
3671// }
3672
3673static int tokenlib_get_integer(lua_State *L)
3674{
3675    return tokenlib_get_constant_value(L, integer_cmd);
3676}
3677
3678static int tokenlib_get_dimension(lua_State *L)
3679{
3680    return tokenlib_get_constant_value(L, dimension_cmd);
3681}
3682
3683// static int tokenlib_get_gluespec(lua_State *L)
3684// {
3685//     return tokenlib_get_constant_value(L, gluespec_cmd);
3686// }
3687
3688/*
3689static int tokenlib_get_command_names(lua_State *L)
3690{
3691    lua_createtable(L, data_cmd + 1, 0);
3692    for (int i = 0; command_names[i].lua; i++) {
3693        lua_rawgeti(L, LUA_REGISTRYINDEX, command_names[i].lua);
3694        lua_rawseti(L, -2, i);
3695    }
3696    return 1;
3697}
3698*/
3699
3700static int tokenlib_serialize(lua_State *L)
3701{
3702    lua_token *n = tokenlib_aux_maybe_istoken(L, 1);
3703    if (n) {
3704        /*tex Do we want single or double hashes here? I need a few use cases */
3705        halfword t = tokenlib_aux_expand_macros_in_tokenlist(n->token);
3706        char *s = tex_tokenlist_to_tstring(t, 1, NULL, 0, 0, 0, 1, 0);
3707        lua_pushstring(L, s ? s : "");
3708    } else {
3709        lua_pushnil(L);
3710    }
3711    return 1;
3712}
3713
3714static int tokenlib_getcommandvalues(lua_State *L)
3715{
3716    lua_createtable(L, number_tex_commands, 1);
3717    for (int i = 0; i < number_tex_commands; i++) {
3718        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.command_names[i].lua);
3719        lua_rawseti(L, -2, lmt_interface.command_names[i].id);
3720    }
3721    return 1;
3722}
3723
3724static int tokenlib_getfunctionvalues(lua_State *L)
3725{
3726    return lmt_push_info_values(L, lmt_interface.lua_function_values);
3727}
3728
3729static const struct luaL_Reg tokenlib_function_list[] = {
3730    { "type",                  tokenlib_type                    },
3731    { "create",                tokenlib_create                  },
3732    { "new",                   tokenlib_new                     },
3733    /* */                                                       
3734    { "istoken",               tokenlib_is_token                },
3735    { "isdefined",             tokenlib_is_defined              },
3736    /* getters */                                               
3737    { "scannext",              tokenlib_scan_next               },
3738    { "scannextexpanded",      tokenlib_scan_next_expanded      },
3739    { "scannextchar",          tokenlib_scan_next_char          },
3740    /* skippers */                                              
3741    { "skipnext",              tokenlib_skip_next               },
3742    { "skipnextexpanded",      tokenlib_skip_next_expanded      },
3743    /* peekers */                                               
3744    { "peeknext",              tokenlib_peek_next               },
3745    { "peeknextexpanded",      tokenlib_peek_next_expanded      },
3746    { "peeknextchar",          tokenlib_peek_next_char          },
3747    /* scanners */                                              
3748    { "scancmdchr",            tokenlib_scan_cmdchr             },
3749    { "scancmdchrexpanded",    tokenlib_scan_cmdchr_expanded    },
3750    { "scankeyword",           tokenlib_scan_keyword            },
3751    { "scankeywordcs",         tokenlib_scan_keyword_cs         },
3752    { "scanint",               tokenlib_scan_integer            }, /* obsolete */
3753    { "scaninteger",           tokenlib_scan_integer            },
3754    { "scanintegerargument",   tokenlib_scan_integer_argument   },
3755    { "scandimensionargument", tokenlib_scan_dimension_argument },
3756    { "scandimenargument",     tokenlib_scan_dimension_argument }, /* obsolete */
3757    { "scancardinal",          tokenlib_scan_cardinal           },
3758    { "scanfloat",             tokenlib_scan_float              },
3759    { "scanreal",              tokenlib_scan_real               },
3760    { "scanluanumber",         tokenlib_scan_luanumber          },
3761    { "scanluainteger",        tokenlib_scan_luainteger         },
3762    { "scanluacardinal",       tokenlib_scan_luacardinal        },
3763    { "scanscale",             tokenlib_scan_scale              },
3764    { "scandimen",             tokenlib_scan_dimension          }, /* obsolete */
3765    { "scandimension",         tokenlib_scan_dimension          },
3766    { "scanposit",             tokenlib_scan_posit              },
3767    { "scanskip",              tokenlib_scan_skip               },
3768    { "scanglue",              tokenlib_scan_glue               },
3769    { "scantoks",              tokenlib_scan_toks               },
3770    { "scantokenlist",         tokenlib_scan_tokenlist          },
3771    { "scancode",              tokenlib_scan_code               },
3772    { "scantokencode",         tokenlib_scan_token_code         }, /* doesn't expand */
3773    { "scanstring",            tokenlib_scan_string             },
3774    { "scantokenstring",       tokenlib_scan_tokenstring        },
3775    { "scanargument",          tokenlib_scan_argument           },
3776    { "scandelimited",         tokenlib_scan_delimited          },
3777    { "scanword",              tokenlib_scan_word               },
3778    { "scanletters",           tokenlib_scan_letters            },
3779    { "scankey",               tokenlib_scan_key                },
3780    { "scanvalue",             tokenlib_scan_value              },
3781    { "scanchar",              tokenlib_scan_char               },
3782    { "scancsname",            tokenlib_scan_csname             },
3783    { "scantoken",             tokenlib_scan_token              }, /* expands next token if needed */
3784    { "scanbox",               tokenlib_scan_box                },
3785    { "scandetokened",         tokenlib_scan_detokened          }, 
3786    { "isnextchar",            tokenlib_is_next_char            },
3787    /* writers */                                                
3788    { "putnext",               tokenlib_put_next                },
3789    { "putback",               tokenlib_put_back                },
3790    { "expand",                tokenlib_expand                  },
3791    /* getters */                                               
3792    { "getcommand",            tokenlib_get_command             },
3793    { "getindex",              tokenlib_get_index               },
3794    { "getrange",              tokenlib_get_range               },
3795 /* { "get_mode",              tokenlib_get_mode                }, */ /* obsolete */
3796    { "getcmdname",            tokenlib_get_cmdname             },
3797    { "getcsname",             tokenlib_get_csname              },
3798    { "getid",                 tokenlib_get_id                  },
3799    { "gettok",                tokenlib_get_tok                 }, /* obsolete */
3800    { "getactive",             tokenlib_get_active              },
3801   /* these are not really needs as we can check flags   */   /* maybe obsolete */
3802    { "getexpandable",         tokenlib_get_expandable          },
3803    { "getprotected",          tokenlib_get_protected           },
3804    { "getfrozen",             tokenlib_get_frozen              },
3805    { "gettolerant",           tokenlib_get_tolerant            },
3806    { "getnoaligned",          tokenlib_get_noaligned           },
3807    { "getprimitive",          tokenlib_get_primitive           },
3808    { "getpermanent",          tokenlib_get_permanent           },
3809    { "getimmutable",          tokenlib_get_immutable           },
3810    { "getinstance",           tokenlib_get_instance            },
3811    /* */                                                     
3812    { "getflags",              tokenlib_get_flags               },
3813    { "getparameters",         tokenlib_get_parameters          },
3814    { "getconstant",           tokenlib_get_constant            },
3815    { "getmacro",              tokenlib_get_macro               },
3816    { "getmeaning",            tokenlib_get_meaning             },
3817    { "getcmdchrcs",           tokenlib_get_cmdchrcs            },
3818    { "getcstoken",            tokenlib_get_cstoken             },
3819    { "getfields",             tokenlib_get_fields              },
3820    /* setters */                                               
3821    { "setmacro",              tokenlib_set_macro               },
3822    { "undefinemacro",         tokenlib_undefine_macro          },
3823    { "expandmacro",           tokenlib_expand_macro            },
3824    { "setchar",               tokenlib_set_char                },
3825    { "setlua",                tokenlib_set_lua                 },
3826    { "setinteger",            tokenlib_set_integer             }, /* can go ... also in texlib */
3827    { "getinteger",            tokenlib_get_integer             }, /* can go ... also in texlib */
3828    { "setdimension",          tokenlib_set_dimension           }, /* can go ... also in texlib */
3829    { "getdimension",          tokenlib_get_dimension           }, /* can go ... also in texlib */
3830    /* gobblers */                                              
3831    { "gobbleinteger",         tokenlib_gobble_integer          },
3832    { "gobbledimen",           tokenlib_gobble_dimension        },
3833    { "gobble",                tokenlib_gobble_until            },
3834    { "grab",                  tokenlib_grab_until              },
3835    /* macros */                                                
3836    { "futureexpand",          tokenlib_future_expand           },
3837    { "pushmacro",             tokenlib_push_macro              },
3838    { "popmacro",              tokenlib_pop_macro               },
3839    /* whatever */                                              
3840    { "savelua",               tokenlib_save_lua                },
3841    { "serialize",             tokenlib_serialize               },
3842    { "getexpansion",          tokenlib_get_expansion           },
3843    /* interface */                                             
3844    { "getfunctionvalues",     tokenlib_getfunctionvalues       },
3845    { "getcommandvalues",      tokenlib_getcommandvalues        },
3846    { "getcommandid",          tokenlib_getcommandid            },
3847    { "getprimitives",         tokenlib_getprimitives           },
3848    /* done */                                                  
3849    { NULL,                    NULL                             },
3850};
3851
3852static const struct luaL_Reg tokenlib_instance_metatable[] = {
3853    { "__index",    tokenlib_getfield },
3854    { "__tostring", tokenlib_tostring },
3855    { "__gc",       tokenlib_free     },
3856    { "__eq",       tokenlib_equal    },
3857    { NULL,         NULL              },
3858};
3859
3860static const struct luaL_Reg tokenlib_package_metatable[] = {
3861    { "__tostring", tokenlib_package_tostring },
3862    { NULL,         NULL                      },
3863};
3864
3865int luaopen_token(lua_State *L)
3866{
3867    luaL_newmetatable(L, TOKEN_METATABLE_INSTANCE);
3868    luaL_setfuncs(L, tokenlib_instance_metatable, 0);
3869    luaL_newmetatable(L, TOKEN_METATABLE_PACKAGE);
3870    luaL_setfuncs(L, tokenlib_package_metatable, 0);
3871    lua_newtable(L);
3872    luaL_setfuncs(L, tokenlib_function_list, 0);
3873    return 1;
3874}
3875
3876typedef struct LoadS { // name
3877    char   *s;
3878    size_t  size;
3879} LoadS;
3880
3881static const char *tokenlib_aux_reader(lua_State *L, void *ud, size_t *size)
3882{
3883    LoadS *ls = (LoadS *) ud;
3884    (void) L;
3885    if (ls->size > 0) {
3886        *size = ls->size;
3887        ls->size = 0;
3888        return ls->s;
3889    } else {
3890        return NULL;
3891    }
3892}
3893
3894void lmt_token_call(int p) /*tex The \TEX\ pointer to the token list. */
3895{
3896    LoadS ls;
3897    int l = 0;
3898    ls.s = tex_tokenlist_to_tstring(p, 1, &l, 0, 0, 0, 0, 1); /* single hashes */
3899    ls.size = (size_t) l;
3900    if (ls.size > 0) {
3901        lua_State *L = lmt_lua_state.lua_instance;
3902        int i;
3903        int top = lua_gettop(L);
3904        lua_pushcfunction(L, lmt_traceback);
3905        i = lua_load(L, tokenlib_aux_reader, &ls, "=[\\directlua]", NULL);
3906        if (i != 0) {
3907            lmt_error(L, "token call, syntax", -1, i == LUA_ERRSYNTAX ? 0 : 1);
3908        } else {
3909            ++lmt_lua_state.direct_callback_count;
3910            i = lua_pcall(L, 0, 0, top + 1);
3911            if (i != 0) {
3912                lua_remove(L, top + 1);
3913                lmt_error(L, "token call, execute", -1, i == LUA_ERRRUN ? 0 : 1);
3914            }
3915        }
3916        lua_settop(L, top);
3917    }
3918}
3919
3920void lmt_function_call(int slot, int prefix) /*tex Functions are collected in an indexed table. */
3921{
3922    lua_State *L = lmt_lua_state.lua_instance;
3923    int stacktop = lua_gettop(L);
3924    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
3925    lua_pushcfunction(L, lmt_traceback);
3926    if (lua_rawgeti(L, -2, slot) == LUA_TFUNCTION) {
3927        int i = 1;
3928        /*tex function index */
3929        lua_pushinteger(L, slot);
3930        if (prefix > 0) {
3931             lua_pushinteger(L, prefix);
3932             ++i;
3933        }
3934        ++lmt_lua_state.function_callback_count;
3935        i = lua_pcall(L, i, 0, stacktop + 2);
3936        if (i) {
3937            lua_remove(L, stacktop + 2);
3938            lmt_error(L, "registered function call", slot, i == LUA_ERRRUN ? 0 : 1);
3939        }
3940    }
3941    lua_settop(L, stacktop);
3942}
3943
3944void lmt_local_call(int slot)
3945{
3946    lua_State *L = lmt_lua_state.lua_instance;
3947    int stacktop = lua_gettop(L);
3948    lua_pushcfunction(L, lmt_traceback);
3949    if (lua_rawgeti(L, LUA_REGISTRYINDEX, slot) == LUA_TFUNCTION) {
3950        int i;
3951        ++lmt_lua_state.local_callback_count;
3952        i = lua_pcall(L, 0, 0, stacktop + 1);
3953        if (i) {
3954            lua_remove(L, stacktop + 1);
3955            lmt_error(L, "local function call", slot, i == LUA_ERRRUN ? 0 : 1);
3956        }
3957    }
3958    lua_settop(L, stacktop);
3959}
3960
3961/*tex We replaced |class| by |category because of g++ issues. */
3962
3963int lmt_function_call_by_category(int slot, int property, halfword *value)
3964{
3965    lua_State *L = lmt_lua_state.lua_instance;
3966    int stacktop = lua_gettop(L);
3967    int category = lua_value_none_code;
3968    lua_pushcfunction(L, lmt_traceback);
3969    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
3970    if (lua_rawgeti(L, -1, slot) == LUA_TFUNCTION) {
3971        int i;
3972        /*tex function index */
3973        lua_pushinteger(L, slot);
3974        if (property) {
3975            lua_pushinteger(L, property);
3976        } else {
3977            lua_push_key(value);
3978        }
3979        ++lmt_lua_state.value_callback_count;
3980        i = lua_pcall(L, 2, 2, stacktop + 1);
3981        if (i) {
3982            lua_remove(L, stacktop + 1);
3983            lmt_error(L, "function call", slot, i == LUA_ERRRUN ? 0 : 1);
3984        } else {
3985            if (lua_type(L, -2) == LUA_TNUMBER) {
3986                category = lmt_tointeger(L, -2);
3987            }
3988            switch (category) {
3989                case lua_value_none_code:
3990                    {
3991                        break;
3992                    }
3993                case lua_value_integer_code:
3994                    {
3995                        /* expects valid integer, no double */
3996                        *value = lua_type(L, -1) == LUA_TNUMBER ? lmt_tohalfword(L, -1) : 0;
3997                        if (*value < - max_integer) {
3998                            *value = max_integer;
3999                        } else if (*value > max_integer) {
4000                            *value = max_integer;
4001                        }
4002                        break;
4003                    }
4004                case lua_value_cardinal_code:
4005                    {
4006                        /* expects valid integer, no double */
4007                        lua_Unsigned u = lua_type(L, -1) == LUA_TNUMBER ? (lua_Unsigned) lua_tointeger(L, -1) : 0;
4008                        if (u > max_cardinal) {
4009                            u = max_cardinal;
4010                        }
4011                        if (*value > max_integer) {
4012                            *value = (halfword) (u - 0x100000000);
4013                        } else {
4014                            *value = (halfword) u;
4015                        }
4016                        break;
4017                    }
4018                case lua_value_dimension_code:
4019                    {
4020                        /* accepts double and rounds it */
4021                        *value = lua_type(L, -1) == LUA_TNUMBER ? lmt_roundnumber(L, -1) : 0;
4022                        if (*value < - max_dimension) {
4023                            *value = max_dimension;
4024                        } else if (*value > max_dimension) {
4025                            *value = max_dimension;
4026                        }
4027                        break;
4028                    }
4029                case lua_value_skip_code:
4030                    {
4031                        halfword n = lmt_check_isnode(L, -1);
4032                        if (n && node_type(n) == glue_spec_node) {
4033                            *value = n;
4034                        } else {
4035                            luaL_error(L, "gluespec node expected");
4036                            *value = tex_copy_node(zero_glue);
4037                        }
4038                        break;
4039                    }
4040                case lua_value_float_code:
4041                    *value = tex_double_to_posit(lua_tonumber(L, -1)).v;
4042                    break;
4043                case lua_value_string_code:
4044                    category = lua_value_none_code;
4045                    break;
4046                case lua_value_boolean_code:
4047                    *value = lua_toboolean(L, -1);
4048                    break;
4049                case lua_value_node_code:
4050                    *value = lmt_check_isnode(L, -1);
4051                    break;
4052                case lua_value_direct_code:
4053                    *value = lmt_check_isdirect(L, -1);
4054                    break;
4055                case lua_value_conditional_code:
4056                    break;
4057                default:
4058                    category = lua_value_none_code;
4059                    break;
4060            }
4061        }
4062    }
4063    lua_settop(L, stacktop);
4064    return category;
4065}
4066
4067/* some day maybe an alternative too
4068
4069void lmt_function_call(int slot)
4070{
4071    lua_State *L = lua_state.lua_instance;
4072    int stacktop = lua_gettop(L);
4073    lua_rawgeti(L, LUA_REGISTRYINDEX, lua_state.function_table_id);
4074    if (lua_rawgeti(L, -1, slot) == LUA_TFUNCTION) {
4075        lua_pushinteger(L, slot);
4076        ++lua_state.function_callback_count;
4077        lua_call(L, 1, 0);
4078    }
4079    lua_settop(L,stacktop);
4080}
4081
4082*/
4083
4084int lmt_push_specification(lua_State *L, halfword ptr, int onlycount)
4085{
4086    if (ptr) {
4087        switch (node_subtype(ptr)) {
4088            case par_shape_code:
4089                {
4090                    int n = specification_count(ptr);
4091                    if (onlycount == 1) {
4092                        lua_pushinteger(L, n);
4093                    } else {
4094                        int r = specification_repeat(ptr);
4095                        lua_createtable(L, n, r ? 1 : 0);
4096                        if (r) {
4097                            lua_push_boolean_at_key(L, repeat, r);
4098                        }
4099                        for (int m = 1; m <= n; m++) {
4100                            lua_createtable(L, 2, 0);
4101                            lua_pushinteger(L, tex_get_specification_indent(ptr, m));
4102                            lua_rawseti(L, -2, 1);
4103                            lua_pushinteger(L, tex_get_specification_width(ptr, m));
4104                            lua_rawseti(L, -2, 2);
4105                            lua_rawseti(L, -2, m);
4106                        }
4107                    }
4108                    return 1;
4109                }
4110            case par_passes_code:
4111                {
4112                    return 0;
4113                }
4114            case inter_line_penalties_code:
4115            case club_penalties_code:
4116            case widow_penalties_code:
4117            case display_widow_penalties_code:
4118            case orphan_penalties_code:
4119            case math_forward_penalties_code:
4120            case math_backward_penalties_code:
4121                {
4122                    int n = specification_count(ptr);
4123                    if (onlycount == 1) {
4124                        lua_pushinteger(L, n);
4125                    } else {
4126                        lua_createtable(L, n, 0);
4127                        for (int m = 1; m <= n; m++) {
4128                            lua_pushinteger(L, tex_get_specification_penalty(ptr, m));
4129                            lua_rawseti(L, -2, m);
4130                        }
4131                    }
4132                    return 1;
4133                }
4134        }
4135    }
4136    lua_pushnil(L);
4137    return 1;
4138}
4139