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