summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua/lmttokenlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua/lmttokenlib.c')
-rw-r--r--source/luametatex/source/lua/lmttokenlib.c3894
1 files changed, 3894 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmttokenlib.c b/source/luametatex/source/lua/lmttokenlib.c
new file mode 100644
index 000000000..896b22eec
--- /dev/null
+++ b/source/luametatex/source/lua/lmttokenlib.c
@@ -0,0 +1,3894 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ The tokenlib started out as an expetiment. The first version provided a rough interface to the
+ internals but could only really be used for simple introspection and limited piping back. A major
+ step up came in a second version where Taco introduced a couple of scanners. During experiments
+ in \CONTEXT\ I added some more so now we have a reasonable repertoire of creators, accessors and
+ scanners. Piping back to \LUA\ happens in the |tex| library and that one also has been enhanced
+ and accepts tokens.
+
+ In \LUAMETATEX\ much got streamlined, partially rewritten and some more got added so we're now
+ actually at the third version. In the meantime the experimental status has been revoked. Also,
+ the internals that relate to tokens in \LUAMETATEX\ have been redone so that the interface to
+ \LUA\ feels more natural.
+
+ Tokens are numbers but these can best be treated as abstractions. The number can be split in
+ components that code some properties. However, these numbers can change depending on what
+ abstraction we decide to use. As with the nodes integers make for an efficient coding but are
+ effectively just black boxes. The Lua interface below communicates via such numbers but don't
+ make your code dependent of the values. The mentioned rework of the internals now makes sure
+ that we get less funny numbers. For instance all chr codes nor occupy proper ranges and names
+ are more meaningful.
+
+*/
+
+# include "luametatex.h"
+
+/* # define TOKEN_METATABLE_INSTANCE "luatex.token" */
+
+typedef struct lua_token_package {
+ struct {
+ quarterword level; /* not used but it reflects the original */
+ quarterword how; /* global */
+ };
+ singleword cmd;
+ singleword flag;
+ halfword chr;
+ halfword cs;
+} lua_token_package;
+
+/*
+
+ So, given what is said above, the \LUA\ interface no longer really is about magic numbers
+ combined from cmd and chr codes, sometimes called modes, but consistently tries to use the
+ combination instead of the composed number. The number is still there (and available) but users
+ need to keep in mind that constructing them directly is bad idea: the internals and therefore
+ cmd and chr codes can change! We start with a table that defines all the properties.
+
+ It must be noticed that the codebase is now rather different from \LUATEX. Of course we still
+ have most of the original commands but new ones have been added, experimental one have been
+ dropped, some have been combined. One criterium for grouping commands is that such a group gets
+ a unique treatment in reading a follow up, serialization, expansion, the main loop, the
+ registers and variables it refers to, etc. There is some logic behind it!
+
+ command_item lmt_command_names[] = {
+ { .id = escape_cmd, .lua = 0, .name = NULL, .kind = character_command_item, .min = 0, .max = max_character_code, .base = 0, .fixedvalue = too_big_char },
+ ....
+ } ;
+
+ has been replaced by a dynamic allocation and later assignment.
+
+ In principle we can add some more clever token definers for instance for integers but that will
+ be done when I need it. The special data / reference commands need some checking (min, max etc.)
+
+*/
+
+# define ignore_entry -1
+# define direct_entry -2
+
+void lmt_tokenlib_initialize(void)
+{
+
+ lmt_interface.command_names = lmt_memory_malloc((register_dimen_reference_cmd + 2) * sizeof(command_item));
+
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[set_mark_cmd] = (command_item) { .id = set_mark_cmd, .lua = lua_key_index(set_mark), .name = lua_key(set_mark), .kind = regular_command_item, .min = 0, .max = last_set_mark_code, .base = 0, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 */
+ 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 };
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 */
+ 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 };
+ 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 };
+ // lmt_interface.command_names[math_char_given_cmd] = (command_item) { .id = math_char_given_cmd, .lua = lua_key_index(math_char_given), .name = lua_key(math_char_given), .kind = character_command_item, .min = 0, .max = max_character_code, .base = 0, .fixedvalue = 0 };
+ // lmt_interface.command_names[math_char_xgiven_cmd] = (command_item) { .id = math_char_xgiven_cmd, .lua = lua_key_index(math_char_xgiven), .name = lua_key(math_char_xgiven), .kind = character_command_item, .min = 0, .max = max_character_code, .base = 0, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_int_cmd] = (command_item) { .id = internal_int_cmd, .lua = lua_key_index(internal_int), .name = lua_key(internal_int), .kind = internal_command_item, .min = first_int_code, .max = last_int_code, .base = internal_int_base, .fixedvalue = 0 };
+ lmt_interface.command_names[register_int_cmd] = (command_item) { .id = register_int_cmd, .lua = lua_key_index(register_int), .name = lua_key(register_int), .kind = register_command_item, .min = 0, .max = max_int_register_index, .base = register_int_base, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_dimen_cmd] = (command_item) { .id = internal_dimen_cmd, .lua = lua_key_index(internal_dimen), .name = lua_key(internal_dimen), .kind = internal_command_item, .min = first_dimen_code, .max = last_dimen_code, .base = internal_dimen_base, .fixedvalue = 0 };
+ lmt_interface.command_names[register_dimen_cmd] = (command_item) { .id = register_dimen_cmd, .lua = lua_key_index(register_dimen), .name = lua_key(register_dimen), .kind = register_command_item, .min = 0, .max = max_dimen_register_index, .base = register_dimen_base, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_mu_glue_cmd] = (command_item) { .id = internal_mu_glue_cmd, .lua = lua_key_index(internal_mu_glue), .name = lua_key(internal_mu_glue), .kind = internal_command_item, .min = first_mu_glue_code, .max = last_mu_glue_code, .base = internal_mu_glue_base, .fixedvalue = 0 };
+ lmt_interface.command_names[register_mu_glue_cmd] = (command_item) { .id = register_mu_glue_cmd, .lua = lua_key_index(register_mu_glue), .name = lua_key(register_mu_glue), .kind = register_command_item, .min = 0, .max = max_mu_glue_register_index, .base = register_mu_glue_base, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[set_font_property_cmd] = (command_item) { .id = set_font_property_cmd, .lua = lua_key_index(set_font_property), .name = lua_key(set_font_property), .kind = regular_command_item, .min = 0, .max = last_font_property_code, .base = 0, .fixedvalue = 0 };
+ lmt_interface.command_names[set_auxiliary_cmd] = (command_item) { .id = set_auxiliary_cmd, .lua = lua_key_index(set_auxiliary), .name = lua_key(set_auxiliary), .kind = regular_command_item, .min = 0, .max = last_auxiliary_code, .base = 0, .fixedvalue = 0 };
+ lmt_interface.command_names[set_page_property_cmd] = (command_item) { .id = set_page_property_cmd, .lua = lua_key_index(set_page_property), .name = lua_key(set_page_property), .kind = regular_command_item, .min = 0, .max = last_page_property_code, .base = 0, .fixedvalue = 0 };
+ lmt_interface.command_names[set_box_property_cmd] = (command_item) { .id = set_box_property_cmd, .lua = lua_key_index(set_box_property), .name = lua_key(set_box_property), .kind = regular_command_item, .min = 0, .max = last_box_property_code, .base = 0, .fixedvalue = 0 };
+ lmt_interface.command_names[set_specification_cmd] = (command_item) { .id = set_specification_cmd, .lua = lua_key_index(set_specification), .name = lua_key(set_specification), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[set_math_parameter_cmd] = (command_item) { .id = set_math_parameter_cmd, .lua = lua_key_index(set_math_parameter), .name = lua_key(set_math_parameter), .kind = regular_command_item, .min = 0, .max = last_math_parameter, .base = 0, .fixedvalue = 0 };
+ // 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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_dimen, .max = max_dimen, .base = direct_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[mathspec_cmd] = (command_item) { .id = mathspec_cmd, .lua = lua_key_index(mathspec), .name = lua_key(fontspec), .kind = regular_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[set_interaction_cmd] = (command_item) { .id = set_interaction_cmd, .lua = lua_key_index(set_interaction), .name = lua_key(set_interaction), .kind = regular_command_item, .min = 0, .max = last_interaction_level, .base = 0, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ 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 };
+ /* lmt_interface.command_names[string_cmd] = (command_item) { .id = string_cmd, .lua = lua_key_index(string), .name = lua_key(string), .kind = regular_command_item, .min = ignore_entry, .max = max_integer, .base = 0, .fixedvalue = 0 }; */
+ 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 };
+ 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 };
+ lmt_interface.command_names[semi_protected_call_cmd] = (command_item) { .id = semi_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 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[tolerant_semi_protected_call_cmd] = (command_item) { .id = tolerant_semi_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 };
+ lmt_interface.command_names[deep_frozen_end_template_cmd] = (command_item) { .id = deep_frozen_end_template_cmd, .lua = lua_key_index(deep_frozen_cs_end_template), .name = lua_key(deep_frozen_cs_end_template), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[deep_frozen_dont_expand_cmd] = (command_item) { .id = deep_frozen_dont_expand_cmd, .lua = lua_key_index(deep_frozen_cs_dont_expand), .name = lua_key(deep_frozen_cs_dont_expand), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_mu_glue_reference_cmd] = (command_item) { .id = internal_mu_glue_reference_cmd, .lua = lua_key_index(internal_mu_glue_reference), .name = lua_key(internal_mu_glue_reference), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[register_mu_glue_reference_cmd] = (command_item) { .id = register_mu_glue_reference_cmd, .lua = lua_key_index(register_mu_glue_reference), .name = lua_key(register_mu_glue_reference), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_toks_reference_cmd] = (command_item) { .id = internal_toks_reference_cmd, .lua = lua_key_index(register_box_reference), .name = lua_key(register_box_reference), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[register_toks_reference_cmd] = (command_item) { .id = register_toks_reference_cmd, .lua = lua_key_index(internal_toks_reference), .name = lua_key(internal_toks_reference), .kind = token_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[specification_reference_cmd] = (command_item) { .id = specification_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 };
+ lmt_interface.command_names[internal_int_reference_cmd] = (command_item) { .id = internal_int_reference_cmd, .lua = lua_key_index(internal_int_reference), .name = lua_key(internal_int_reference), .kind = regular_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[register_int_reference_cmd] = (command_item) { .id = register_int_reference_cmd, .lua = lua_key_index(register_int_reference), .name = lua_key(register_int_reference), .kind = regular_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ 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 };
+ 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 };
+ lmt_interface.command_names[internal_dimen_reference_cmd] = (command_item) { .id = internal_dimen_reference_cmd, .lua = lua_key_index(internal_dimen_reference), .name = lua_key(internal_dimen_reference), .kind = regular_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[register_dimen_reference_cmd] = (command_item) { .id = register_dimen_reference_cmd, .lua = lua_key_index(register_dimen_reference), .name = lua_key(register_dimen_reference), .kind = regular_command_item, .min = ignore_entry, .max = ignore_entry, .base = ignore_entry, .fixedvalue = 0 };
+ lmt_interface.command_names[register_dimen_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 };
+
+ if (lmt_interface.command_names[last_cmd].id != last_cmd) {
+ tex_fatal_error("mismatch between tex and lua command name tables");
+ }
+}
+
+typedef struct saved_tex_scanner {
+ int cmd;
+ int chr;
+ int cs;
+ int tok;
+} saved_tex_scanner;
+
+inline static saved_tex_scanner tokenlib_aux_save_tex_scanner(void) {
+ return (saved_tex_scanner) {
+ .cmd = cur_cmd,
+ .chr = cur_chr,
+ .cs = cur_cs,
+ .tok = cur_tok
+ };
+}
+
+inline static void tokenlib_aux_unsave_tex_scanner(saved_tex_scanner a)
+{
+ cur_cmd = a.cmd;
+ cur_chr = a.chr;
+ cur_cs = a.cs;
+ cur_tok = a.tok;
+}
+
+static int tokenlib_aux_get_command_id(const char *s)
+{
+ for (int i = 0; lmt_interface.command_names[i].id != -1; i++) {
+ if (s == lmt_interface.command_names[i].name) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*tex
+ We have some checkers that use the information from |command_names|:
+
+ \startitemize
+ \startitem the 0..64K counter, dimen, token etc registers \stopitem
+ \startitem the predefined internal quantities \stopitem
+ \stopitemize
+*/
+
+/*
+inline static int tokenlib_valid_cmd(int cmd)
+{
+ return cmd >= first_cmd && cmd <= last_cmd;
+}
+*/
+
+inline static int tokenlib_aux_valid_chr(int cmd, int chr)
+{
+ command_item item = lmt_interface.command_names[cmd];
+ if (chr > 0) {
+ switch (item.base) {
+ case ignore_entry:
+ case direct_entry:
+ break;
+ default:
+ if (chr >= item.min && chr <= item.max) {
+ return item.base + chr;
+ }
+ }
+ } else if (chr == item.fixedvalue) {
+ return chr;
+ }
+ return 0;
+}
+
+inline static int tokenlib_aux_valid_cs(int cs)
+{
+ return (cs >= 0 && cs <= lmt_token_memory_state.tokens_data.allocated) ? cs : -1;
+}
+
+// not ok
+
+inline static int tokenlib_aux_valid_token(int cmd, int chr, int cs)
+{
+ if (cs) {
+ cs = tokenlib_aux_valid_cs(cs);
+ if (cs >= 0) {
+ return cs_token_flag + cs;
+ }
+ } if (cmd >= first_cmd && cmd <= last_cmd) {
+ chr = tokenlib_aux_valid_chr(cmd, chr);
+ if (chr >= 0) {
+ return token_val(cmd, chr);
+ }
+ }
+ return -1;
+}
+
+inline static int tokenlib_aux_to_valid_index(int cmd, int chr)
+{
+ if (cmd >= 0 && cmd <= last_cmd) {
+ command_item item = lmt_interface.command_names[cmd];
+ switch (item.kind) {
+ case unused_command_item:
+ return 0;
+ case regular_command_item:
+ case character_command_item:
+ return chr;
+ case register_command_item:
+ case internal_command_item:
+ case reference_command_item:
+ case data_command_item:
+ {
+ halfword c = chr;
+ switch (item.base) {
+ case ignore_entry:
+ return 0;
+ case direct_entry:
+ break;
+ default:
+ chr -= item.base;
+ break;
+ }
+ if (c >= item.min && c <= item.max) {
+ return c;
+ } else {
+ return item.min;
+ }
+ }
+ case token_command_item:
+ case node_command_item:
+ return item.fixedvalue;
+ }
+ }
+ return 0;
+}
+
+inline static void tokenlib_aux_make_token_table(lua_State *L, int cmd, int chr, int cs)
+{
+ lua_createtable(L, 3, 0);
+ lua_pushinteger(L, cmd);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr)); /* index or value */
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, cs);
+ lua_rawseti(L, -2, 3);
+}
+
+/*tex
+
+ Takes a table |{ cmd, chr, cs }| where either the first two are taken or the last one. This is
+ something historic. So we have either |{ cmd, chr, - }| or |{ -, -, cs}| to deal with. This
+ might change in the future but we then need to check all usage in \CONTEXT\ first.
+*/
+
+inline static int lmt_token_from_lua(lua_State *L)
+{
+ int cmd, chr, cs;
+ lua_rawgeti(L, -1, 1);
+ cmd = lmt_tointeger(L, -1);
+ lua_rawgeti(L, -2, 2);
+ chr = lmt_optinteger(L, -1, 0);
+ lua_rawgeti(L, -3, 3);
+ cs = lmt_optinteger(L, -1, 0);
+ lua_pop(L, 3);
+ return tokenlib_aux_valid_token(cmd, chr, cs); /* just the token value */
+}
+
+void lmt_token_list_to_lua(lua_State *L, halfword p)
+{
+ int i = 1;
+ int v = p;
+ int max = lmt_token_memory_state.tokens_data.top; /*tex It doesn't change here. */
+ while (v && v < max) {
+ i++;
+ v = token_link(v);
+ }
+ lua_createtable(L, i, 0);
+ i = 1;
+ while (p && p < max) {
+ int cmd, chr, cs;
+ if (token_info(p) >= cs_token_flag) {
+ cs = token_info(p) - cs_token_flag;
+ cmd = eq_type(cs);
+ chr = eq_value(cs);
+ } else {
+ cs = 0;
+ cmd = token_cmd(token_info(p));
+ chr = token_chr(token_info(p));
+ }
+ tokenlib_aux_make_token_table(L, cmd, chr, cs);
+ lua_rawseti(L, -2, i++);
+ p = token_link(p);
+ }
+}
+
+void lmt_token_list_to_luastring(lua_State *L, halfword p, int nospace, int strip)
+{
+ int l;
+ char *s = tex_tokenlist_to_tstring(p, 1, &l, 0, nospace, strip); /* nasty ... preambles or not, could have been endmatchtoken */
+ if (l) {
+ lua_pushlstring(L, s, (size_t) l);
+ } else {
+ lua_pushliteral(L, "");
+ }
+}
+
+static lua_token *tokenlib_aux_check_istoken(lua_State *L, int ud);
+
+halfword lmt_token_list_from_lua(lua_State *L, int slot)
+{
+ halfword h = tex_get_available_token(null);
+ halfword p = h;
+ token_link(h) = null;
+ switch (lua_type(L, slot)) {
+ case LUA_TTABLE:
+ {
+ int j = (int) lua_rawlen(L, slot);
+ if (j > 0) {
+ for (int i = 1; i <= j; i++) {
+ int tok;
+ lua_rawgeti(L, slot, (int) i);
+ tok = lmt_token_from_lua(L);
+ if (tok >= 0) {
+ p = tex_store_new_token(p, tok);
+ }
+ lua_pop(L, 1);
+ };
+ }
+ return h;
+ }
+ case LUA_TSTRING:
+ {
+ size_t j;
+ const char *s = lua_tolstring(L, slot, &j);
+ for (size_t i = 0; i < j; i++) {
+ int tok;
+ if (s[i] == ascii_space) {
+ tok = token_val(spacer_cmd, s[i]);
+ } else {
+ int k = (int) aux_str2uni((const unsigned char *) (s + i));
+ i = i + (size_t) (utf8_size(k)) - 1;
+ tok = token_val(other_char_cmd, k);
+ }
+ p = tex_store_new_token(p, tok);
+ }
+ return h;
+ }
+ case LUA_TUSERDATA:
+ {
+ lua_token *t = tokenlib_aux_check_istoken(L, slot);
+ p = tex_store_new_token(p, t->token);
+ return h;
+ }
+ default:
+ {
+ tex_put_available_token(h);
+ return null;
+ }
+ }
+}
+
+halfword lmt_token_code_from_lua(lua_State *L, int slot)
+{
+ lua_token *t = tokenlib_aux_check_istoken(L, slot);
+ return t->token;
+}
+
+# define DEFAULT_SCAN_CODE_SET (2048 + 4096) /*tex default: letter and other */
+
+/*tex two core helpers .. todo: combine active*/
+
+# define is_active_string(s) (strlen(s) > 3 && *s == 0xEF && *(s+1) == 0xBF && *(s+2) == 0xBF)
+
+static unsigned char *tokenlib_aux_get_cs_text(int cs)
+{
+ if (cs == null_cs) {
+ return (unsigned char *) lmt_memory_strdup("\\csname\\endcsname");
+ } else if ((cs_text(cs) < 0) || (cs_text(cs) >= lmt_string_pool_state.string_pool_data.ptr)) {
+ return (unsigned char *) lmt_memory_strdup("");
+ } else if (tex_is_active_cs(cs_text(cs))) {
+ return (unsigned char *) tex_makecstring(cs_text(cs));
+ } else {
+ return (unsigned char *) tex_makecstring(cs_text(cs));
+ }
+}
+
+static lua_token *tokenlib_aux_maybe_istoken(lua_State *L, int ud)
+{
+ lua_token *t = lua_touserdata(L, ud);
+ if (t && lua_getmetatable(L, ud)) {
+ lua_get_metatablelua(token_instance);
+ if (! lua_rawequal(L, -1, -2)) {
+ t = NULL;
+ }
+ lua_pop(L, 2);
+ }
+ return t;
+}
+
+static lua_token_package *tokenlib_aux_maybe_ispackage(lua_State *L, int ud)
+{
+ lua_token_package *t = lua_touserdata(L, ud);
+ if (t && lua_getmetatable(L, ud)) {
+ lua_get_metatablelua(token_package);
+ if (! lua_rawequal(L, -1, -2)) {
+ t = NULL;
+ }
+ lua_pop(L, 2);
+ }
+ return t;
+}
+
+/*tex we could make the message a function and just inline the rest (via a macro) */
+
+lua_token *tokenlib_aux_check_istoken(lua_State *L, int ud)
+{
+ lua_token *t = tokenlib_aux_maybe_istoken(L, ud);
+ if (! t) {
+ tex_formatted_error("token lib", "lua <token> expected, not an object with type %s", luaL_typename(L, ud));
+ }
+ return t;
+}
+
+static lua_token_package *tokenlib_aux_check_ispackage(lua_State *L, int ud)
+{
+ lua_token_package *t = tokenlib_aux_maybe_ispackage(L, ud);
+ if (! t) {
+ tex_formatted_error("token lib", "lua <token package> expected, not an object with type %s", luaL_typename(L, ud));
+ }
+ return t;
+}
+
+/*tex token library functions */
+
+static void tokenlib_aux_make_new_token(lua_State *L, int cmd, int chr, int cs)
+{
+ int tok = tokenlib_aux_valid_token(cmd, chr, cs);
+ if (tok >= 0) {
+ lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
+ thetok->token = tex_get_available_token(tok);
+ thetok->origin = token_origin_lua;
+ lua_get_metatablelua(token_instance);
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void tokenlib_aux_make_new_token_tok(lua_State *L, int tok)
+{
+ if (tok >= 0) {
+ lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
+ thetok->token = tex_get_available_token(tok);
+ thetok->origin = token_origin_lua;
+ lua_get_metatablelua(token_instance);
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void tokenlib_aux_make_new_package(lua_State *L, singleword cmd, singleword flag, int chr, int cs, quarterword how)
+{
+ lua_token_package *package = (lua_token_package *) lua_newuserdatauv(L, sizeof(lua_token_package), 0);
+ package->cmd = cmd;
+ package->flag = flag;
+ package->chr = chr;
+ package->cs = cs;
+ package->how = how;
+ lua_get_metatablelua(token_package);
+ lua_setmetatable(L, -2);
+}
+
+static void tokenlib_aux_push_token(lua_State *L, int tok)
+{
+ lua_token *thetok = (lua_token *) lua_newuserdatauv(L, sizeof(lua_token), 0);
+ thetok->token = tok;
+ thetok->origin = token_origin_lua;
+ lua_get_metatablelua(token_instance);
+ lua_setmetatable(L, -2);
+}
+
+static int tokenlib_getcommandid(lua_State *L)
+{
+ int id = -1;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ id = tokenlib_aux_get_command_id(lua_tostring(L, 1));
+ break;
+ case LUA_TNUMBER:
+ id = lmt_tointeger(L, 1);
+ break;
+ }
+ if (id >= 0 && id < number_glue_pars) {
+ lua_pushinteger(L, id);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_scan_next(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword tok = tex_get_token();
+ tokenlib_aux_make_new_token_tok(L, tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_next_expanded(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword tok = tex_get_x_token();
+ tokenlib_aux_make_new_token_tok(L, tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_skip_next(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ (void) L;
+ tex_get_token();
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 0;
+}
+
+static int tokenlib_skip_next_expanded(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ (void) L;
+ tex_get_x_token();
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 0;
+}
+
+/*tex
+
+ This is experimental code:
+
+ \starttyping
+ local t1 = token.get_next()
+ local t2 = token.get_next()
+ local t3 = token.get_next()
+ local t4 = token.get_next()
+ -- watch out, we flush in sequence
+ token.put_next { t1, t2 }
+ -- but this one gets pushed in front
+ token.put_next ( t3, t4 )
+ -- so when we get wxyz we put yzwx!
+ \stoptyping
+
+ At some point we can consider a token.print that delays and goes via the same rope mechanism as
+ |texio.print| and friends but then one can as well serialize the tokens and do a normal print so
+ there is no real gain in it. After all, the tokenlib operates at the input level so we might as
+ well keep it there.
+
+*/
+
+inline static int tokenlib_aux_to_token_val(int chr)
+{
+ switch (chr) {
+ case '\n':
+ case '\r':
+ case ' ':
+ return token_val(spacer_cmd, ' ');
+ default:
+ {
+ int cmd = tex_get_cat_code(cat_code_table_par, chr);
+ switch (cmd) {
+ case escape_cmd:
+ case ignore_cmd:
+ case comment_cmd:
+ case invalid_char_cmd:
+ case active_char_cmd:
+ cmd = other_char_cmd;
+ break;
+ }
+ return token_val(cmd, chr);
+ }
+ }
+}
+
+/*tex
+ The original implementation was a bit different in the sense that I distinguished between one or
+ more arguments with the one argument case handling a table. The reason was that I considered
+ having an optional second argument that could control the catcode table.
+
+ In the end this function is not used that often (of at all), so after checking the manual, I
+ decided not to provide that feature so the code could be simplified a bit. But, as compensation,
+ nested tables became possible.
+*/
+
+static void tokenlib_aux_to_token(lua_State *L, int i, int m, int *head, int *tail)
+{
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ /*tex More efficient is to iterate (but then we also need to know the length). */
+ {
+ size_t l = 0;
+ const char *s = lua_tolstring(L, i, &l);
+ const unsigned char *p = (const unsigned char *) s;
+ size_t n = aux_utf8len(s, l);
+ for (size_t j = 0; j < n; j++) {
+ int ch = *p;
+ halfword x = tex_get_available_token(tokenlib_aux_to_token_val(aux_str2uni(p)));
+ if (*head) {
+ token_link(*tail) = x;
+ } else {
+ *head = x;
+ }
+ *tail = x;
+ p += utf8_size(ch);
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ halfword t = tex_get_available_token(tokenlib_aux_to_token_val((int) lua_tointeger(L, i)));
+ if (*head) {
+ token_link(*tail) = t;
+ } else {
+ *head = t;
+ }
+ *tail = t;
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ size_t n = lua_rawlen(L, i);
+ for (size_t j = 1; j <= n; j++) {
+ lua_rawgeti(L, i, j);
+ tokenlib_aux_to_token(L, -1, m, head, tail);
+ lua_pop(L, 1);
+ }
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ /* todo: like nodelib: |maybe_is_token|. */
+ lua_token *p = lua_touserdata(L, i);
+ halfword t, q;
+ if (p && lua_getmetatable(L, i)) {
+ t = lua_rawequal(L, m, -1) ? token_info(p->token) : tokenlib_aux_to_token_val(0xFFFD);
+ lua_pop(L, 1); /* The metatable. */
+ } else {
+ t = tokenlib_aux_to_token_val(0xFFFD);
+ }
+ q = tex_get_available_token(t);
+ if (*head) {
+ token_link(*tail) = q;
+ } else {
+ *head = q;
+ }
+ *tail = q;
+ break;
+ }
+ default:
+ /*tex Just ignore it. */
+ break;
+ }
+}
+
+inline static int tokenlib_put_next(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ halfword h = null;
+ halfword t = null;
+ int m = top + 1;
+ lua_get_metatablelua(token_instance);
+ for (int i = 1; i <= top; i++) {
+ tokenlib_aux_to_token(L, i, m, &h, &t);
+ }
+ if (h) {
+ tex_begin_inserted_list(h);
+ }
+ lua_settop(L, top);
+ }
+ return 0;
+}
+
+inline static int tokenlib_put_back(lua_State *L)
+{
+ lua_token *t = tokenlib_aux_check_istoken(L, 1);
+ if (t) {
+ tex_back_input(token_info(t->token));
+ }
+ return 0;
+}
+
+static int tokenlib_scan_keyword(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ int v = 0;
+ if (s) {
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ v = tex_scan_keyword(s);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ }
+ lua_pushboolean(L, v);
+ return 1;
+}
+
+static int tokenlib_scan_keyword_cs(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ int v = 0;
+ if (s) {
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ v = tex_scan_keyword_case_sensitive(s);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ }
+ lua_pushboolean(L, v);
+ return 1;
+}
+
+static int tokenlib_scan_csname(lua_State *L)
+{
+ int t;
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ if (lua_toboolean(L, 1)) {
+ /*tex unchecked (maybe backport this option to luatex) */
+ do {
+ tex_get_token();
+ } while (cur_tok == space_token);
+ } else {
+ /*tex checked */
+ tex_get_next();
+ }
+ t = cur_cs ? cs_token_flag + cur_cs : token_val (cur_cmd, cur_chr);
+ if (t >= cs_token_flag) {
+ unsigned char *s = tokenlib_aux_get_cs_text(t - cs_token_flag);
+ if (s) {
+ if (tex_is_active_cs(cs_text(t - cs_token_flag))) {
+ lua_pushstring(L, (char *) (s + 3));
+ } else {
+ lua_pushstring(L, (char *) s);
+ }
+ lmt_memory_free(s);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_integer(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int eq = lua_toboolean(L, 1);
+ halfword v = tex_scan_int(eq, NULL);
+ lua_pushinteger(L, (int) v);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_cardinal(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ unsigned int v = 0;
+ tex_scan_cardinal(&v, 0);
+ lua_pushinteger(L, (unsigned int) v);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_gobble_integer(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int eq = lua_toboolean(L, 1);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ tex_scan_int(eq, NULL);
+ lua_pushboolean(L, ! lmt_error_state.last_intercept);
+ lmt_error_state.intercept = 0;
+ lmt_error_state.last_intercept = 0;
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+inline static void tokenlib_aux_goto_first_candidate(void)
+{
+ do {
+ tex_get_token();
+ } while (cur_cmd == spacer_cmd);
+}
+
+inline static void tokenlib_aux_goto_first_candidate_x(void)
+{
+ do {
+ tex_get_x_token();
+ } while (cur_cmd == spacer_cmd);
+}
+
+inline static void tokenlib_aux_add_utf_char_to_buffer(luaL_Buffer *b, halfword chr)
+{
+ if (chr <= ascii_max) {
+ luaL_addchar(b, (unsigned char) chr);
+ } else {
+ /*
+ unsigned char word[5 + 1];
+ char *uindex = aux_uni2string((char *) word, (unsigned int) chr);
+ *uindex = '\0';
+ luaL_addstring(b, (char *) word);
+ */
+ unsigned char word[5 + 1];
+ aux_uni2string((char *) word, (unsigned int) chr);
+ luaL_addlstring(b, (char *) word, utf8_size(chr));
+ }
+}
+
+/*tex
+ We could of course work with sets or ranges but the bit of duplicate code doesn't harm that
+ much. The hexadecimal variant also deals with \LUA\ serialized numbers like |123.345E67| being
+ equivalent to |0x1.6e0276db950fp+229| (as output by the |q| formatter option).
+
+ Nota Bene: |DECIMAL| can be defined as macro or whatever else; the ms compiler reports an error,
+ so we use |SCANDECIMAL| instead.
+*/
+
+static int tokenlib_scan_float_indeed(lua_State *L, int exponent, int hexadecimal)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int negative = 0;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ tokenlib_aux_goto_first_candidate_x();
+ if (lua_toboolean(L, 1) && (cur_tok == equal_token)) {
+ tokenlib_aux_goto_first_candidate_x();
+ }
+ /*tex we collapse as in |scan_dimen| */
+ while(1) {
+ if (cur_tok == minus_token) {
+ negative = ! negative;
+ } else if (cur_tok != plus_token) {
+ break;
+ }
+ tokenlib_aux_goto_first_candidate_x();
+ }
+ if (negative) {
+ luaL_addchar(&b, '-');
+ }
+ /*tex we accept |[.,]digits| */
+ if (hexadecimal && (cur_tok == zero_token)) {
+ luaL_addchar(&b, '0');
+ tex_get_x_token();
+ if (tex_token_is_hexadecimal(cur_tok)) {
+ luaL_addchar(&b, 'x');
+ goto SCANHEXADECIMAL;
+ } else {
+ goto PICKUPDECIMAL;
+ }
+ } else {
+ goto SCANDECIMAL;
+ }
+ SCANDECIMAL:
+ if (tex_token_is_seperator(cur_tok)) {
+ luaL_addchar(&b, '.');
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (exponent) {
+ goto DECIMALEXPONENT;
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ } else {
+ goto PICKUPDECIMAL;
+ }
+ while (1) {
+ tex_get_x_token();
+ PICKUPDECIMAL:
+ if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (tex_token_is_seperator(cur_tok)) {
+ luaL_addchar(&b, '.');
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ tex_back_input(cur_tok);
+ break;
+ }
+ }
+ } else if (exponent) {
+ goto DECIMALEXPONENT;
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ DECIMALEXPONENT:
+ if (tex_token_is_exponent(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ tex_get_x_token();
+ if (tex_token_is_sign(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ }
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ break;
+ }
+ }
+ }
+ tex_back_input(cur_tok);
+ goto DONE;
+ SCANHEXADECIMAL:
+ tex_get_x_token();
+ if (tex_token_is_seperator(cur_tok)) {
+ luaL_addchar(&b, '.');
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (exponent) {
+ goto HEXADECIMALEXPONENT;
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ } else {
+ /* hm, we could avoid this pushback */
+ tex_back_input(cur_tok);
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (tex_token_is_seperator(cur_tok)) {
+ luaL_addchar(&b, '.');
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ tex_back_input(cur_tok);
+ break;
+ }
+ }
+ } else if (exponent) {
+ goto HEXADECIMALEXPONENT;
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ }
+ HEXADECIMALEXPONENT:
+ if (tex_token_is_xexponent(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ tex_get_x_token();
+ if (tex_token_is_sign(cur_tok)) {
+ /*
+ tex_normal_warning("scanner", "no negative hexadecimal exponent permitted, ignoring minus sign");
+ */
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ }
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ break;
+ }
+ }
+ }
+ tex_back_input(cur_tok);
+ DONE:
+ luaL_pushresult(&b);
+ {
+ int ok = 0;
+ double d = lua_tonumberx(L, -1, &ok);
+ if (ok) {
+ lua_pushnumber(L, d);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_integer_indeed(lua_State *L, int cardinal)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int negative = 0;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ tokenlib_aux_goto_first_candidate_x();
+ if (lua_toboolean(L, 1) && (cur_tok == equal_token)) {
+ tokenlib_aux_goto_first_candidate_x();
+ }
+ /*tex we collapse as in |scan_dimen| */
+ if (! cardinal) {
+ while(1) {
+ if (cur_tok == minus_token) {
+ negative = ! negative;
+ } else if (cur_tok != plus_token) {
+ break;
+ }
+ tokenlib_aux_goto_first_candidate_x();
+ }
+ if (negative) {
+ luaL_addchar(&b, '-');
+ }
+ } else if (cur_tok == minus_token) {
+ tex_normal_warning("scanner", "positive number expected, ignoring minus sign");
+ tokenlib_aux_goto_first_candidate_x();
+ }
+ if (cur_tok == zero_token) {
+ luaL_addchar(&b, '0');
+ tex_get_x_token();
+ if (tex_token_is_hexadecimal(cur_tok)) {
+ luaL_addchar(&b, 'x');
+ goto HEXADECIMAL;
+ } else {
+ goto PICKUPDECIMAL;
+ }
+ } else {
+ goto PICKUPDECIMAL;
+ }
+ while (1) {
+ tex_get_x_token();
+ PICKUPDECIMAL:
+ if (tex_token_is_digit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ HEXADECIMAL:
+ while (1) {
+ tex_get_x_token();
+ if (tex_token_is_xdigit(cur_tok)) {
+ luaL_addchar(&b, (unsigned char) cur_chr);
+ } else {
+ tex_back_input(cur_tok);
+ goto DONE;
+ }
+ }
+ DONE:
+ luaL_pushresult(&b);
+ if (cardinal) {
+ int ok = 0;
+ lua_Unsigned c = lua_tointegerx(L, -1, &ok);
+ if (ok) {
+ lua_pushinteger(L, c);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ int ok = 0;
+ lua_Integer i = lua_tointegerx(L, -1, &ok);
+ if (ok) {
+ lua_pushinteger(L, i);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_float(lua_State *L)
+{
+ return tokenlib_scan_float_indeed(L, 1, 0);
+}
+
+static int tokenlib_scan_real(lua_State *L)
+{
+ return tokenlib_scan_float_indeed(L, 0, 0);
+}
+
+static int tokenlib_scan_luanumber(lua_State* L)
+{
+ return tokenlib_scan_float_indeed(L, 1, 1);
+}
+
+static int tokenlib_scan_luainteger(lua_State* L)
+{
+ return tokenlib_scan_integer_indeed(L, 0);
+}
+
+static int tokenlib_scan_luacardinal(lua_State* L)
+{
+ return tokenlib_scan_integer_indeed(L, 1);
+}
+
+static int tokenlib_scan_scale(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int eq = lua_toboolean(L, 1);
+ halfword val = tex_scan_scale(eq);
+ lua_pushinteger(L, val);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_dimen(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int inf = lua_toboolean(L, 1);
+ int mu = lua_toboolean(L, 2);
+ int eq = lua_toboolean(L, 3);
+ halfword order;
+ halfword val = tex_scan_dimen(mu, inf, 0, eq, &order);
+ lua_pushinteger(L, val);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ if (inf) {
+ lua_pushinteger(L, order);
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+static int tokenlib_gobble_dimen(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int inf = lua_toboolean(L, 1);
+ int mu = lua_toboolean(L, 2);
+ int eq = lua_toboolean(L, 3);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ tex_scan_dimen(mu, inf, 0, eq, NULL);
+ lua_pushboolean(L, ! lmt_error_state.last_intercept);
+ lmt_error_state.intercept = 0;
+ lmt_error_state.last_intercept = 0;
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_skip(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int mu = lua_toboolean(L, 1) ? mu_val_level : glue_val_level;
+ int eq = lua_toboolean(L, 2);
+ halfword v = tex_scan_glue(mu, eq);
+ lmt_push_node_fast(L, v);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_glue(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int mu = lua_toboolean(L, 1) ? mu_val_level : glue_val_level;
+ int eq = lua_toboolean(L, 2);
+ int t = lua_toboolean(L, 3);
+ halfword v = tex_scan_glue(mu, eq);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ if (t) {
+ lua_createtable(L, 5, 0);
+ lua_pushinteger(L, glue_amount(v));
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, glue_stretch(v));
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, glue_shrink(v));
+ lua_rawseti(L, -2, 3);
+ lua_pushinteger(L, glue_stretch_order(v));
+ lua_rawseti(L, -2, 4);
+ lua_pushinteger(L, glue_shrink_order(v));
+ lua_rawseti(L, -2, 5);
+ return 1;
+ } else {
+ lua_pushinteger(L, glue_amount(v));
+ lua_pushinteger(L, glue_stretch(v));
+ lua_pushinteger(L, glue_shrink(v));
+ lua_pushinteger(L, glue_stretch_order(v));
+ lua_pushinteger(L, glue_shrink_order(v));
+ return 5;
+ }
+}
+
+inline static void lmt_token_list_to_lua_tokens(lua_State *L, halfword t)
+{
+ int i = 1;
+ lua_newtable(L);
+ while (t) {
+ halfword n = token_link(t);
+ token_link(t) = null;
+ tokenlib_aux_push_token(L, t);
+ lua_rawseti(L, -2, i++);
+ t = n;
+ }
+}
+
+void lmt_token_register_to_lua(lua_State *L, halfword t)
+{
+ int i = 1;
+ lua_newtable(L);
+ if (t) {
+ t = token_link(t);
+ while (t) {
+ halfword m = tex_get_available_token(token_info(t));
+ tokenlib_aux_push_token(L, m);
+ lua_rawseti(L, -2, i++);
+ t = token_link(t);
+ }
+ }
+}
+
+static int tokenlib_scan_toks(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int macro = lua_toboolean(L, 1);
+ int expand = lua_toboolean(L, 2);
+ halfword defref = lmt_input_state.def_ref;
+ halfword result, t;
+ if (macro) {
+ result = expand ? tex_scan_macro_expand() : tex_scan_macro_normal();
+ } else {
+ result = expand ? tex_scan_toks_expand(0, NULL, 0) : tex_scan_toks_normal(0, NULL);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ lmt_input_state.def_ref = defref;
+ t = token_link(result);
+ token_link(result) = null;
+ tex_put_available_token(result);
+ lmt_token_list_to_lua_tokens(L, t);
+ return 1;
+}
+
+static int tokenlib_scan_tokenlist(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int macro = lua_toboolean(L, 1);
+ int expand = lua_toboolean(L, 2);
+ halfword result;
+ halfword defref = lmt_input_state.def_ref;
+ if (macro) {
+ result = expand ? tex_scan_macro_expand() : tex_scan_macro_normal();
+ } else {
+ result = expand ? tex_scan_toks_expand(0, NULL, 0) : tex_scan_toks_normal(0, NULL);
+ }
+ tokenlib_aux_push_token(L, result);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ lmt_input_state.def_ref = defref;
+ return 1;
+}
+
+/* todo: other call_cmd */
+
+static int tokenlib_scan_string(lua_State *L)
+{
+ /*tex can be simplified, no need for intermediate list */
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate_x(); /* actually this expands a following macro*/
+ switch (cur_cmd) {
+ case left_brace_cmd:
+ {
+ halfword defref = lmt_input_state.def_ref;
+ halfword result = tex_scan_toks_expand(1, NULL, 0);
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ lmt_input_state.def_ref = defref;
+ break;
+ }
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ case tolerant_call_cmd:
+ case tolerant_protected_call_cmd:
+ case tolerant_semi_protected_call_cmd:
+ {
+ halfword t = token_link(cur_chr);
+ lmt_token_list_to_luastring(L, t, 0, 0);
+ tex_flush_token_list(t);
+ break;
+ }
+ case letter_cmd:
+ case other_char_cmd:
+ {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (1) {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ tex_get_x_token();
+ if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd ) {
+ break ;
+ }
+ }
+ tex_back_input(cur_tok);
+ luaL_pushresult(&b);
+ break;
+ }
+ default:
+ {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ break;
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_argument(lua_State *L)
+{
+ /*tex can be simplified, no need for intermediate list */
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate();
+ switch (cur_cmd) {
+ case left_brace_cmd:
+ {
+ halfword defref = lmt_input_state.def_ref;
+ int expand = lua_type(L, 1) == LUA_TBOOLEAN ? lua_toboolean(L, 1) : 1;
+ halfword result = expand ? tex_scan_toks_expand(1, NULL, 0) : tex_scan_toks_normal(1, NULL);
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ lmt_input_state.def_ref = defref;
+ break;
+ }
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ case tolerant_call_cmd:
+ case tolerant_protected_call_cmd:
+ case tolerant_semi_protected_call_cmd:
+ {
+ halfword result;
+ halfword defref = lmt_input_state.def_ref;
+ tex_back_input(right_brace_token + '}');
+ if (lua_type(L, 1) == LUA_TBOOLEAN && ! lua_toboolean(L, 1)) {
+ tex_expand_current_token();
+ result = tex_scan_toks_normal(1, NULL);
+ } else {
+ tex_back_input(cur_tok);
+ result = tex_scan_toks_expand(1, NULL, 0);
+ }
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ lmt_input_state.def_ref = defref;
+ break;
+ }
+ case letter_cmd:
+ case other_char_cmd:
+ {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ // while (1) {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ // get_x_token();
+ // if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd ) {
+ // break ;
+ // }
+ // }
+ // back_input(cur_tok);
+ luaL_pushresult(&b);
+ break;
+ }
+ default:
+ {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ break;
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static void show_right_brace_error(void)
+{
+ tex_handle_error(
+ normal_error_type,
+ "Unbalanced value parsing (in Lua call)",
+ "A { has to be matched by a }."
+ );
+}
+
+static int tokenlib_scan_integer_argument(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int wrapped = 0;
+ tokenlib_aux_goto_first_candidate();
+ if (cur_cmd != left_brace_cmd) {
+ tex_back_input(cur_tok);
+ } else {
+ wrapped = 1;
+ }
+ lua_pushinteger(L, (int) tex_scan_int(0, NULL));
+ if (wrapped) {
+ tokenlib_aux_goto_first_candidate();
+ if (cur_cmd != right_brace_cmd) {
+ show_right_brace_error();
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_dimen_argument(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int wrapped = 0;
+ halfword order = 0;
+ int inf = lua_toboolean(L, 1);
+ int mu = lua_toboolean(L, 2);
+ int eq = lua_toboolean(L, 3);
+ tokenlib_aux_goto_first_candidate();
+ if (cur_cmd != left_brace_cmd) {
+ tex_back_input(cur_tok);
+ } else {
+ wrapped = 1;
+ }
+ lua_pushinteger(L, tex_scan_dimen(mu, inf, 0, eq, &order));
+ if (wrapped) {
+ tokenlib_aux_goto_first_candidate();
+ if (cur_cmd != right_brace_cmd) {
+ show_right_brace_error();
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ if (inf) {
+ lua_pushinteger(L, order);
+ return 2;
+ } else {
+ return 1;
+ }
+}
+
+static int tokenlib_scan_delimited(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword left = lua_type(L, 1) == LUA_TNUMBER ? lmt_tohalfword(L, 1) : 0;
+ halfword right = lua_type(L, 2) == LUA_TNUMBER ? lmt_tohalfword(L, 2) : 0;
+ int expand = (lua_type(L, 3) == LUA_TBOOLEAN) ? expand = lua_toboolean(L, 3) : 1;
+ /* Maybe some more? */
+ if (left) {
+ left = token_val(left == 32 ? spacer_cmd : other_char_cmd, left);
+ }
+ if (right) {
+ right = token_val(right == 32 ? spacer_cmd : other_char_cmd, right);
+ } else {
+ /* actually an error as we now get a runaway argument */
+ }
+ if (expand) {
+ tokenlib_aux_goto_first_candidate_x();
+ } else {
+ tokenlib_aux_goto_first_candidate();
+ }
+ if (! left || cur_tok == left) {
+ halfword defref = lmt_input_state.def_ref;
+ halfword result = get_reference_token();
+ halfword unbalance = 0;
+ halfword p = result;
+ lmt_input_state.def_ref = result;
+ /* */
+ if (expand) {
+ /* like scan_toks_expand, maybe use |get_x_or_protected|. */
+ if (! left) {
+ goto INITIAL1; /* ugly but saved a |back_input| */
+ }
+ while (1) {
+ PICKUP:
+ tex_get_next();
+ INITIAL1:
+ switch (cur_cmd) {
+ case call_cmd:
+ case tolerant_call_cmd:
+ tex_expand_current_token();
+ goto PICKUP;
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ case tolerant_protected_call_cmd:
+ case tolerant_semi_protected_call_cmd:
+ cur_tok = cs_token_flag + cur_cs;
+ goto APPENDTOKEN;
+ case the_cmd:
+ {
+ halfword t = null;
+ halfword h = tex_the_toks(cur_chr, &t);
+ if (h) {
+ set_token_link(p, h);
+ p = t;
+ }
+ goto PICKUP;
+ }
+ default:
+ if (cur_cmd > max_command_cmd) {
+ tex_expand_current_token();
+ goto PICKUP;
+ } else {
+ goto DONEEXPANDING;
+ }
+ }
+ DONEEXPANDING:
+ tex_x_token();
+ if (cur_tok == right) {
+ break;
+ } else if (cur_tok < right_brace_limit) {
+ /* if (cur_cmd < right_brace_cmd) { */
+ if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
+ ++unbalance;
+ } else if (unbalance) {
+ --unbalance;
+ } else {
+ goto FINALYDONE;
+ }
+ }
+ APPENDTOKEN:
+ p = tex_store_new_token(p, cur_tok);
+ }
+ } else {
+ /* like scan_toks_normal */
+ if (! left) {
+ goto INITIAL2; /* ugly but saved a |back_input| */
+ }
+ while (1) {
+ tex_get_token();
+ INITIAL2:
+ if (cur_tok == right) {
+ break;
+ } else if (cur_tok < right_brace_limit) {
+ /* if (cur_cmd < right_brace_cmd) { */
+ if (cur_cmd == left_brace_cmd || cur_cmd == relax_cmd) {
+ ++unbalance;
+ } else if (unbalance) {
+ --unbalance;
+ } else {
+ break;
+ }
+ }
+ p = tex_store_new_token(p, cur_tok);
+ }
+ }
+ FINALYDONE:
+ /* */
+ lmt_input_state.def_ref = defref;
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ } else {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_gobble_until(lua_State *L) /* not ok because we can have different cs's */
+{
+ lua_token *left = tokenlib_aux_check_istoken(L, 1);
+ lua_token *right = tokenlib_aux_check_istoken(L, 2);
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ int level = 1;
+ int l = token_info(left->token);
+ int r = token_info(right->token);
+ int cmd, chr, lcmd, lchr, rcmd, rchr;
+ if (l >= cs_token_flag) {
+ lcmd = eq_type(l - cs_token_flag);
+ lchr = eq_value(l - cs_token_flag);
+ } else {
+ lcmd = token_cmd(l);
+ lchr = token_chr(l);
+ }
+ if (r >= cs_token_flag) {
+ rcmd = eq_type(r - cs_token_flag);
+ rchr = eq_value(r - cs_token_flag);
+ } else {
+ rcmd = token_cmd(l);
+ rchr = token_chr(l);
+ }
+ while (1) {
+ tex_get_token();
+ if (cur_tok >= cs_token_flag) {
+ cmd = eq_type(cur_cs);
+ chr = eq_value(cur_cs);
+ } else {
+ cmd = cur_cmd;
+ chr = cur_chr;
+ }
+ if (cmd == lcmd && chr == lchr) {
+ ++level;
+ } else if (cmd == rcmd && chr == rchr) {
+ --level;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 0;
+}
+
+/* only csnames, todo: no need for a token list .. make a direct tostring */
+
+static int tokenlib_grab_until(lua_State *L)
+{
+ lua_token *left = tokenlib_aux_check_istoken(L, 1);
+ lua_token *right = tokenlib_aux_check_istoken(L, 2);
+ int l = token_info(left->token);
+ int r = token_info(right->token);
+ int lstr = 0;
+ int rstr = 0;
+ if (l >= cs_token_flag) {
+ lstr = cs_text(l - cs_token_flag);
+ }
+ if (r >= cs_token_flag) {
+ rstr = cs_text(r - cs_token_flag);
+ }
+ if (lstr && rstr) {
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword defref = lmt_input_state.def_ref;
+ halfword result = get_reference_token();
+ halfword p = result;
+ int level = 1;
+ int nospace = lua_toboolean(L, 3);
+ int strip = lmt_optinteger(L, 4, -1);
+ while (1) {
+ tex_get_token();
+ if (cur_tok >= cs_token_flag) {
+ int str = cs_text(cur_tok - cs_token_flag);
+ if (str == lstr) {
+ ++level;
+ } else if (str == rstr) {
+ --level;
+ if (level == 0) {
+ break;
+ }
+ }
+ }
+ p = tex_store_new_token(p, cur_tok);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ lmt_input_state.def_ref = defref;
+ lmt_token_list_to_luastring(L, result, nospace, strip);
+ tex_flush_token_list(result);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_scan_word(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate_x();
+ if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (1) {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ tex_get_x_token();
+ if (cur_cmd != letter_cmd && cur_cmd != other_char_cmd) {
+ break;
+ }
+ }
+ if (! (lua_toboolean(L, 1) && ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)))) {
+ tex_back_input(cur_tok);
+ }
+ luaL_pushresult(&b);
+ } else {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_letters(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate_x();
+ if (cur_cmd == letter_cmd) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (1) {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ tex_get_x_token();
+ if (cur_cmd != letter_cmd) {
+ break ;
+ }
+ }
+ if (! (lua_toboolean(L, 1) && ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)))) {
+ tex_back_input(cur_tok);
+ }
+ luaL_pushresult(&b);
+ } else {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_char(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
+ if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd) {
+ int c = lmt_tointeger(L, 1);
+ if (c == cur_chr) {
+ lua_pushboolean(L, 1);
+ } else {
+ lua_pushboolean(L, 0);
+ tex_back_input(cur_tok);
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ tex_back_input(cur_tok);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_next_char(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ const char mapping[14][2] = { "\\", "{", "}", "$", "&", "\n", "#", "^", "_", " ", "", "", "", "%" };
+ tex_get_token();
+ switch (cur_cmd) {
+ case escape_cmd:
+ case left_brace_cmd:
+ case right_brace_cmd:
+ case math_shift_cmd:
+ case alignment_tab_cmd:
+ case end_line_cmd:
+ case parameter_cmd:
+ case superscript_cmd:
+ case subscript_cmd:
+ case ignore_cmd:
+ case spacer_cmd:
+ case comment_cmd:
+ lua_pushstring(L, mapping[cur_cmd]);
+ break;
+ case letter_cmd:
+ case other_char_cmd:
+ {
+ char buffer[6];
+ char *uindex = aux_uni2string((char *) buffer, (unsigned int) cur_chr);
+ *uindex = '\0';
+ lua_pushstring(L, buffer);
+ break;
+ }
+ default:
+ lua_pushstring(L, "");
+ break;
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_is_next_char(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
+ if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd ) {
+ int c = lmt_tointeger(L, 1);
+ lua_pushboolean(L, c == cur_chr);
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ tex_back_input(cur_tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_peek_next(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ if (lua_toboolean(L, 1)) {
+ tokenlib_aux_goto_first_candidate();
+ } else {
+ tex_get_token();
+ }
+ // make_new_token(L, cur_cmd, cur_chr, cur_cs);
+ tokenlib_aux_make_new_token_tok(L, cur_tok);
+ tex_back_input(cur_tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_peek_next_expanded(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ if (lua_toboolean(L, 1)) {
+ tokenlib_aux_goto_first_candidate_x();
+ } else {
+ tex_get_x_token();
+ }
+ // make_new_token(L, cur_cmd, cur_chr, cur_cs);
+ tokenlib_aux_make_new_token_tok(L, cur_tok);
+ tex_back_input(cur_tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_peek_next_char(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate(); /* no expansion */ /* optional expansion ? */ /* gobbles spaces */
+ if (cur_cmd == letter_cmd || cur_cmd == other_char_cmd ) {
+ lua_pushinteger(L, cur_chr);
+ } else {
+ lua_pushnil(L);
+ }
+ tex_back_input(cur_tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+/*tex
+
+ This next two are experimental and might evolve. It will take a while before
+ I decide if this is the way to go. They are not used in critical code so we
+ have all time of the world.
+
+*/
+
+static int tokenlib_scan_key(lua_State *L)
+{
+ int c1 = lmt_optinteger(L, 1, '\0');
+ int c2 = lmt_optinteger(L, 2, '\0');
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate_x();
+ if ((cur_cmd == letter_cmd || cur_cmd == other_char_cmd) && (cur_chr != c1) && (cur_chr != c2)) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (1) {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ tex_get_x_token();
+ if ((cur_cmd != letter_cmd && cur_cmd != other_char_cmd) || (cur_chr == c1) || (cur_chr == c2)) {
+ break ;
+ }
+ }
+ /*
+ if (! (lua_toboolean(L, 1) && (cur_cmd == spacer_cmd || cur_cmd == relax_cmd))) {
+ back_input(cur_tok);
+ }
+ */
+ tex_back_input(cur_tok);
+ luaL_pushresult(&b);
+ } else {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+/* todo: other call_cmd */
+/* todo: non expandable option */
+
+static int tokenlib_scan_value(lua_State *L)
+{
+ /*tex can be simplified, no need for intermediate list */
+ int c1 = lmt_optinteger(L, 1, '\0');
+ int c2 = lmt_optinteger(L, 2, '\0');
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tokenlib_aux_goto_first_candidate_x(); /* no _x */
+ switch (cur_cmd) {
+ case left_brace_cmd:
+ {
+ halfword result;
+ halfword defref = lmt_input_state.def_ref;
+ result = tex_scan_toks_expand(1, NULL, 0);
+ lmt_input_state.def_ref = defref;
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ }
+ break;
+ /*
+ case call_cmd:
+ {
+ halfword t = cur_cs ? cs_token_flag + cur_cs : token_val(cur_cmd, cur_chr);
+ if (t >= cs_token_flag) {
+ unsigned char *s = get_cs_text(t - cs_token_flag);
+ if (s) {
+ // if (is_active_cs(cs_text(t - cs_token_flag))) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ cs_name_to_buffer(s);
+ luaL_pushresult(&b);
+ lmt_memory_free(s);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ break;
+ */
+ case letter_cmd:
+ case other_char_cmd:
+ {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (1) {
+ switch (cur_cmd) {
+ case left_brace_cmd:
+ {
+ halfword result;
+ halfword defref = lmt_input_state.def_ref;
+ result = tex_scan_toks_expand(1, NULL, 0);
+ lmt_input_state.def_ref = defref;
+ lmt_token_list_to_luastring(L, result, 0, 0);
+ tex_flush_token_list(result);
+ luaL_addchar(&b, '{');
+ luaL_addvalue(&b);
+ luaL_addchar(&b, '}');
+ }
+ break;
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ case tolerant_call_cmd:
+ case tolerant_protected_call_cmd:
+ case tolerant_semi_protected_call_cmd:
+ {
+ /*tex We need to add a space. */
+ halfword t = cur_cs ? cs_token_flag + cur_cs : token_val(cur_cmd, cur_chr);
+ if (t >= cs_token_flag) {
+ unsigned char *s = tokenlib_aux_get_cs_text(t - cs_token_flag);
+ if (s) {
+ if (tex_is_active_cs(cs_text(t - cs_token_flag))) {
+ lua_pushstring(L, (char *) (s + 3));
+ luaL_addvalue(&b);
+ } else {
+ luaL_addchar(&b, '\\');
+ lua_pushstring(L, (char *) s);
+ luaL_addvalue(&b);
+ luaL_addchar(&b, ' ');
+ }
+ lmt_memory_free(s);
+ }
+ }
+ }
+ break;
+ case letter_cmd:
+ case other_char_cmd:
+ if (cur_chr == c1 || cur_chr == c2) {
+ goto DONE;
+ } else {
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ }
+ break;
+ default:
+ /* what to do */
+ tokenlib_aux_add_utf_char_to_buffer(&b, cur_chr);
+ break;
+ }
+ tex_get_x_token();
+ }
+ DONE:
+ tex_back_input(cur_tok);
+ luaL_pushresult(&b);
+ }
+ break;
+ default:
+ {
+ tex_back_input(cur_tok);
+ lua_pushnil(L);
+ }
+ break;
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+/*tex Till here. */
+
+static int tokenlib_future_expand(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword spa = null;
+ halfword yes = tex_get_token(); /* no expansion */
+ halfword nop = tex_get_token(); /* no expansion */
+ while (1) {
+ halfword t = tex_get_token();
+ switch (t) {
+ case spacer_cmd:
+ spa = t; /* preserves spaces */
+ break;
+ case letter_cmd:
+ case other_char_cmd:
+ if (lua_tointeger(L, 1) == cur_chr) {
+ tex_back_input(t);
+ tex_back_input(yes);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 0;
+ }
+ default:
+ tex_back_input(t);
+ if (spa && lua_toboolean(L, 2)) {
+ tex_back_input(spa);
+ }
+ tex_back_input(nop);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int tokenlib_scan_code(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tex_get_x_token();
+ if (cur_cmd <= max_char_code_cmd) {
+ int cc = lmt_optinteger(L, 1, DEFAULT_SCAN_CODE_SET);
+ if (cc & (1 << (cur_cmd))) {
+ lua_pushinteger(L, (int) cur_chr);
+ } else {
+ lua_pushnil(L);
+ tex_back_input(cur_tok);
+ }
+ } else {
+ lua_pushnil(L);
+ tex_back_input(cur_tok);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_scan_token_code(lua_State *L)
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ halfword t = tex_get_token();
+ /* maybe treat spaces as such */
+ if (cur_cmd <= max_char_code_cmd) {
+ if (DEFAULT_SCAN_CODE_SET & (1 << (cur_cmd))) {
+ lua_pushinteger(L, (int) cur_chr);
+ } else {
+ lua_pushnil(L);
+ tex_back_input(t);
+ }
+ } else {
+ lua_pushnil(L);
+ tex_back_input(t);
+ }
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+static int tokenlib_is_token(lua_State *L)
+{
+ lua_pushboolean(L, tokenlib_aux_maybe_istoken(L, 1) ? 1 : 0);
+ return 1;
+}
+
+static int tokenlib_expand(lua_State *L)
+{
+ (void) L;
+ tex_expand_current_token();
+ /* should we push back? */
+ return 0;
+}
+
+static int tokenlib_is_defined(lua_State *L)
+{
+ int b = 0;
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, 0);
+ b = (cs != undefined_control_sequence) && (eq_type(cs) != undefined_cs_cmd);
+ }
+ }
+ lua_pushboolean(L, b);
+ return 1;
+}
+
+/*tex
+ The next two will be redone so that they check if valid tokens are created. For that I need to
+ clean up the \TEX\ end a bit more so that we can do proper cmd checking.
+*/
+
+static int tokenlib_create(lua_State *L)
+{
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ int cs = 0;
+ int chr = (int) lua_tointeger(L, 1);
+ int cmd = (int) luaL_optinteger(L, 2, tex_get_cat_code(cat_code_table_par, chr));
+ switch (cmd) {
+ case escape_cmd:
+ case ignore_cmd:
+ case comment_cmd:
+ case invalid_char_cmd:
+ /* tex_formatted_warning("token lib","not a good token, catcode %i can not be returned, so 12 will be used",(int) cmd); */
+ cmd = other_char_cmd;
+ break;
+ case active_char_cmd:
+ cs = tex_active_to_cs(chr, ! lmt_hash_state.no_new_cs);
+ cmd = eq_type(cs);
+ chr = eq_value(cs);
+ break;
+ }
+ tokenlib_aux_make_new_token(L, cmd, chr, cs);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, lua_toboolean(L, 2));
+ int cmd = eq_type(cs);
+ int chr = eq_value(cs);
+ tokenlib_aux_make_new_token(L, cmd, chr, cs);
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ }
+ default:
+ {
+ lua_pushnil(L);
+ break;
+ }
+ }
+ return 1;
+}
+
+/*tex
+ The order of arguments is somewhat strange but it comes from \LUATEX.
+*/
+
+static int tokenlib_new(lua_State *L)
+{
+ int chr = 0;
+ int cmd = 0;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ cmd = (int) tokenlib_aux_get_command_id(lua_tostring(L, 1));
+ chr = (int) luaL_optinteger(L, 2, 0);
+ break;
+ case LUA_TNUMBER:
+ chr = (int) lua_tointeger(L, 1);
+ cmd = (int) luaL_optinteger(L, 2, 0);
+ break;
+ default:
+ break;
+ }
+ tokenlib_aux_make_new_token(L, cmd, chr, 0);
+ return 1;
+}
+
+/*tex
+ The next few are more test functions and at some point they will replace the above or at least
+ be combined so that we do proper checking.
+*/
+
+static int tokenlib_get_cmdchrcs(lua_State* L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, 0);
+ int cmd = eq_type(cs);
+ int chr = eq_value(cs);
+ if (! lua_toboolean(L, 2)) {
+ /*tex This option is only for diagnostics! */
+ chr = tokenlib_aux_to_valid_index(cmd, chr);
+ }
+ lua_pushinteger(L, cmd);
+ lua_pushinteger(L, chr); /* or index */
+ lua_pushinteger(L, cs);
+ return 3;
+ }
+ return 0;
+}
+
+static int tokenlib_scan_cmdchr(lua_State *L)
+{
+ int cmd, chr;
+ halfword tok = tex_get_token();
+ if (tok >= cs_token_flag) {
+ tok -= cs_token_flag;
+ cmd = eq_type(tok);
+ chr = eq_value(tok);
+ } else {
+ cmd = token_cmd(tok);
+ chr = token_chr(tok);
+ }
+ lua_pushinteger(L, cmd);
+ lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
+ return 2;
+}
+
+static int tokenlib_scan_cmdchr_expanded(lua_State *L)
+{
+ int cmd, chr;
+ halfword tok = tex_get_x_token();
+ if (tok >= cs_token_flag) {
+ tok -= cs_token_flag;
+ cmd = eq_type(tok);
+ chr = eq_value(tok);
+ } else {
+ cmd = token_cmd(tok);
+ chr = token_chr(tok);
+ }
+ lua_pushinteger(L, cmd);
+ lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
+ return 2;
+}
+
+
+static int tokenlib_get_cstoken(lua_State* L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ lua_pushinteger(L, (lua_Integer) tex_string_locate(s, l, 0) + cs_token_flag);
+ return 1;
+ }
+ return 0;
+}
+
+static int tokenlib_getprimitives(lua_State *L)
+{
+ int cs = 0;
+ int nt = 0;
+ int raw = lua_toboolean(L, 1);
+ lua_createtable(L, prim_size, 0);
+ while (cs < prim_size) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0 && (get_prim_origin(cs) != no_command)) {
+ char *ss = tex_makecstring(s);
+ int cmd = prim_eq_type(cs);
+ int chr = prim_equiv(cs);
+ if (! raw) {
+ chr = tokenlib_aux_to_valid_index(cmd, chr);
+ }
+ lua_createtable(L, 4, 0);
+ lua_pushinteger(L, cmd);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, chr);
+ lua_rawseti(L, -2, 2);
+ lua_pushstring(L, ss);
+ lua_rawseti(L, -2, 3);
+ lua_pushinteger(L, prim_origin(cs));
+ lua_rawseti(L, -2, 4);
+ lua_rawseti(L, -2, ++nt);
+ lmt_memory_free(ss);
+ }
+ cs++;
+ }
+ return 1;
+}
+
+/*tex token instance functions */
+
+static int tokenlib_free(lua_State *L)
+{
+ /* lua_token *n = check_istoken(L, 1); */
+ lua_token *n = lua_touserdata(L, 1);
+ if (n->origin == token_origin_lua) {
+ if (token_link(n->token)) {
+ tex_flush_token_list(n->token);
+ } else {
+ tex_put_available_token(n->token);
+ }
+ } else {
+ /*tex This can't happen (yet). */
+ }
+ return 1;
+}
+
+/*tex fast accessors */
+
+inline static int tokenlib_get_command(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword t = token_info(n->token);
+ lua_pushinteger(L, (t >= cs_token_flag) ? (int) eq_type(t - cs_token_flag) : token_cmd(t));
+ return 1;
+}
+
+inline static int tokenlib_get_index(lua_State *L)
+{
+ int cmd, chr;
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ if (tok >= cs_token_flag) {
+ tok -= cs_token_flag;
+ cmd = eq_type(tok);
+ chr = eq_value(tok);
+ } else {
+ cmd = token_cmd(tok);
+ chr = token_chr(tok);
+ }
+ lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
+ return 1;
+}
+
+inline static int tokenlib_get_range(lua_State *L)
+{
+ int cmd;
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ cmd = (int) lua_tointeger(L, 1);
+ } else {
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
+ }
+ if (cmd >= 0 && cmd <= last_cmd) {
+ command_item item = lmt_interface.command_names[cmd];
+ lua_pushinteger(L, item.kind);
+ switch (item.kind) {
+ case unused_command_item:
+ lua_pushboolean(L, 0);
+ lua_pushboolean(L, 0);
+ break;
+ case regular_command_item:
+ case character_command_item:
+ case register_command_item:
+ case internal_command_item:
+ case reference_command_item:
+ case data_command_item:
+ lua_pushinteger(L, item.min);
+ lua_pushinteger(L, item.max);
+ break;
+ case token_command_item:
+ case node_command_item:
+ lua_pushboolean(L, 0);
+ lua_pushboolean(L, 0);
+ break;
+ }
+ lua_pushinteger(L, item.fixedvalue);
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
+inline static int tokenlib_get_cmdname(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ int cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
+ lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
+ return 1;
+}
+
+void lmt_push_cmd_name(lua_State *L, int cmd)
+{
+ if (cmd >= 0) {
+ lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+inline static int tokenlib_get_csname(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ if (tok >= cs_token_flag) {
+ unsigned char *s = tokenlib_aux_get_cs_text(tok - cs_token_flag);
+ if (s) {
+ if (tex_is_active_cs(cs_text(tok - cs_token_flag))) {
+ lua_pushstring(L, (char *) (s + 3));
+ } else {
+ lua_pushstring(L, (char *) s);
+ }
+ lmt_memory_free(s);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+inline static int tokenlib_get_id(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ lua_pushinteger(L, n->token);
+ return 1;
+}
+
+inline static int tokenlib_get_tok(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushinteger(L, tok);
+ return 1;
+}
+
+inline static int tokenlib_get_active(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ int result = 0;
+ if (tok >= cs_token_flag) {
+ unsigned char *s = tokenlib_aux_get_cs_text(tok - cs_token_flag);
+ if (s) {
+ result = tex_is_active_cs(cs_text(tok - cs_token_flag));
+ lmt_memory_free(s);
+ }
+ }
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+inline static int tokenlib_get_expandable(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
+ lua_pushboolean(L, cmd > max_command_cmd);
+ return 1;
+}
+
+inline static int tokenlib_get_protected(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
+ lua_pushboolean(L, is_protected_cmd(cmd));
+ return 1;
+}
+
+inline static int tokenlib_get_tolerant(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ halfword cmd = (tok >= cs_token_flag) ? eq_type(tok - cs_token_flag) : token_cmd(tok);
+ lua_pushboolean(L, is_tolerant_cmd(cmd));
+ return 1;
+}
+
+inline static int tokenlib_get_noaligned(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, noaligned_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_primitive(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, primitive_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_permanent(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, permanent_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_immutable(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, immutable_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_mutable(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, mutable_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_frozen(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, frozen_flag_bit));
+ return 1;
+}
+
+inline static int tokenlib_get_instance(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, instance_flag_bit));
+ return 1;
+}
+
+
+inline static int tokenlib_get_untraced(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag && has_eq_flag_bits(tok - cs_token_flag, untraced_flag_bit));
+ return 1;
+}
+
+
+inline static int tokenlib_get_flags(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_pushboolean(L, tok >= cs_token_flag ? eq_flag(tok - cs_token_flag) : 0);
+ return 1;
+}
+
+inline static int tokenlib_get_parameters(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ if (tok >= cs_token_flag && is_call_cmd(eq_type(tok - cs_token_flag))) {
+ halfword v = eq_value(tok - cs_token_flag);
+ if (v && token_link(v)) {
+ lua_pushinteger(L, get_token_parameters(v));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 0;
+}
+
+static int tokenlib_getfield(lua_State *L)
+{
+ const char *s = lua_tostring(L, 2);
+ if (lua_key_eq(s, command)) {
+ return tokenlib_get_command(L);
+ } else if (lua_key_eq(s, index)) {
+ return tokenlib_get_index(L);
+ } else if (lua_key_eq(s, cmdname)) {
+ return tokenlib_get_cmdname(L);
+ } else if (lua_key_eq(s, csname)) {
+ return tokenlib_get_csname(L);
+ } else if (lua_key_eq(s, id)) {
+ return tokenlib_get_id(L);
+ } else if (lua_key_eq(s, tok)) {
+ return tokenlib_get_tok(L);
+ } else if (lua_key_eq(s, active)) {
+ return tokenlib_get_active(L);
+ } else if (lua_key_eq(s, expandable)) {
+ return tokenlib_get_expandable(L);
+ } else if (lua_key_eq(s, protected)) {
+ return tokenlib_get_protected(L);
+ } else if (lua_key_eq(s, frozen)) {
+ return tokenlib_get_frozen(L);
+ } else if (lua_key_eq(s, tolerant)) {
+ return tokenlib_get_tolerant(L);
+ } else if (lua_key_eq(s, noaligned)) {
+ return tokenlib_get_noaligned(L);
+ } else if (lua_key_eq(s, permanent)) {
+ return tokenlib_get_permanent(L);
+ } else if (lua_key_eq(s, immutable)) {
+ return tokenlib_get_immutable(L);
+ } else if (lua_key_eq(s, mutable)) {
+ return tokenlib_get_mutable(L);
+ } else if (lua_key_eq(s, primitive)) {
+ return tokenlib_get_primitive(L);
+ } else if (lua_key_eq(s, instance)) {
+ return tokenlib_get_instance(L);
+ } else if (lua_key_eq(s, untraced)) {
+ return tokenlib_get_untraced(L);
+ } else if (lua_key_eq(s, flags)) {
+ return tokenlib_get_flags(L);
+ } else if (lua_key_eq(s, parameters)) {
+ return tokenlib_get_parameters(L);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_get_fields(lua_State *L)
+{
+ halfword cmd = null;
+ halfword chr = null;
+ int flags = 0;
+ int onlyflags = lua_toboolean(L, 2);
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *str = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ lua_createtable(L, 0, onlyflags ? 0 : 5);
+ halfword cs = tex_string_locate(str, l, 0);
+ cmd = eq_type(cs);
+ chr = eq_value(cs);
+ flags = eq_flag(cs);
+ if (! onlyflags) {
+ lua_push_key(csname);
+ lua_pushstring(L, str);
+ lua_rawset(L, -3);
+ }
+ break;
+ } else {
+ return 0;
+ }
+ }
+ case LUA_TUSERDATA:
+ {
+ lua_token *n = tokenlib_aux_check_istoken(L, 1);
+ halfword tok = token_info(n->token);
+ lua_createtable(L, 0, onlyflags ? 0 : 5);
+ if (tok >= cs_token_flag) {
+ int t = tok - cs_token_flag;
+ unsigned char* str = tokenlib_aux_get_cs_text(t);
+ if (str) {
+ if (! onlyflags) {
+ lua_push_key(csname);
+ if (tex_is_active_cs(cs_text(t))) {
+ lua_push_key(active);
+ lua_pushboolean(L, 1);
+ lua_rawset(L, -3);
+ lua_pushstring(L, (char*) (str + 3));
+ } else {
+ lua_pushstring(L, (char*) str);
+ }
+ lua_rawset(L, -3);
+ }
+ lmt_memory_free(str);
+ }
+ cmd = eq_type(t);
+ chr = eq_value(t);
+ } else {
+ cmd = token_cmd(tok);
+ chr = token_chr(tok);
+ }
+ break;
+ }
+ default:
+ return 0;
+
+ }
+ if (flags) {
+ if (is_frozen (flags)) { lua_push_key(frozen); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_noaligned(flags)) { lua_push_key(noaligned); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_permanent(flags)) { lua_push_key(permanent); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_immutable(flags)) { lua_push_key(immutable); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_mutable (flags)) { lua_push_key(mutable); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_primitive(flags)) { lua_push_key(primitive); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_instance (flags)) { lua_push_key(instance); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_untraced (flags)) { lua_push_key(untraced); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (flags) { lua_push_key(flags); lua_pushinteger(L, flags); lua_rawset(L, -3); }
+ if (is_protected(cmd)) { lua_push_key(protected); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ if (is_tolerant (cmd)) { lua_push_key(tolerant); lua_pushboolean(L, 1); lua_rawset(L, -3); }
+ }
+ if (! onlyflags) {
+ lua_push_key(command);
+ lua_pushinteger(L, cmd);
+ lua_rawset(L, -3);
+ lua_push_key(cmdname);
+ lua_push_key_by_index(lmt_interface.command_names[cmd].lua);
+ lua_rawset(L, -3);
+ lua_push_key(index); /* or value */
+ lua_pushinteger(L, tokenlib_aux_to_valid_index(cmd, chr));
+ lua_rawset(L, -3);
+ if (is_call_cmd(cmd) && chr && token_link(chr)) {
+ lua_push_key(parameters);
+ lua_pushinteger(L, get_token_parameters(token_link(chr)));
+ lua_rawset(L, -3);
+ }
+ }
+ return 1;
+}
+
+/*tex end */
+
+static int tokenlib_equal(lua_State* L)
+{
+ lua_token* n = tokenlib_aux_check_istoken(L, 1);
+ lua_token* m = tokenlib_aux_check_istoken(L, 2);
+ lua_pushboolean(L, token_info(n->token) == token_info(m->token));
+ return 1;
+}
+
+static int tokenlib_tostring(lua_State* L)
+{
+ lua_token* n = tokenlib_aux_maybe_istoken(L, 1);
+ if (n) {
+ halfword id = n->token;
+ halfword tok = token_info(id);
+ halfword lnk = token_link(id);
+ char* ori = (n->origin == token_origin_lua) ? "lua" : "tex";
+ halfword cmd, chr;
+ unsigned char* csn = NULL;
+ unsigned char* csp = NULL;
+ const char* cmn = NULL;
+ if (tok >= cs_token_flag) {
+ tok -= cs_token_flag;
+ csn = tokenlib_aux_get_cs_text(tok);
+ csp = csn;
+ if (csn && tex_is_active_cs(cs_text(tok))) {
+ csn += 3;
+ }
+ cmd = eq_type(tok);
+ chr = eq_value(tok);
+ } else {
+ cmd = token_cmd(tok);
+ chr = token_chr(tok);
+ }
+ if (! cmn) {
+ if (cmd >= first_cmd && cmd <= last_cmd) {
+ cmn = lmt_interface.command_names[cmd].name;
+ switch (lmt_interface.command_names[cmd].base) {
+ case ignore_entry:
+ case direct_entry:
+ break;
+ default:
+ chr -= lmt_interface.command_names[cmd].base;
+ }
+ } else {
+ cmn = "bad_token";
+ }
+ }
+ if (csn && csn[0] != '\0') {
+ if (lnk) {
+ lua_pushfstring(L, "<%s token : %d => %d : %s : %s %d>", ori, id, lnk, (char *) csn, cmn, chr);
+ } else {
+ lua_pushfstring(L, "<%s token : %d == %s : %s %d>", ori, id, (char *) csn, cmn, chr);
+ }
+ } else {
+ if (! lnk) {
+ lua_pushfstring(L, "<%s token : %d == %s %d>", ori, id, cmn, chr);
+ } else if (cmd == 0 && chr == 0) {
+ /*tex A zero escape token is less likely than an initial list refcount token. */
+ lua_pushfstring(L, "<%s token : %d => %d : refcount>", ori, id, lnk);
+ } else {
+ lua_pushfstring(L, "<%s token : %d => %d : %s %d>", ori, id, lnk, cmn, chr);
+ }
+ }
+ if (csp) {
+ lmt_memory_free(csp);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_package_tostring(lua_State *L)
+{
+ lua_token_package *n = tokenlib_aux_check_ispackage(L, 1);
+ if (n) {
+ if (is_call_cmd(n->cmd)) {
+ lua_pushfstring(L, "<tex token package %d: %d %d %d>", n->cs, n->cmd, n->chr, get_token_reference(n->chr));
+ } else {
+ lua_pushfstring(L, "<tex token package %d: %d %d>", n->cs, n->cmd, n->chr);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int tokenlib_type(lua_State *L)
+{
+ if (tokenlib_aux_maybe_istoken(L, 1)) {
+ lua_push_key(token);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_scan_token(lua_State *L) /*tex Similer to |get_next_expanded|, expands and no skips. */
+{
+ saved_tex_scanner texstate = tokenlib_aux_save_tex_scanner();
+ tex_get_x_token();
+ // make_new_token(L, cur_cmd, cur_chr, cur_cs);
+ tokenlib_aux_make_new_token_tok(L, cur_tok);
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+/*tex This is always a copy! */
+
+static int tokenlib_scan_box(lua_State *L)
+{
+ saved_tex_scanner texstate;
+ if (lua_gettop(L) > 0) {
+ const char *s = lua_tostring(L, 1);
+ halfword code = -1 ;
+ if (lua_key_eq(s, hbox)) {
+ code = vtop_code + hmode;
+ } else if (lua_key_eq(s, vbox)) {
+ code = vtop_code + vmode;
+ } else if (lua_key_eq(s, vtop)) {
+ code = vtop_code;
+ }
+ if (code >= 0) {
+ tex_back_input(token_val(make_box_cmd, code));
+ }
+ }
+ /*tex
+ This is a tricky call as we are in \LUA\ and therefore mess with the main loop.
+ */
+ texstate = tokenlib_aux_save_tex_scanner();
+ lmt_push_node_fast(L, tex_local_scan_box());
+ tokenlib_aux_unsave_tex_scanner(texstate);
+ return 1;
+}
+
+/* experiment */
+
+/* [catcodetable] csname content : \def\csname{content} */
+/* [catcodetable] csname content global : \gdef\csname{content} */
+/* [catcodetable] csname : \def\csname{} */
+
+/* TODO: check for a quick way to set a macro to empty (HH) */
+
+static int tokenlib_get_meaning(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ halfword cs = tex_string_locate(name, lname, 0);
+ halfword cmd = eq_type(cs);
+ if (is_call_cmd(cmd)) {
+ int chr = eq_value(cs);
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ lmt_token_list_to_lua(L, token_link(chr));
+ } else {
+ lmt_token_register_to_lua(L, chr);
+ }
+ } else {
+ char *str = tex_tokenlist_to_tstring(chr, 1, NULL, 0, 0, 0);
+ lua_pushstring(L, str ? str : "");
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*tex
+
+ The final line of this routine is slightly subtle; at least, the author didn't think about it
+ until getting burnt! There is a used-up token list on the stack, namely the one that contained
+ |end_write_token|. We insert this artificial |\endwrite| to prevent runaways, as explained
+ above.) If it were not removed, and if there were numerous writes on a single page, the stack
+ would overflow.
+
+*/
+
+static void tokenlib_aux_expand_macros_in_tokenlist(halfword p)
+{
+ halfword old_mode;
+ halfword q = tex_get_available_token(right_brace_token + '}');
+ halfword r = tex_get_available_token(deep_frozen_end_write_token);
+ token_link(q) = r;
+ tex_begin_inserted_list(q);
+ tex_begin_token_list(p, write_text);
+ q = tex_get_available_token(left_brace_token + '{'); /* not needed when we expand with first arg == 1 */
+ tex_begin_inserted_list(q);
+ /*tex Now we're ready to scan |{<token list>}| |\endwrite|. */
+ old_mode = cur_list.mode;
+ cur_list.mode = 0;
+ /*tex Disable |\prevdepth|, |\spacefactor|, |\lastskip|, |\prevgraf|. */
+ cur_cs = 0; /* was write_loc i.e. eq of \write */
+ /*tex Expand macros, etc. */
+ tex_scan_toks_expand(0, NULL, 0); /* could be 1 and no left brace above */
+ // tex_scan_toks_expand(1, NULL); /* could be 1 and no left brace above */
+ tex_get_token();
+ if (cur_tok != deep_frozen_end_write_token) {
+ /*tex Recover from an unbalanced write command */
+ tex_handle_error(
+ normal_error_type,
+ "Unbalanced token list expansion",
+ "On this page there's a token list expansion with fewer real {'s than }'s. I can't\n"
+ "handle that very well; good luck."
+ );
+ do {
+ tex_get_token();
+ } while (cur_tok != deep_frozen_end_write_token);
+ }
+ cur_list.mode = old_mode;
+ /*tex Conserve stack space. */
+ tex_end_token_list();
+}
+
+static int tokenlib_get_macro(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ halfword cs = tex_string_locate(name, lname, 0);
+ halfword cmd = eq_type(cs);
+ if (is_call_cmd(cmd)) {
+ halfword chr = eq_value(cs);
+ char *str = NULL;
+ if (lua_toboolean(L, 2)) {
+ tokenlib_aux_expand_macros_in_tokenlist(chr); // todo: use return value instead of def_ref
+ str = tex_tokenlist_to_tstring(lmt_input_state.def_ref, 1, NULL, 0, 0, 0);
+ tex_flush_token_list(lmt_input_state.def_ref);
+ } else {
+ str = tex_tokenlist_to_tstring(chr, 1, NULL, 1, 0, 0);
+ }
+ lua_pushstring(L, str ? str : "");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* maybe just memoryword */
+
+// todo: node lists:
+//
+// [internal|register]_[glue|mu_glue]_reference_cmd
+// specification_reference_cmd
+// box_reference_cmd
+
+static int tokenlib_push_macro(lua_State *L) // todo: just store cmd and flag together
+{
+ /*tex
+ We need to check for a valid hit, but what is best here, for instance using |(cmd >= call_cmd)|
+ is not okay as we miss a lot then.
+ */
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ if (lname > 0) {
+ halfword cs = tex_string_locate(name, lname, 0);
+ singleword cmd = eq_type(cs);
+ halfword chr = eq_value(cs);
+ quarterword global = lua_toboolean(L, 2) ? add_global_flag(0) : 0; /* how */
+ if (is_call_cmd(cmd)) {
+ tex_add_token_reference(chr);
+ }
+ tokenlib_aux_make_new_package(L, cmd, eq_flag(cs), chr, cs, global);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+char *lmt_get_expansion(halfword head, int *len)
+{
+ char *str = NULL;
+ halfword ref = get_reference_token();
+ set_token_link(ref, head);
+ tokenlib_aux_expand_macros_in_tokenlist(ref); // todo: use return value instead of def_ref
+ str = tex_tokenlist_to_tstring(lmt_input_state.def_ref, 1, len, 0, 0, 0);
+ tex_flush_token_list(lmt_input_state.def_ref);
+ tex_flush_token_list(ref);
+ return str;
+}
+
+static int tokenlib_get_expansion(lua_State* L)
+{
+ const char *str;
+ size_t len;
+ int slot = 1;
+ halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
+ if (! tex_valid_catcode_table(ct)) {
+ ct = cat_code_table_par;
+ }
+ str = lua_tolstring(L, 1, &len);
+ if (len > 0) {
+ halfword h = get_reference_token();
+ halfword t = h;
+ char *s;
+ int l;
+ tex_parse_str_to_tok(h, &t, ct, str, len, 2); /* ignore unknown */
+
+ tokenlib_aux_expand_macros_in_tokenlist(h); // todo: use return value instead of def_ref
+ s = tex_tokenlist_to_tstring(lmt_input_state.def_ref, 1, &l, 0, 0, 0);
+ tex_flush_token_list(lmt_input_state.def_ref);
+ tex_flush_token_list(h);
+
+ if (l > 0) {
+ lua_pushlstring(L, (const char *) s, (size_t) l);
+ return 1;
+ }
+ }
+ lua_pushliteral(L, "");
+ return 1;
+}
+
+static int tokenlib_pop_macro(lua_State *L)
+{
+ lua_token_package *p = tokenlib_aux_check_ispackage(L, 1);
+ if (p) {
+ tex_forced_define(p->how, p->cs, p->flag, p->cmd, p->chr);
+ }
+ return 0;
+}
+
+static int tokenlib_save_lua(lua_State *L)
+{
+ halfword f = lmt_tohalfword(L, 1);
+ if (lua_toboolean(L, 2) && cur_level > 0) {
+ /* use with care */
+ halfword ptr = lmt_save_state.save_stack_data.ptr;
+ while (1) {
+ --ptr;
+ switch (save_type(ptr)) {
+ case level_boundary:
+ goto SAVE;
+ case restore_lua:
+ if (save_value(ptr) == f) {
+ return 0;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ SAVE:
+ tex_save_halfword_on_stack(restore_lua, f);
+ return 0;
+}
+
+static int tokenlib_set_lua(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ if (name) {
+ int flags = 0;
+ int funct = lmt_tointeger(L, 2); /*tex todo: check range */
+ lmt_check_for_flags(L, 3, &flags, 1, 1);
+ halfword cs = tex_string_locate(name, lname, 1);
+ if (tex_define_permitted(cs, flags)) {
+ if (is_value(flags)) {
+ tex_define(flags, cs, lua_value_cmd, funct);
+ } else if (is_conditional(flags)) {
+ tex_define(flags, cs, if_test_cmd, last_if_test_code + funct);
+ /* with some effort we could combine these two an dise the flag */
+ } else if (is_protected(flags)) {
+ tex_define(flags, cs, lua_protected_call_cmd, funct);
+ } else {
+ tex_define(flags, cs, lua_call_cmd, funct);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* [catcodes,]name,data[,global,frozen,protected]* */
+
+static int tokenlib_undefine_macro(lua_State *L) /* todo: protected */
+{
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ if (name) {
+ halfword cs = tex_string_locate(name, lname, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 2, &flags, 1, 1);
+ tex_define(flags, cs, undefined_cs_cmd, null);
+ }
+ return 0;
+}
+
+static int tokenlib_set_macro(lua_State *L) /* todo: protected */
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ const char *name = NULL;
+ size_t lname = 0;
+ int slot = 1;
+ halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
+ if (! tex_valid_catcode_table(ct)) {
+ ct = cat_code_table_par;
+ }
+ name = lua_tolstring(L, slot++, &lname);
+ if (name) {
+ size_t lstr = 0;
+ const char *str = lua_tolstring(L, slot++, &lstr);
+ halfword cs = tex_string_locate(name, lname, 1);
+ int flags = 0;
+ if (slot <= top) {
+ slot = lmt_check_for_flags(L, slot, &flags, 1, 1);
+ }
+ if (tex_define_permitted(cs, flags)) { /* we check before we allocate */
+ halfword h = get_reference_token();
+ halfword t = h;
+ if (lstr > 0) {
+ /*tex Options: 1=create (will trigger an error), 2=ignore. */
+ tex_parse_str_to_tok(h, &t, ct, str, lstr, lua_toboolean(L, slot++) ? 2 : 1);
+ }
+ tex_define(flags, cs, tex_flags_to_cmd(flags), h);
+ }
+ }
+ }
+ return 0;
+}
+
+// todo: use: is_call_cmd(cmd)
+
+halfword lmt_macro_to_tok(lua_State *L, int slot, halfword *tail)
+{
+ halfword tok = 0;
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, slot, &lname);
+ int cs = tex_string_locate(name, lname, 0);
+ int cmd = eq_type(cs);
+ if (is_call_cmd(cmd)) {
+ tok = cs_token_flag + cs;
+ } else if (cmd != undefined_cs_cmd) {
+ /*tex Bonus: not really a macro! */
+ tok = token_val(cmd, eq_value(cs));
+ }
+ break;
+ }
+ case LUA_TUSERDATA:
+ tok = token_info(lmt_token_code_from_lua(L, slot));
+ if (! is_call_cmd(tok >= cs_token_flag ? eq_type(tok - cs_token_flag) : token_cmd(tok))) {
+ tok = 0;
+ }
+ break;
+ }
+ if (tok) {
+ int top = lua_gettop(L);
+ halfword m = tex_get_available_token(tok);
+ halfword a = m;
+ halfword c = cat_code_table_par;
+ if (top > slot) {
+ int arg = 0;
+ for (int i = slot + 1; i <= top; i++) {
+ switch (lua_type(L, i)) {
+ case LUA_TBOOLEAN:
+ {
+ arg = lua_toboolean(L, i);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *s = lua_tolstring(L, i, &l);
+ if (arg) {
+ a = tex_store_new_token(a, left_brace_token + '{');
+ }
+ /*tex We use option 1 so we get an undefined error. */
+ tex_parse_str_to_tok(a, &a, c, s, l, 1);
+ if (arg) {
+ a = tex_store_new_token(a, right_brace_token + '}');
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ /* catcode table */
+ c = lmt_tohalfword(L, i);
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ size_t l;
+ const char *s ;
+ int j = (int) lua_rawlen(L, i);
+ for (int k = 1; k <= j; k++) {
+ lua_rawgeti(L, i, k);
+ s = lua_tolstring(L, -1, &l);
+ a = tex_store_new_token(a, left_brace_token + '{');
+ /*tex We use option 1 so we get an udndefined error. */
+ tex_parse_str_to_tok(a, &a, c, s, l, 1);
+ a = tex_store_new_token(a, right_brace_token + '}');
+ lua_pop(L, 1);
+ };
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ a = tex_store_new_token(a, lmt_token_code_from_lua(L, i));
+ break;
+ }
+ }
+ }
+ }
+ if (tail) {
+ *tail = a;
+ }
+ return m;
+ } else {
+ if (tail) {
+ *tail = null;
+ }
+ return null;
+ }
+}
+
+static int tokenlib_expand_macro(lua_State *L)
+{
+ halfword tail = null;
+ halfword tok = lmt_macro_to_tok(L, 1, &tail);
+ if (tok) {
+ /* todo: append to tail */
+ tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
+ tex_begin_inserted_list(tok);
+ // halfword h = tex_get_available_token(token_val(end_local_cmd, 0));
+ // token_link(tail) = h;
+ // tex_begin_inserted_list(tok);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via (run) macro");
+ }
+ tex_local_control(1);
+ } else {
+ tex_local_control_message("invalid (run) macro");
+ }
+ return 0;
+}
+
+/* a weird place, should be in tex */
+
+static int tokenlib_set_char(lua_State *L) /* also in texlib */
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ if (name) {
+ int value = lmt_tointeger(L, 2);
+ if (value >= 0 && value <= max_character_code) {
+ int flags = 0;
+ int cs = tex_string_locate(name, lname, 1);
+ if (top > 2) {
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ }
+ tex_define(flags, cs, char_given_cmd, value);
+ }
+ }
+ }
+ return 0;
+}
+
+/* a weird place, these should be in tex */
+
+static int tokenlib_set_constant(lua_State *L, singleword cmd, halfword min, halfword max)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ if (name) {
+ halfword value = lmt_tohalfword(L, 2);
+ if (value >= min && value <= max) {
+ int flags = 0;
+ int cs = tex_string_locate(name, lname, 1);
+ if (top > 2) {
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ }
+ tex_define(flags, cs, cmd, value);
+ }
+ }
+ }
+ return 0;
+}
+
+static int tokenlib_get_constant(lua_State *L, halfword cmd)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (l > 0) {
+ int cs = tex_string_locate(s, l, 0);
+ if (eq_type(cs) == cmd) {
+ lua_pushinteger(L, eq_value(cs));
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int tokenlib_set_integer(lua_State *L)
+{
+ return tokenlib_set_constant(L, integer_cmd, min_integer, max_integer);
+}
+
+static int tokenlib_set_dimension(lua_State *L)
+{
+ return tokenlib_set_constant(L, dimension_cmd, min_dimen, max_dimen);
+}
+
+// static int tokenlib_set_gluespec(lua_State *L)
+// {
+// return tokenlib_set_constant(L, gluespec_cmd, min_dimen, max_dimen);
+// }
+
+static int tokenlib_get_integer(lua_State *L)
+{
+ return tokenlib_get_constant(L, integer_cmd);
+}
+
+static int tokenlib_get_dimension(lua_State *L)
+{
+ return tokenlib_get_constant(L, dimension_cmd);
+}
+
+// static int tokenlib_get_gluespec(lua_State *L)
+// {
+// return tokenlib_get_constant(L, gluespec_cmd);
+// }
+
+/*
+static int tokenlib_get_command_names(lua_State *L)
+{
+ lua_createtable(L, data_cmd + 1, 0);
+ for (int i = 0; command_names[i].lua; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, command_names[i].lua);
+ lua_rawseti(L, -2, i);
+ }
+ return 1;
+}
+*/
+
+static int tokenlib_serialize(lua_State *L)
+{
+ lua_token *n = tokenlib_aux_maybe_istoken(L, 1);
+ if (n) {
+ halfword t = n->token;
+ char *s;
+ tokenlib_aux_expand_macros_in_tokenlist(t); // todo: use return value instead of def_ref
+ s = tex_tokenlist_to_tstring(lmt_input_state.def_ref, 1, NULL, 0, 0, 0);
+ lua_pushstring(L, s ? s : "");
+ tex_flush_token_list(lmt_input_state.def_ref);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int tokenlib_getcommandvalues(lua_State *L)
+{
+ lua_createtable(L, number_tex_commands, 1);
+ for (int i = 0; i < number_tex_commands; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.command_names[i].lua);
+ lua_rawseti(L, -2, lmt_interface.command_names[i].id);
+ }
+ return 1;
+}
+
+static int tokenlib_getfunctionvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.lua_function_values);
+}
+
+static const struct luaL_Reg tokenlib_function_list[] = {
+ { "type", tokenlib_type },
+ { "create", tokenlib_create },
+ { "new", tokenlib_new },
+ /* */
+ { "istoken", tokenlib_is_token },
+ { "isdefined", tokenlib_is_defined },
+ /* getters */
+ { "scannext", tokenlib_scan_next },
+ { "scannextexpanded", tokenlib_scan_next_expanded },
+ { "scannextchar", tokenlib_scan_next_char },
+ /* skippers */
+ { "skipnext", tokenlib_skip_next },
+ { "skipnextexpanded", tokenlib_skip_next_expanded },
+ /* peekers */
+ { "peeknext", tokenlib_peek_next },
+ { "peeknextexpanded", tokenlib_peek_next_expanded },
+ { "peeknextchar", tokenlib_peek_next_char },
+ /* scanners */
+ { "scancmdchr", tokenlib_scan_cmdchr },
+ { "scancmdchrexpanded", tokenlib_scan_cmdchr_expanded },
+ { "scankeyword", tokenlib_scan_keyword },
+ { "scankeywordcs", tokenlib_scan_keyword_cs },
+ { "scaninteger", tokenlib_scan_integer },
+ { "scanintegerargument", tokenlib_scan_integer_argument },
+ { "scandimenargument", tokenlib_scan_dimen_argument },
+ { "scancardinal", tokenlib_scan_cardinal },
+ { "scanfloat", tokenlib_scan_float },
+ { "scanreal", tokenlib_scan_real },
+ { "scanluanumber", tokenlib_scan_luanumber },
+ { "scanluainteger", tokenlib_scan_luainteger },
+ { "scanluacardinal", tokenlib_scan_luacardinal },
+ { "scanscale", tokenlib_scan_scale },
+ { "scandimen", tokenlib_scan_dimen },
+ { "scanskip", tokenlib_scan_skip },
+ { "scanglue", tokenlib_scan_glue },
+ { "scantoks", tokenlib_scan_toks },
+ { "scantokenlist", tokenlib_scan_tokenlist },
+ { "scancode", tokenlib_scan_code },
+ { "scantokencode", tokenlib_scan_token_code }, /* doesn't expand */
+ { "scanstring", tokenlib_scan_string },
+ { "scanargument", tokenlib_scan_argument },
+ { "scandelimited", tokenlib_scan_delimited },
+ { "scanword", tokenlib_scan_word },
+ { "scanletters", tokenlib_scan_letters },
+ { "scankey", tokenlib_scan_key },
+ { "scanvalue", tokenlib_scan_value },
+ { "scanchar", tokenlib_scan_char },
+ { "scancsname", tokenlib_scan_csname },
+ { "scantoken", tokenlib_scan_token }, /* expands next token if needed */
+ { "scanbox", tokenlib_scan_box },
+ { "isnextchar", tokenlib_is_next_char },
+ /* writers */
+ { "putnext", tokenlib_put_next },
+ { "putback", tokenlib_put_back },
+ { "expand", tokenlib_expand },
+ /* getters */
+ { "getcommand", tokenlib_get_command },
+ { "getindex", tokenlib_get_index },
+ { "getrange", tokenlib_get_range },
+ /* { "get_mode", tokenlib_get_mode }, */ /* obsolete */
+ { "getcmdname", tokenlib_get_cmdname },
+ { "getcsname", tokenlib_get_csname },
+ { "getid", tokenlib_get_id },
+ { "gettok", tokenlib_get_tok }, /* obsolete */
+ { "getactive", tokenlib_get_active },
+ { "getexpandable", tokenlib_get_expandable },
+ { "getprotected", tokenlib_get_protected },
+ { "getfrozen", tokenlib_get_frozen },
+ { "gettolerant", tokenlib_get_tolerant },
+ { "getnoaligned", tokenlib_get_noaligned },
+ { "getprimitive", tokenlib_get_primitive },
+ { "getpermanent", tokenlib_get_permanent },
+ { "getimmutable", tokenlib_get_immutable },
+ { "getinstance", tokenlib_get_instance },
+ { "getflags", tokenlib_get_flags },
+ { "getparameters", tokenlib_get_parameters },
+ { "getmacro", tokenlib_get_macro },
+ { "getmeaning", tokenlib_get_meaning },
+ { "getcmdchrcs", tokenlib_get_cmdchrcs },
+ { "getcstoken", tokenlib_get_cstoken },
+ { "getfields", tokenlib_get_fields },
+ /* setters */
+ { "setmacro", tokenlib_set_macro },
+ { "undefinemacro", tokenlib_undefine_macro },
+ { "expandmacro", tokenlib_expand_macro },
+ { "setchar", tokenlib_set_char },
+ { "setlua", tokenlib_set_lua },
+ { "setinteger", tokenlib_set_integer }, /* can go ... also in texlib */
+ { "getinteger", tokenlib_get_integer }, /* can go ... also in texlib */
+ { "setdimension", tokenlib_set_dimension }, /* can go ... also in texlib */
+ { "getdimension", tokenlib_get_dimension }, /* can go ... also in texlib */
+ /* gobblers */
+ { "gobbleinteger", tokenlib_gobble_integer },
+ { "gobbledimen", tokenlib_gobble_dimen },
+ { "gobble", tokenlib_gobble_until },
+ { "grab", tokenlib_grab_until },
+ /* macros */
+ { "futureexpand", tokenlib_future_expand },
+ { "pushmacro", tokenlib_push_macro },
+ { "popmacro", tokenlib_pop_macro },
+ /* whatever */
+ { "savelua", tokenlib_save_lua },
+ { "serialize", tokenlib_serialize },
+ { "getexpansion", tokenlib_get_expansion },
+ /* interface */
+ { "getfunctionvalues", tokenlib_getfunctionvalues },
+ { "getcommandvalues", tokenlib_getcommandvalues },
+ { "getcommandid", tokenlib_getcommandid },
+ { "getprimitives", tokenlib_getprimitives },
+ /* done */
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg tokenlib_instance_metatable[] = {
+ { "__index", tokenlib_getfield },
+ { "__tostring", tokenlib_tostring },
+ { "__gc", tokenlib_free },
+ { "__eq", tokenlib_equal },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg tokenlib_package_metatable[] = {
+ { "__tostring", tokenlib_package_tostring },
+ { NULL, NULL },
+};
+
+int luaopen_token(lua_State *L)
+{
+ luaL_newmetatable(L, TOKEN_METATABLE_INSTANCE);
+ luaL_setfuncs(L, tokenlib_instance_metatable, 0);
+ luaL_newmetatable(L, TOKEN_METATABLE_PACKAGE);
+ luaL_setfuncs(L, tokenlib_package_metatable, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, tokenlib_function_list, 0);
+ return 1;
+}
+
+typedef struct LoadS { // name
+ char *s;
+ size_t size;
+} LoadS;
+
+static const char *tokenlib_aux_reader(lua_State *L, void *ud, size_t *size)
+{
+ LoadS *ls = (LoadS *) ud;
+ (void) L;
+ if (ls->size > 0) {
+ *size = ls->size;
+ ls->size = 0;
+ return ls->s;
+ } else {
+ return NULL;
+ }
+}
+
+void lmt_token_call(int p) /*tex The \TEX\ pointer to the token list. */
+{
+ LoadS ls;
+ int l = 0;
+ ls.s = tex_tokenlist_to_tstring(p, 1, &l, 0, 0, 0);
+ ls.size = (size_t) l;
+ if (ls.size > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int i;
+ int top = lua_gettop(L);
+ lua_pushcfunction(L, lmt_traceback);
+ i = lua_load(L, tokenlib_aux_reader, &ls, "=[\\directlua]", NULL);
+ if (i != 0) {
+ lmt_error(L, "token call, syntax", -1, i == LUA_ERRSYNTAX ? 0 : 1);
+ } else {
+ ++lmt_lua_state.direct_callback_count;
+ i = lua_pcall(L, 0, 0, top + 1);
+ if (i != 0) {
+ lua_remove(L, top + 1);
+ lmt_error(L, "token call, execute", -1, i == LUA_ERRRUN ? 0 : 1);
+ }
+ }
+ lua_settop(L, top);
+ }
+}
+
+void lmt_function_call(int slot, int prefix) /*tex Functions are collected in an indexed table. */
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ lua_pushcfunction(L, lmt_traceback);
+ if (lua_rawgeti(L, -2, slot) == LUA_TFUNCTION) {
+ int i = 1;
+ /*tex function index */
+ lua_pushinteger(L, slot);
+ if (prefix > 0) {
+ lua_pushinteger(L, prefix);
+ ++i;
+ }
+ ++lmt_lua_state.function_callback_count;
+ i = lua_pcall(L, i, 0, stacktop + 2);
+ if (i) {
+ lua_remove(L, stacktop + 2);
+ lmt_error(L, "registered function call", slot, i == LUA_ERRRUN ? 0 : 1);
+ }
+ }
+ lua_settop(L, stacktop);
+}
+
+void lmt_local_call(int slot)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ int stacktop = lua_gettop(L);
+ lua_pushcfunction(L, lmt_traceback);
+ if (lua_rawgeti(L, LUA_REGISTRYINDEX, slot) == LUA_TFUNCTION) {
+ int i;
+ ++lmt_lua_state.local_callback_count;
+ i = lua_pcall(L, 0, 0, stacktop + 1);
+ if (i) {
+ lua_remove(L, stacktop + 1);
+ lmt_error(L, "local function call", slot, i == LUA_ERRRUN ? 0 : 1);
+ }
+ }
+ lua_settop(L, stacktop);
+}
+
+int lmt_function_call_by_class(int slot, int property, halfword *value)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ int stacktop = lua_gettop(L);
+ int class = lua_value_none_code;
+ lua_pushcfunction(L, lmt_traceback);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ if (lua_rawgeti(L, -1, slot) == LUA_TFUNCTION) {
+ int i;
+ /*tex function index */
+ lua_pushinteger(L, slot);
+ if (property) {
+ lua_pushinteger(L, property);
+ } else {
+ lua_push_key(value);
+ }
+ ++lmt_lua_state.value_callback_count;
+ i = lua_pcall(L, 2, 2, stacktop + 1);
+ if (i) {
+ lua_remove(L, stacktop + 1);
+ lmt_error(L, "function call", slot, i == LUA_ERRRUN ? 0 : 1);
+ } else {
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ class = lmt_tointeger(L, -2);
+ }
+ switch (class) {
+ case lua_value_none_code:
+ {
+ break;
+ }
+ case lua_value_integer_code:
+ {
+ *value = lua_type(L, -1) == LUA_TNUMBER ? lmt_tohalfword(L, -1) : 0;
+ if (*value < - max_integer) {
+ *value = max_integer;
+ } else if (*value > max_integer) {
+ *value = max_integer;
+ }
+ break;
+ }
+ case lua_value_cardinal_code:
+ {
+ lua_Unsigned u = lua_type(L, -1) == LUA_TNUMBER ? (lua_Unsigned) lua_tointeger(L, -1) : 0;
+ if (u > max_cardinal) {
+ u = max_cardinal;
+ }
+ if (*value > max_integer) {
+ *value = (halfword) (u - 0x100000000);
+ } else {
+ *value = (halfword) u;
+ }
+ break;
+ }
+ case lua_value_dimension_code:
+ {
+ *value = lua_type(L, -1) == LUA_TNUMBER ? lmt_tohalfword(L, -1) : 0;
+ if (*value < - max_dimen) {
+ *value = max_dimen;
+ } else if (*value > max_dimen) {
+ *value = max_dimen;
+ }
+ break;
+ }
+ case lua_value_skip_code:
+ {
+ halfword n = lmt_check_isnode(L, -1);
+ if (n && node_type(n) == glue_spec_node) {
+ *value = n;
+ } else {
+ luaL_error(L, "gluespec node expected");
+ *value = tex_copy_node(zero_glue);
+ }
+ break;
+ }
+ case lua_value_float_code:
+ case lua_value_string_code:
+ {
+ class = lua_value_none_code;
+ break;
+ }
+ case lua_value_boolean_code:
+ {
+ *value = lua_toboolean(L, -1);
+ break;
+ }
+ case lua_value_node_code:
+ {
+ *value = lmt_check_isnode(L, -1);
+ break;
+ }
+ case lua_value_direct_code:
+ *value = lmt_check_isdirect(L, -1);
+ break;
+ default:
+ {
+ class = lua_value_none_code;
+ break;
+ }
+ }
+ }
+ }
+ lua_settop(L, stacktop);
+ return class;
+}
+
+/* some day maybe an alternative too
+
+void lmt_function_call(int slot)
+{
+ lua_State *L = lua_state.lua_instance;
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lua_state.function_table_id);
+ if (lua_rawgeti(L, -1, slot) == LUA_TFUNCTION) {
+ lua_pushinteger(L, slot);
+ ++lua_state.function_callback_count;
+ lua_call(L, 1, 0);
+ }
+ lua_settop(L,stacktop);
+}
+
+*/
+
+int lmt_push_specification(lua_State *L, halfword ptr, int onlycount)
+{
+ if (ptr) {
+ switch (node_subtype(ptr)) {
+ case par_shape_code:
+ {
+ int n = specification_count(ptr);
+ if (onlycount == 1) {
+ lua_pushinteger(L, n);
+ } else {
+ int r = specification_repeat(ptr);
+ lua_createtable(L, n, r ? 1 : 0);
+ if (r) {
+ lua_push_boolean_at_key(L, repeat, r);
+ }
+ for (int m = 1; m <= n; m++) {
+ lua_createtable(L, 2, 0);
+ lua_pushinteger(L, tex_get_specification_indent(ptr, m));
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, tex_get_specification_width(ptr, m));
+ lua_rawseti(L, -2, 2);
+ lua_rawseti(L, -2, m);
+ }
+ }
+ return 1;
+ }
+ case inter_line_penalties_code:
+ case club_penalties_code:
+ case widow_penalties_code:
+ case display_widow_penalties_code:
+ case orphan_penalties_code:
+ case math_forward_penalties_code:
+ case math_backward_penalties_code:
+ {
+ int n = specification_count(ptr);
+ if (onlycount == 1) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_createtable(L, n, 0);
+ for (int m = 1; m <= n; m++) {
+ lua_pushinteger(L, tex_get_specification_penalty(ptr, m));
+ lua_rawseti(L, -2, m);
+ }
+ }
+ return 1;
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}