summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua')
-rw-r--r--source/luametatex/source/lua/lmtcallbacklib.c615
-rw-r--r--source/luametatex/source/lua/lmtcallbacklib.h105
-rw-r--r--source/luametatex/source/lua/lmtenginelib.c1146
-rw-r--r--source/luametatex/source/lua/lmtenginelib.h41
-rw-r--r--source/luametatex/source/lua/lmtfontlib.c1020
-rw-r--r--source/luametatex/source/lua/lmtfontlib.h10
-rw-r--r--source/luametatex/source/lua/lmtinterface.c544
-rw-r--r--source/luametatex/source/lua/lmtinterface.h1754
-rw-r--r--source/luametatex/source/lua/lmtlanguagelib.c439
-rw-r--r--source/luametatex/source/lua/lmtlanguagelib.h20
-rw-r--r--source/luametatex/source/lua/lmtlibrary.c106
-rw-r--r--source/luametatex/source/lua/lmtlibrary.h60
-rw-r--r--source/luametatex/source/lua/lmtluaclib.c660
-rw-r--r--source/luametatex/source/lua/lmtluaclib.h10
-rw-r--r--source/luametatex/source/lua/lmtlualib.c627
-rw-r--r--source/luametatex/source/lua/lmtlualib.h25
-rw-r--r--source/luametatex/source/lua/lmtmplib.c3137
-rw-r--r--source/luametatex/source/lua/lmtnodelib.c10324
-rw-r--r--source/luametatex/source/lua/lmtnodelib.h114
-rw-r--r--source/luametatex/source/lua/lmtstatuslib.c526
-rw-r--r--source/luametatex/source/lua/lmttexiolib.c307
-rw-r--r--source/luametatex/source/lua/lmttexiolib.h13
-rw-r--r--source/luametatex/source/lua/lmttexlib.c5580
-rw-r--r--source/luametatex/source/lua/lmttexlib.h29
-rw-r--r--source/luametatex/source/lua/lmttokenlib.c3894
-rw-r--r--source/luametatex/source/lua/lmttokenlib.h52
26 files changed, 31158 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtcallbacklib.c b/source/luametatex/source/lua/lmtcallbacklib.c
new file mode 100644
index 000000000..8724cdd6f
--- /dev/null
+++ b/source/luametatex/source/lua/lmtcallbacklib.c
@@ -0,0 +1,615 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ These are the supported callbacks (by name). This list must have the same size and order as the
+ array in |luatexcallbackids.h|! We could have kept the names private here and maybe they will
+ become that again. On the other hand we can now use them in reports.
+
+*/
+
+callback_state_info lmt_callback_state = {
+ .metatable_id = 0,
+ .padding = 0,
+ .values = { 0 },
+};
+
+/* todo: use lua keywords instead */
+
+static const char *callbacklib_names[total_callbacks] = {
+ "", /*tex empty on purpose */
+ "find_log_file",
+ "find_format_file",
+ "open_data_file",
+ "process_jobname",
+ "start_run",
+ "stop_run",
+ "define_font",
+ "pre_output_filter",
+ "buildpage_filter",
+ "hpack_filter",
+ "vpack_filter",
+ "hyphenate",
+ "ligaturing",
+ "kerning",
+ "glyph_run",
+ "pre_linebreak_filter",
+ "linebreak_filter",
+ "post_linebreak_filter",
+ "append_to_vlist_filter",
+ "alignment_filter",
+ "local_box_filter",
+ "packed_vbox_filter",
+ "mlist_to_hlist",
+ "pre_dump",
+ "start_file",
+ "stop_file",
+ "intercept_tex_error",
+ "intercept_lua_error",
+ "show_error_message",
+ "show_warning_message",
+ "hpack_quality",
+ "vpack_quality",
+ "insert_par",
+ "append_line_filter",
+ "build_page_insert",
+ /* "fire_up_output", */
+ "wrapup_run",
+ "begin_paragraph",
+ "paragraph_context",
+ /* "get_math_char", */
+ "math_rule",
+ "make_extensible",
+ "register_extensible",
+ "show_whatsit",
+ "get_attribute",
+ "get_noad_class",
+ "get_math_dictionary",
+ "show_lua_call",
+ "trace_memory",
+ "handle_overload",
+ "missing_character",
+ "process_character",
+};
+
+/*tex
+
+ This is the generic callback handler, inspired by the one described in the \LUA\ manual(s). It
+ got adapted over time and can also handle some userdata arguments.
+
+*/
+
+static int callbacklib_aux_run(lua_State *L, int id, int special, const char *values, va_list vl, int top, int base)
+{
+ int narg = 0;
+ int nres = 0;
+ if (special == 2) {
+ /*tex copy the enclosing table */
+ lua_pushvalue(L, -2);
+ }
+ for (narg = 0; *values; narg++) {
+ switch (*values++) {
+ case callback_boolean_key:
+ /*tex A boolean: */
+ lua_pushboolean(L, va_arg(vl, int));
+ break;
+ case callback_charnum_key:
+ /*tex A (8 bit) character: */
+ {
+ char cs = (char) va_arg(vl, int);
+ lua_pushlstring(L, &cs, 1);
+ }
+ break;
+ case callback_integer_key:
+ /*tex An integer: */
+ lua_pushinteger(L, va_arg(vl, int));
+ break;
+ case callback_line_key:
+ /*tex A buffer section, with implied start: */
+ lua_pushlstring(L, (char *) (lmt_fileio_state.io_buffer + lmt_fileio_state.io_first), (size_t) va_arg(vl, int));
+ break;
+ case callback_strnumber_key:
+ /*tex A \TEX\ string (indicated by an index): */
+ {
+ size_t len;
+ const char *s = tex_makeclstring(va_arg(vl, int), &len);
+ lua_pushlstring(L, s, len);
+ }
+ break;
+ case callback_lstring_key:
+ /*tex A \LUA\ string: */
+ {
+ lstring *lstr = va_arg(vl, lstring *);
+ lua_pushlstring(L, (const char *) lstr->s, lstr->l);
+ }
+ break;
+ case callback_node_key:
+ /*tex A \TEX\ node: */
+ lmt_push_node_fast(L, va_arg(vl, int));
+ break;
+ case callback_string_key:
+ /*tex A \CCODE\ string: */
+ lua_pushstring(L, va_arg(vl, char *));
+ break;
+ case '-':
+ narg--;
+ break;
+ case '>':
+ goto ENDARGS;
+ default:
+ ;
+ }
+ }
+ ENDARGS:
+ nres = (int) strlen(values);
+ if (special == 1) {
+ nres++;
+ } else if (special == 2) {
+ narg++;
+ }
+ {
+ lmt_lua_state.saved_callback_count++;
+ int i = lua_pcall(L, narg, nres, base);
+ if (i) {
+ /*tex
+ We can't be more precise here as it could be called before \TEX\ initialization is
+ complete.
+ */
+ lua_remove(L, top + 2);
+ lmt_error(L, "run callback", id, (i == LUA_ERRRUN ? 0 : 1));
+ lua_settop(L, top);
+ return 0;
+ }
+ }
+ if (nres == 0) {
+ return 1;
+ }
+ nres = -nres;
+ while (*values) {
+ int t = lua_type(L, nres);
+ switch (*values++) {
+ case callback_boolean_key:
+ switch (t) {
+ case LUA_TBOOLEAN:
+ *va_arg(vl, int *) = lua_toboolean(L, nres);
+ break;
+ case LUA_TNIL:
+ *va_arg(vl, int *) = 0;
+ break;
+ default:
+ return tex_formatted_error("callback", "boolean or nil expected, false or nil, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ /*
+ case callback_charnum_key:
+ break;
+ */
+ case callback_integer_key:
+ switch (t) {
+ case LUA_TNUMBER:
+ *va_arg(vl, int *) = lmt_tointeger(L, nres);
+ break;
+ default:
+ return tex_formatted_error("callback", "number expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ case callback_line_key:
+ switch (t) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *s = lua_tolstring(L, nres, &len);
+ if (s && (len > 0)) {
+ int *bufloc = va_arg(vl, int *);
+ int ret = *bufloc;
+ if (tex_room_in_buffer(ret + (int) len)) {
+ strncpy((char *) (lmt_fileio_state.io_buffer + ret), s, len);
+ *bufloc += (int) len;
+ /* while (len--) { fileio_state.io_buffer[(*bufloc)++] = *s++; } */
+ while ((*bufloc) - 1 > ret && lmt_fileio_state.io_buffer[(*bufloc) - 1] == ' ') {
+ (*bufloc)--;
+ }
+ } else {
+ return 0;
+ }
+ }
+ /*tex We can assume no more arguments! */
+ }
+ break;
+ case LUA_TNIL:
+ /*tex We assume no more arguments! */
+ return 0;
+ default:
+ return tex_formatted_error("callback", "string or nil expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ case callback_strnumber_key:
+ switch (t) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *s = lua_tolstring(L, nres, &len);
+ if (s) {
+ *va_arg(vl, int *) = tex_maketexlstring(s, len);
+ } else {
+ /*tex |len| can be zero */
+ *va_arg(vl, int *) = 0;
+ }
+ }
+ break;
+ default:
+ return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ case callback_lstring_key:
+ switch (t) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *s = lua_tolstring(L, nres, &len);
+ if (s && len > 0) {
+ lstring *lsret = lmt_memory_malloc(sizeof(lstring));
+ if (lsret) {
+ lsret->s = lmt_memory_malloc((unsigned) (len + 1));
+ if (lsret->s) {
+ (void) memcpy(lsret->s, s, (len + 1));
+ lsret->l = len;
+ *va_arg(vl, lstring **) = lsret;
+ } else {
+ *va_arg(vl, int *) = 0;
+ }
+ } else {
+ *va_arg(vl, int *) = 0;
+ }
+ } else {
+ /*tex |len| can be zero */
+ *va_arg(vl, int *) = 0;
+ }
+ }
+ break;
+ default:
+ return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ case callback_node_key:
+ switch (t) {
+ case LUA_TUSERDATA:
+ *va_arg(vl, int *) = lmt_check_isnode(L, nres);
+ break;
+ default:
+ *va_arg(vl, int *) = null;
+ break;
+ }
+ break;
+ case callback_string_key:
+ switch (t) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *s = lua_tolstring(L, nres, &len);
+ if (s) {
+ char *ss = lmt_memory_malloc((unsigned) (len + 1));
+ if (ss) {
+ memcpy(ss, s, (len + 1));
+ }
+ *va_arg(vl, char **) = ss;
+ } else {
+ *va_arg(vl, char **) = NULL;
+ // *va_arg(vl, int *) = 0;
+ }
+ }
+ break;
+ default:
+ return tex_formatted_error("callback", "string expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ case callback_result_key:
+ switch (t) {
+ case LUA_TNIL:
+ *va_arg(vl, int *) = 0;
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, nres) == 0) {
+ *va_arg(vl, int *) = 0;
+ break;
+ } else {
+ return tex_formatted_error("callback", "string, false or nil expected, not: %s\n", lua_typename(L, t));
+ }
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *s = lua_tolstring(L, nres, &len);
+ if (s) {
+ char *ss = lmt_memory_malloc((unsigned) (len + 1));
+ if (ss) {
+ memcpy(ss, s, (len + 1));
+ *va_arg(vl, char **) = ss;
+ } else {
+ *va_arg(vl, char **) = NULL;
+ // *va_arg(vl, int *) = 0;
+ }
+ } else {
+ *va_arg(vl, char **) = NULL;
+ // *va_arg(vl, int *) = 0;
+ }
+ }
+ break;
+ default:
+ return tex_formatted_error("callback", "string, false or nil expected, not: %s\n", lua_typename(L, t));
+ }
+ break;
+ default:
+ return tex_formatted_error("callback", "invalid value type returned\n");
+ }
+ nres++;
+ }
+ return 1;
+}
+
+/*tex
+ Especially the \IO\ related callbacks are registered once, for instance when a file is opened,
+ and (re)used later. These are dealt with here.
+*/
+
+int lmt_run_saved_callback_close(lua_State *L, int r)
+{
+ int ret = 0;
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, r);
+ lua_push_key(close);
+ if (lua_rawget(L, -2) == LUA_TFUNCTION) {
+ ret = lua_pcall(L, 0, 0, 0);
+ if (ret) {
+ return tex_formatted_error("lua", "error in close file callback") - 1;
+ }
+ }
+ lua_settop(L, stacktop);
+ return ret;
+}
+
+int lmt_run_saved_callback_line(lua_State *L, int r, int firstpos)
+{
+ int ret = -1; /* -1 is error, >= 0 is buffer length */
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, r);
+ lua_push_key(reader);
+ if (lua_rawget(L, -2) == LUA_TFUNCTION) {
+ lua_pushvalue(L, -2);
+ lmt_lua_state.file_callback_count++;
+ ret = lua_pcall(L, 1, 1, 0);
+ if (ret) {
+ ret = tex_formatted_error("lua", "error in read line callback") - 1;
+ } else if (lua_type(L, -1) == LUA_TSTRING) {
+ size_t len;
+ const char *s = lua_tolstring(L, -1, &len);
+ if (s && len > 0) {
+ while (len >= 1 && s[len-1] == ' ') {
+ len--;
+ }
+ if (len > 0) {
+ if (tex_room_in_buffer(firstpos + (int) len)) {
+ strncpy((char *) (lmt_fileio_state.io_buffer + firstpos), s, len);
+ ret = firstpos + (int) len;
+ } else {
+ tex_overflow_error("buffer", (int) len);
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = -1;
+ }
+ }
+ lua_settop(L, stacktop);
+ return ret;
+}
+
+/*tex
+
+ Many callbacks have a specific handler, so they don't use the previously mentioned generic one.
+ The next bunch of helpers checks for them being set and deals invoking them as well as reporting
+ errors.
+
+*/
+
+int lmt_callback_okay(lua_State *L, int i, int *top)
+{
+ *top = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
+ lua_pushcfunction(L, lmt_traceback); /* goes before function */
+ if (lua_rawgeti(L, -2, i) == LUA_TFUNCTION) {
+ lmt_lua_state.saved_callback_count++;
+ return 1;
+ } else {
+ lua_pop(L, 3);
+ return 0;
+ }
+}
+
+void lmt_callback_error(lua_State *L, int top, int i)
+{
+ lua_remove(L, top + 2);
+ lmt_error(L, "callback error", -1, (i == LUA_ERRRUN ? 0 : 1));
+ lua_settop(L, top);
+}
+
+int lmt_run_and_save_callback(lua_State *L, int i, const char *values, ...)
+{
+ int top = 0;
+ int ret = 0;
+ if (lmt_callback_okay(L, i, &top)) {
+ va_list args;
+ va_start(args, values);
+ ret = callbacklib_aux_run(L, i, 1, values, args, top, top + 2);
+ va_end(args);
+ if (ret > 0) {
+ ret = lua_type(L, -1) == LUA_TTABLE ? luaL_ref(L, LUA_REGISTRYINDEX) : 0;
+ }
+ lua_settop(L, top);
+ }
+ return ret;
+}
+
+int lmt_run_callback(lua_State *L, int i, const char *values, ...)
+{
+ int top = 0;
+ int ret = 0;
+ if (lmt_callback_okay(L, i, &top)) {
+ va_list args;
+ va_start(args, values);
+ ret = callbacklib_aux_run(L, i, 0, values, args, top, top + 2);
+ va_end(args);
+ lua_settop(L, top);
+ }
+ return ret;
+}
+
+void lmt_destroy_saved_callback(lua_State *L, int i)
+{
+ luaL_unref(L, LUA_REGISTRYINDEX, i);
+}
+
+static int callbacklib_callback_found(const char *s)
+{
+ if (s) {
+ for (int cb = 0; cb < total_callbacks; cb++) {
+ if (strcmp(callbacklib_names[cb], s) == 0) {
+ return cb;
+ }
+ }
+ }
+ return -1;
+}
+
+static int callbacklib_callback_register(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ int cb = callbacklib_callback_found(s);
+ if (cb >= 0) {
+ switch (lua_type(L, 2)) {
+ case LUA_TFUNCTION:
+ lmt_callback_state.values[cb] = cb;
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ goto BAD; /*tex Only |false| is valid. */
+ }
+ // fall through
+ case LUA_TNIL:
+ lmt_callback_state.values[cb] = -1;
+ break;
+ }
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
+ lua_pushvalue(L, 2); /*tex the function or nil */
+ lua_rawseti(L, -2, cb);
+ lua_rawseti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
+ lua_pushinteger(L, cb);
+ return 1;
+ }
+ BAD:
+ lua_pushnil(L);
+ return 1;
+}
+
+void lmt_run_memory_callback(const char* what, int success)
+{
+ lmt_run_callback(lmt_lua_state.lua_instance, trace_memory_callback, "Sb->", what, success);
+ fflush(stdout);
+}
+
+/*tex
+
+ The \LUA\ library that deals with callbacks has some diagnostic helpers that makes it possible
+ to implement a higher level interface.
+
+*/
+
+static int callbacklib_callback_find(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ if (s) {
+ int cb = callbacklib_callback_found(s);
+ if (cb >= 0) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_callback_state.metatable_id);
+ lua_rawgeti(L, -1, cb);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int callbacklib_callback_known(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ lua_pushboolean(L, s && (callbacklib_callback_found(s) >= 0));
+ return 1;
+}
+
+static int callbacklib_callback_list(lua_State *L)
+{
+ lua_createtable(L, 0, total_callbacks);
+ for (int cb = 1; cb < total_callbacks; cb++) {
+ lua_pushstring(L, callbacklib_names[cb]);
+ lua_pushboolean(L, lmt_callback_defined(cb));
+ lua_rawset(L, -3);
+ }
+ return 1;
+}
+
+/* todo: language function calls */
+
+void lmt_push_callback_usage(lua_State *L)
+{
+ lua_createtable(L, 0, 9);
+ lua_push_integer_at_key(L, saved, lmt_lua_state.saved_callback_count);
+ lua_push_integer_at_key(L, file, lmt_lua_state.file_callback_count);
+ lua_push_integer_at_key(L, direct, lmt_lua_state.direct_callback_count);
+ lua_push_integer_at_key(L, function, lmt_lua_state.function_callback_count);
+ lua_push_integer_at_key(L, value, lmt_lua_state.value_callback_count);
+ lua_push_integer_at_key(L, local, lmt_lua_state.local_callback_count);
+ lua_push_integer_at_key(L, bytecode, lmt_lua_state.bytecode_callback_count);
+ lua_push_integer_at_key(L, message, lmt_lua_state.message_callback_count);
+ lua_push_integer_at_key(L, count,
+ lmt_lua_state.saved_callback_count
+ + lmt_lua_state.file_callback_count
+ + lmt_lua_state.direct_callback_count
+ + lmt_lua_state.function_callback_count
+ + lmt_lua_state.value_callback_count
+ + lmt_lua_state.local_callback_count
+ + lmt_lua_state.bytecode_callback_count
+ + lmt_lua_state.message_callback_count
+ );
+}
+
+static int callbacklib_callback_usage(lua_State *L)
+{
+ lmt_push_callback_usage(L);
+ return 1;
+}
+
+static const struct luaL_Reg callbacklib_function_list[] = {
+ { "find", callbacklib_callback_find },
+ { "known", callbacklib_callback_known },
+ { "register", callbacklib_callback_register },
+ { "list", callbacklib_callback_list },
+ { "usage", callbacklib_callback_usage },
+ { NULL, NULL },
+};
+
+int luaopen_callback(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, callbacklib_function_list, 0);
+ lua_newtable(L);
+ lmt_callback_state.metatable_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtcallbacklib.h b/source/luametatex/source/lua/lmtcallbacklib.h
new file mode 100644
index 000000000..6faa4ddac
--- /dev/null
+++ b/source/luametatex/source/lua/lmtcallbacklib.h
@@ -0,0 +1,105 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LCALLBACKLIB_H
+# define LMT_LCALLBACKLIB_H
+
+# include "lapi.h"
+
+typedef enum callback_callback_types {
+ find_log_file_callback = 1,
+ find_format_file_callback,
+ open_data_file_callback,
+ process_jobname_callback,
+ start_run_callback,
+ stop_run_callback,
+ define_font_callback,
+ pre_output_filter_callback,
+ buildpage_filter_callback,
+ hpack_filter_callback,
+ vpack_filter_callback,
+ hyphenate_callback,
+ ligaturing_callback,
+ kerning_callback,
+ glyph_run_callback,
+ pre_linebreak_filter_callback,
+ linebreak_filter_callback,
+ post_linebreak_filter_callback,
+ append_to_vlist_filter_callback,
+ alignment_filter_callback,
+ local_box_filter_callback,
+ packed_vbox_filter_callback,
+ mlist_to_hlist_callback,
+ pre_dump_callback,
+ start_file_callback,
+ stop_file_callback,
+ intercept_tex_error_callback,
+ intercept_lua_error_callback,
+ show_error_message_callback,
+ show_warning_message_callback,
+ hpack_quality_callback,
+ vpack_quality_callback,
+ insert_par_callback,
+ append_line_filter_callback,
+ build_page_insert_callback,
+ /* fire_up_output_callback, */
+ wrapup_run_callback,
+ begin_paragraph_callback,
+ paragraph_context_callback,
+ /* get_math_char_callback, */
+ math_rule_callback,
+ make_extensible_callback,
+ register_extensible_callback,
+ show_whatsit_callback,
+ get_attribute_callback,
+ get_noad_class_callback,
+ get_math_dictionary_callback,
+ show_lua_call_callback,
+ trace_memory_callback,
+ handle_overload_callback,
+ missing_character_callback,
+ process_character_callback,
+ total_callbacks,
+} callback_callback_types;
+
+typedef struct callback_state_info {
+ int metatable_id;
+ int padding;
+ int values[total_callbacks];
+} callback_state_info;
+
+extern callback_state_info lmt_callback_state;
+
+typedef enum callback_keys {
+ callback_boolean_key = 'b', /*tex a boolean (int) */
+ callback_charnum_key = 'c', /*tex a byte (char) */
+ callback_integer_key = 'd', /*tex an integer */
+ callback_line_key = 'l', /*tex a buffer section, with implied start */
+ callback_strnumber_key = 's', /*tex a \TEX\ string (index) */
+ callback_lstring_key = 'L', /*tex a \LUA\ string (struct) */
+ callback_node_key = 'N', /*tex a \TEX\ node (halfword) */
+ callback_string_key = 'S', /*tex a \CCODE\ string */
+ callback_result_key = 'R', /*tex a string (return value) but nil is also okay */
+} callback_keys;
+
+inline static int lmt_callback_defined (int a) { return lmt_callback_state.values[a]; }
+inline static int lmt_callback_call (lua_State *L, int i, int o, int top) { return lua_pcallk(L, i, o, top + 2, 0, NULL); }
+
+extern int lmt_callback_okay (lua_State *L, int i, int *top);
+extern void lmt_callback_error (lua_State *L, int top, int i);
+inline void lmt_callback_wrapup (lua_State *L, int top) { lua_settop(L, top); }
+
+extern int lmt_run_callback (lua_State *L, int i, const char *values, ...);
+extern int lmt_run_and_save_callback (lua_State *L, int i, const char *values, ...);
+extern int lmt_run_saved_callback_line (lua_State *L, int i, int firstpos);
+extern int lmt_run_saved_callback_close (lua_State *L, int i);
+
+extern void lmt_destroy_saved_callback (lua_State *L, int i);
+
+extern void lmt_run_memory_callback (const char *what, int success);
+
+extern void lmt_push_callback_usage (lua_State *L);
+
+# endif
+
diff --git a/source/luametatex/source/lua/lmtenginelib.c b/source/luametatex/source/lua/lmtenginelib.c
new file mode 100644
index 000000000..f8df06657
--- /dev/null
+++ b/source/luametatex/source/lua/lmtenginelib.c
@@ -0,0 +1,1146 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+engine_state_info lmt_engine_state = {
+ .lua_init = 0,
+ .lua_only = 0,
+ .luatex_banner = NULL,
+ .engine_name = NULL,
+ .startup_filename = NULL,
+ .startup_jobname = NULL,
+ .dump_name = NULL,
+ .utc_time = 0,
+ .permit_loadlib = 0,
+};
+
+/*tex
+ We assume that the strings are proper \UTF\ and in \MSWINDOWS\ we handle wide characters to get
+ that right.
+*/
+
+typedef struct environment_state_info {
+ char **argv;
+ int argc;
+ int npos;
+ char *flag;
+ char *value;
+ char *name;
+ char *ownpath;
+ char *ownbase;
+ char *ownname;
+ char *owncore;
+ char *input_name;
+ int luatex_lua_offset;
+} environment_state_info;
+
+static environment_state_info lmt_environment_state = {
+ .argv = NULL,
+ .argc = 0,
+ .npos = 0,
+ .flag = NULL,
+ .value = NULL,
+ .name = NULL,
+ .ownpath = NULL,
+ .ownbase = NULL,
+ .ownname = NULL,
+ .owncore = NULL,
+ .input_name = NULL,
+ .luatex_lua_offset = 0,
+};
+
+/*tex todo: make helpers in loslibext which has similar code */
+
+static void enginelib_splitnames(void)
+{
+ char *p = lmt_memory_strdup(lmt_environment_state.ownpath); /*tex We need to make copies! */
+ /*
+ printf("ownpath = %s\n",environment_state.ownpath);
+ printf("ownbase = %s\n",environment_state.ownbase);
+ printf("ownname = %s\n",environment_state.ownname);
+ printf("owncore = %s\n",environment_state.owncore);
+ */
+ /*
+ We loose some here but not enough to worry about. Maybe eventually we will use our own
+ |basename| and |dirname| anyway.
+ */
+ lmt_environment_state.ownbase = aux_basename(lmt_memory_strdup(p));
+ lmt_environment_state.ownname = aux_basename(lmt_memory_strdup(p));
+ lmt_environment_state.ownpath = aux_dirname(lmt_memory_strdup(p)); /* We could use p and not free later, but this is cleaner. */
+ /* */
+ for (size_t i = 0; i < strlen(lmt_environment_state.ownname); i++) {
+ if (lmt_environment_state.ownname[i] == '.') {
+ lmt_environment_state.ownname[i] = '\0';
+ break ;
+ }
+ }
+ lmt_environment_state.owncore = lmt_memory_strdup(lmt_environment_state.ownname);
+ /*
+ printf("ownpath = %s\n",environment_state.ownpath);
+ printf("ownbase = %s\n",environment_state.ownbase);
+ printf("ownname = %s\n",environment_state.ownname);
+ printf("owncore = %s\n",environment_state.owncore);
+ */
+ lmt_memory_free(p);
+}
+
+/*tex A bunch of internalized strings: see |linterface.h |.*/
+
+/* declare_shared_lua_keys; */
+/* declare_metapost_lua_keys; */
+
+char *tex_engine_input_filename(void)
+{
+ /*tex When npos equals zero we have no filename i.e. nothing that doesn't start with |--|. */
+ return lmt_environment_state.npos > 0 && lmt_environment_state.npos < lmt_environment_state.argc ? lmt_environment_state.argv[lmt_environment_state.npos] : NULL;
+}
+
+/*tex
+
+ Filenames can have spaces in which case (double) quotes are used to indicate the bounds of the
+ string. At the \TEX\ level curly braces are also an option but these are dealt with in the
+ scanner.
+
+ Comment: maybe we should also support single quotes, so that we're consistent with \LUA\ quoting.
+
+*/
+
+static char *enginelib_normalize_quotes(const char* name, const char* mesg)
+{
+ char *ret = lmt_memory_malloc(strlen(name) + 3);
+ if (ret) {
+ int must_quote = strchr(name, ' ') != NULL;
+ /* Leave room for quotes and NUL. */
+ int quoted = 0;
+ char *p = ret;
+ if (must_quote) {
+ *p++ = '"';
+ }
+ for (const char *q = name; *q; q++) {
+ if (*q == '"') {
+ quoted = ! quoted;
+ } else {
+ *p++ = *q;
+ }
+ }
+ if (must_quote) {
+ *p++ = '"';
+ }
+ *p = '\0';
+ if (quoted) {
+ tex_emergency_message("system", "unbalanced quotes in %s %s\n", mesg, name);
+ tex_emergency_exit();
+ }
+ }
+ return ret;
+}
+
+/*
+
+ We support a minimum set of options but more can be supported by supplying an (startup)
+ initialization script and/or by setting values in the |texconfig| table. At some point we might
+ provide some default initiazation script but that's for later. In fact, a bug in \LUATEX\ <
+ 1.10 made some of the command line options get lost anyway due to setting their values before
+ checking the config table (probably introduced at some time). As no one noticed that anyway,
+ removing these from the commandline is okay.
+
+ Part of the commandline handler is providing (minimal) help information and reporting credits
+ (more credits can be found in the source file). Here comes the basic help.
+
+ At some point I will likely add a |--permitloadlib| flag and block loading of libraries when
+ that flag is not given so that we satisfy operating systems and/or distributions that have some
+ restrictions on loading libraries. It also means that the optional modules will be (un)locked,
+ but we can control that in the runners so it's no big deal because we will never depend on
+ external code for the \CONTEXT\ core features.
+
+*/
+
+static void enginelib_show_help(void)
+{
+ puts(
+ "Usage: " luametatex_name_lowercase " --lua=FILE [OPTION]... [TEXNAME[.tex]] [COMMANDS]\n"
+ " or: " luametatex_name_lowercase " --lua=FILE [OPTION]... \\FIRST-LINE\n"
+ " or: " luametatex_name_lowercase " --lua=FILE [OPTION]... &FMT ARGS\n"
+ "\n"
+ "Run " luametatex_name_camelcase " on TEXNAME, usually creating TEXNAME.pdf. Any remaining COMMANDS"
+ "are processed as luatex input, after TEXNAME is read.\n"
+ "\n"
+ "Alternatively, if the first non-option argument begins with a backslash,\n"
+ luametatex_name_camelcase " interprets all non-option arguments as an input line.\n"
+ "\n"
+ "Alternatively, if the first non-option argument begins with a &, the next word\n"
+ "is taken as the FMT to read, overriding all else. Any remaining arguments are\n"
+ "processed as above.\n"
+ "\n"
+ "If no arguments or options are specified, prompt for input.\n"
+ "\n"
+ "The following regular options are understood:\n"
+ "\n"
+ " --credits display credits and exit\n"
+ " --fmt=FORMAT load the format file FORMAT\n"
+ " --help display help and exit\n"
+ " --ini be ini" luametatex_name_lowercase ", for dumping formats\n"
+ " --jobname=STRING set the job name to STRING\n"
+ " --lua=FILE load and execute a lua initialization script\n"
+ " --version display version and exit\n"
+ "\n"
+ "Alternate behaviour models can be obtained by special switches\n"
+ "\n"
+ " --luaonly run a lua file, then exit\n"
+ "\n"
+ "Loading libraries from Lua is blocked unless one explicitly permits it:\n"
+ "\n"
+ " --permitloadlib permit loading of external libraries (coming)\n"
+ "\n"
+ "See the reference manual for more information about the startup process.\n"
+ "\n"
+ "Email bug reports to " luametatex_bug_address ".\n"
+ );
+ exit(EXIT_SUCCESS);
+}
+
+/*tex
+
+ This is the minimal version info display. The credits option provides a bit more information.
+*/
+
+static void enginelib_show_version_info(void)
+{
+ tex_print_version_banner();
+ puts(
+ "\n"
+ "\n"
+ "Execute '" luametatex_name_lowercase " --credits' for credits and version details.\n"
+ "\n"
+ "There is NO warranty. Redistribution of this software is covered by the terms\n"
+ "of the GNU General Public License, version 2 or (at your option) any later\n"
+ "version. For more information about these matters, see the file named COPYING\n"
+ "and the LuaMetaTeX source.\n"
+ "\n"
+ "Functionality : level " LMT_TOSTRING(luametatex_development_id) "\n"
+ "Support : " luametatex_support_address "\n"
+ "Copyright : The Lua(Meta)TeX Team(s) (2005-2022+)\n"
+ "\n"
+ "The LuaMetaTeX project is related to ConTeXt development. This macro package\n"
+ "tightly integrates TeX and MetaPost in close cooperation with Lua. Updates will\n"
+ "happen in sync with ConTeXt and when needed. Don't be fooled by unchanged dates:\n"
+ "long term stability is the objective."
+ );
+ exit(EXIT_SUCCESS);
+}
+
+/*tex
+
+ We only mention the most relevelant credits here. The first part is there to indicate a bit of
+ history. A very large part of the code, of course, comes from Don Knuths original \TEX, and the
+ same is true for most documentation!
+
+ Most of the \ETEX\ extensions are present too. Much of the expansion and protrusion code
+ originates in \PDFTEX\ but we don't have any of its backend code. From \OMEGA\ (\ALEPH) we took
+ bits and pieces too, for instance the basics of handling directions but at this point we only
+ have two directions left (that don't need much code). One features that sticks are the left-
+ and right boxes.
+
+ The \METAPOST\ library is an important component and also add quite some code. Here we use a
+ stripped down version of the version 2 library with some extra additions.
+
+ We take \LUA\ as it is. In the meantime we went from \LUA\ 5.2 to 5.3 to 5.4 and will follow up
+ on what makes sense. For as far as possible no changes are made but there are some configuration
+ options in use. We use an \UTF8\ aware setup. Of course \LPEG\ is part of the deal.
+
+ The lean and mean \PDF\ library is made for \LUATEX\ and we use that one here too. In
+ \LUAMETATEX\ we use some of its helpers to implement for instance md5 and sha support. In
+ \LUAMETATEX\ there are some more than mentioned here but they are {\em not} part of the default
+ binary. Some libraries mentioned below can become loaded on demand.
+
+*/
+
+static void enginelib_show_credits(void)
+{
+ tex_print_version_banner();
+ puts(
+ "\n"
+ "\n"
+ "Here we mention those involved in the bits and pieces that define " luametatex_name_camelcase ". More details of\n"
+ "what comes from where can be found in the manual and other documents (that come with ConTeXt).\n"
+ "\n"
+ " luametatex : Hans Hagen, Alan Braslau, Mojca Miklavec, Wolfgang Schuster, Mikael Sundqvist\n"
+ "\n"
+ "It is a follow up on:\n"
+ "\n"
+ " luatex : Hans Hagen, Hartmut Henkel, Taco Hoekwater, Luigi Scarso\n"
+ "\n"
+ "This program itself builds upon the code from:\n"
+ "\n"
+ " tex : Donald Knuth\n"
+ "\n"
+ "We also took a few features from:\n"
+ "\n"
+ " etex : Peter Breitenlohner, Phil Taylor and friends\n"
+ "\n"
+ "The font expansion and protrusion code is derived from:\n"
+ "\n"
+ " pdftex : Han The Thanh and friends\n"
+ "\n"
+ "Part of the bidirectional text flow model is inspired by:\n"
+ "\n"
+ " omega : John Plaice and Yannis Haralambous\n"
+ " aleph : Giuseppe Bilotta\n"
+ "\n"
+ "Graphic support is originates in:\n"
+ "\n"
+ " metapost : John Hobby, Taco Hoekwater, Luigi Scarso, Hans Hagen and friends\n"
+ "\n"
+ "All this is opened up with:\n"
+ "\n"
+ " lua : Roberto Ierusalimschy, Waldemar Celes and Luiz Henrique de Figueiredo\n"
+ " lpeg : Roberto Ierusalimschy\n"
+ "\n"
+ "A few libraries are embedded, of which we mention:\n"
+ "\n"
+# ifdef MI_MALLOC_VERSION
+ " mimalloc : Daan Leijen (https://github.com/microsoft/mimalloc)\n" /* not enabled for arm yet */
+# endif
+ " miniz : Rich Geldreich etc\n"
+ " pplib : Paweł Jackowski (with partial code from libraries)\n"
+ " md5 : Peter Deutsch (with partial code from pplib libraries)\n"
+ " sha2 : Aaron D. Gifford (with partial code from pplib libraries)\n"
+ " socket : Diego Nehab (partial and adapted)\n"
+ " libcerf : Joachim Wuttke (adapted for MSVC)\n"
+ " decnumber : Mike Cowlishaw from IBM (one of the number models in MP)\n"
+ " avl : Richard (adapted a bit to fit in)\n"
+ " hjn : Raph Levien (derived from TeX's hyphenator, but adapted again)\n"
+ "\n"
+ "The code base contains more names and references. Some libraries are partially adapted or\n"
+ "have been replaced. The MetaPost library has additional functionality, some of which is\n"
+ "experimental. The LuaMetaTeX project relates to ConTeXt. This LuaMetaTeX 2+ variant is a\n"
+ "lean and mean variant of LuaTeX 1+ but the core typesetting functionality is the same and\n"
+ "and has been extended in many aspects.\n"
+ "\n"
+ "There is a lightweight subsystem for optional libraries but here we also delegate as much\n"
+ "as possibe to Lua. A few interfaces are provided bny default, others can be added using a\n"
+ "simple foreign interface subsystem. Although this is provided an dconsidered part of the\n"
+ "LuaMetaTeX engine it is not something ConTeXt depends (and will) depend on.\n"
+ "\n"
+ "version : " luametatex_version_string " | " LMT_TOSTRING(luametatex_development_id) "\n"
+ "format id : " LMT_TOSTRING(luametatex_format_fingerprint) "\n"
+# ifdef __DATE__
+ "date : " __TIME__ " | " __DATE__ "\n"
+# endif
+# ifdef LMT_COMPILER_USED
+ "compiler : " LMT_COMPILER_USED "\n"
+# endif
+ );
+ exit(EXIT_SUCCESS);
+}
+
+/*tex
+
+ Some properties of the command line (and startup call) are reflected in variables that start
+ with \type {self}.
+
+*/
+
+static void enginelib_prepare_cmdline(int zero_offset)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ /*tex We keep this reorganized |arg| table, which can start at -3! */
+ lua_createtable(L, lmt_environment_state.argc, 0);
+ for (lua_Integer i = 0; i < lmt_environment_state.argc; i++) {
+ lua_set_string_by_index(L, (int) (i - zero_offset), lmt_environment_state.argv[i]);
+ }
+ lua_setglobal(L, "arg");
+ /* */
+ lua_getglobal(L, "os");
+ lua_set_string_by_key(L, "selfbin", lmt_environment_state.argv[0]);
+ lua_set_string_by_key(L, "selfpath", lmt_environment_state.ownpath);
+ lua_set_string_by_key(L, "selfdir", lmt_environment_state.ownpath); /* for old times sake */
+ lua_set_string_by_key(L, "selfbase", lmt_environment_state.ownbase);
+ lua_set_string_by_key(L, "selfname", lmt_environment_state.ownname);
+ lua_set_string_by_key(L, "selfcore", lmt_environment_state.owncore);
+ lua_createtable(L, lmt_environment_state.argc, 0);
+ for (lua_Integer i = 0; i < lmt_environment_state.argc; i++) {
+ lua_set_string_by_index(L, (int) i, lmt_environment_state.argv[i]);
+ }
+ lua_setfield(L, -2, "selfarg");
+}
+
+/*tex
+
+ Argument checking is somewhat tricky because it can interfere with the used console (shell). It
+ makes sense to combine this with the \LUA\ command line parser code but even that is no real way
+ out. For instance, on \MSWINDOWS\ we need to deal with wide characters.
+
+ The code below is as independent from libraries as possible and differs from the the code used
+ in other \TEX\ engine. We issue no warnings and silently recover, because in the end the macro
+ package (and its \LUA\ code) can deal with that.
+
+*/
+
+static void enginelib_check_option(char **options, int i)
+{
+ char *option = options[i];
+ char *n = option;
+ lmt_environment_state.flag = NULL;
+ lmt_environment_state.value = NULL;
+ if (*n == '-') {
+ n++;
+ } else {
+ goto NOTHING;
+ }
+ if (*n == '-') {
+ n++;
+ } else {
+ goto NOTHING;
+ }
+ if (*n == '\0') {
+ return;
+ }
+ {
+ char *v = strchr(n, '=');
+ size_t l = (int) (v ? (v - n) : strlen(n));
+ lmt_environment_state.flag = lmt_memory_malloc(l + 1);
+ if (lmt_environment_state.flag) {
+ memcpy(lmt_environment_state.flag, n, l);
+ lmt_environment_state.flag[l] = '\0';
+ if (v) {
+ v++;
+ l = (int) strlen(v);
+ lmt_environment_state.value = lmt_memory_malloc(l + 1);
+ if (lmt_environment_state.value) {
+ memcpy(lmt_environment_state.value, v, l);
+ lmt_environment_state.value[l] = '\0';
+ }
+ }
+ }
+ return;
+ }
+ NOTHING:
+ if (lmt_environment_state.name == NULL && i > 0) {
+ lmt_environment_state.name = option;
+ lmt_environment_state.npos = i;
+ }
+}
+
+/*tex
+
+ The |lmt| suffix is actually a \CONTEXT\ thing but it permits us to have \LUA\ files for
+ \LUAMETATEX\ and \LUATEX\ alongside. The ones for this engine can use a more recent variant of
+ \LUA\ and thereby be not compatible. Especially syntax extension complicates this like using
+ |<const>| in \LUA 5.4+ or before that bitwise operators in \LUA\ 5.3 (not/never in \LUAJIT).
+
+*/
+
+const char *suffixes[] = { "lmt", "lua", NULL };
+
+static void enginelib_parse_options(void)
+{
+ /*tex We add 5 chars (separator and suffix) so we reserve 6. */
+ char *firstfile = (char*) lmt_memory_malloc(strlen(lmt_environment_state.ownpath) + strlen(lmt_environment_state.owncore) + 6);
+ for (int i = 0; suffixes[i]; i++) {
+ sprintf(firstfile, "%s/%s.%s", lmt_environment_state.ownpath, lmt_environment_state.owncore, suffixes[i]);
+ /* stat */
+ if (aux_is_readable(firstfile)) {
+ lmt_memory_free(lmt_engine_state.startup_filename);
+ lmt_engine_state.startup_filename = firstfile;
+ lmt_environment_state.luatex_lua_offset = 0;
+ lmt_engine_state.lua_only = 1;
+ lmt_engine_state.lua_init = 1;
+ return;
+ }
+ }
+ lmt_memory_free(firstfile);
+ firstfile = NULL;
+ /* */
+ for (int i = 1;;) {
+ if (i == lmt_environment_state.argc || *lmt_environment_state.argv[i] == '\0') {
+ break;
+ }
+ enginelib_check_option(lmt_environment_state.argv, i);
+ i++;
+ if (! lmt_environment_state.flag) {
+ continue;
+ }
+ if (strcmp(lmt_environment_state.flag, "luaonly") == 0) {
+ lmt_engine_state.lua_only = 1;
+ lmt_environment_state.luatex_lua_offset = i;
+ lmt_engine_state.lua_init = 1;
+ } else if (strcmp(lmt_environment_state.flag, "lua") == 0) {
+ if (lmt_environment_state.value) {
+ lmt_memory_free(lmt_engine_state.startup_filename);
+ lmt_engine_state.startup_filename = lmt_memory_strdup(lmt_environment_state.value);
+ lmt_environment_state.luatex_lua_offset = i - 1;
+ lmt_engine_state.lua_init = 1;
+ }
+ } else if (strcmp(lmt_environment_state.flag, "jobname") == 0) {
+ if (lmt_environment_state.value) {
+ lmt_memory_free(lmt_engine_state.startup_jobname);
+ lmt_engine_state.startup_jobname = lmt_memory_strdup(lmt_environment_state.value);
+ }
+ } else if (strcmp(lmt_environment_state.flag, "fmt") == 0) {
+ if (lmt_environment_state.value) {
+ lmt_memory_free(lmt_engine_state.dump_name);
+ lmt_engine_state.dump_name = lmt_memory_strdup(lmt_environment_state.value);
+ }
+ } else if (! lmt_engine_state.permit_loadlib && strcmp(lmt_environment_state.flag, "permitloadlib") == 0) {
+ lmt_engine_state.permit_loadlib = 1;
+ } else if (strcmp(lmt_environment_state.flag, "ini") == 0) {
+ lmt_main_state.run_state = initializing_state;
+ } else if (strcmp(lmt_environment_state.flag, "help") == 0) {
+ enginelib_show_help();
+ } else if (strcmp(lmt_environment_state.flag, "version") == 0) {
+ enginelib_show_version_info();
+ } else if (strcmp(lmt_environment_state.flag, "credits") == 0) {
+ enginelib_show_credits();
+ }
+ lmt_memory_free(lmt_environment_state.flag);
+ lmt_environment_state.flag = NULL;
+ if (lmt_environment_state.value) {
+ lmt_memory_free(lmt_environment_state.value);
+ lmt_environment_state.value = NULL;
+ }
+ }
+ /*tex This is an attempt to find |input_name| or |dump_name|. */
+ if (lmt_environment_state.argv[lmt_environment_state.npos]) { /* aka name */
+ if (lmt_engine_state.lua_only) {
+ if (! lmt_engine_state.startup_filename) {
+ lmt_engine_state.startup_filename = lmt_memory_strdup(lmt_environment_state.argv[lmt_environment_state.npos]);
+ lmt_environment_state.luatex_lua_offset = lmt_environment_state.npos;
+ }
+ } else if (lmt_environment_state.argv[lmt_environment_state.npos][0] == '&') {
+ /*tex This is historic but and might go away. */
+ if (! lmt_engine_state.dump_name) {
+ lmt_engine_state.dump_name = lmt_memory_strdup(lmt_environment_state.argv[lmt_environment_state.npos] + 1);
+ }
+ } else if (lmt_environment_state.argv[lmt_environment_state.npos][0] == '*') {
+ /*tex This is historic but and might go away. */
+ if (! lmt_environment_state.input_name) {
+ lmt_environment_state.input_name = lmt_memory_strdup(lmt_environment_state.argv[lmt_environment_state.npos] + 1);
+ }
+ } else if (lmt_environment_state.argv[lmt_environment_state.npos][0] == '\\') {
+ /*tex We have a command but this and might go away. */
+ } else {
+ /*tex We check for some suffixes first. */
+ firstfile = lmt_memory_strdup(lmt_environment_state.argv[lmt_environment_state.npos]);
+ for (int i = 0; suffixes[i]; i++) {
+ if (strstr(firstfile, suffixes[i]) == firstfile + strlen(firstfile) - 4){
+ if (lmt_engine_state.startup_filename) {
+ lmt_memory_free(firstfile);
+ } else {
+ lmt_engine_state.startup_filename = firstfile;
+ lmt_environment_state.luatex_lua_offset = lmt_environment_state.npos;
+ lmt_engine_state.lua_only = 1;
+ lmt_engine_state.lua_init = 1;
+ }
+ goto DONE;
+ }
+ }
+ if (lmt_environment_state.input_name) {
+ lmt_memory_free(firstfile);
+ } else {
+ lmt_environment_state.input_name = firstfile;
+ }
+ }
+ }
+ DONE:
+ /*tex Finalize the input filename. */
+ if (lmt_environment_state.input_name) {
+ /* probably not ok */
+ lmt_environment_state.argv[lmt_environment_state.npos] = enginelib_normalize_quotes(lmt_environment_state.input_name, "argument");
+ }
+}
+
+/*tex
+
+ Being a general purpose typesetting system, a \TEX\ system normally has its own way of dealing
+ with language, script, country etc.\ specific properties. It is for that reason that we disable
+ locales.
+
+*/
+
+static void enginelib_set_locale(void)
+{
+ setlocale(LC_ALL, "C");
+}
+
+static void enginelib_update_options(void)
+{
+ int starttime = -1;
+ int utc = -1;
+ int permitloadlib = -1;
+ if (! lmt_environment_state.input_name) {
+ tex_engine_get_config_string("jobname", &lmt_environment_state.input_name);
+ }
+ if (! lmt_engine_state.dump_name) {
+ tex_engine_get_config_string("formatname", &lmt_engine_state.dump_name);
+ }
+ tex_engine_get_config_number("starttime", &starttime);
+ if (starttime >= 0) {
+ aux_set_start_time(starttime);
+ }
+ tex_engine_get_config_boolean("useutctime", &utc);
+ if (utc >= 0 && utc <= 1) {
+ lmt_engine_state.utc_time = utc;
+ }
+ tex_engine_get_config_boolean("permitloadlib", &permitloadlib);
+ if (permitloadlib >= 0) {
+ lmt_engine_state.permit_loadlib = permitloadlib;
+ }
+}
+
+/*tex
+
+ We have now arrived at the main initializer. What happens after this is determined by what
+ callbacks are set. The engine can behave as just a \LUA\ interpreter, startup the \TEX\
+ machinery in so called virgin mode, or load a format and carry on from that.
+
+*/
+
+void tex_engine_initialize(int ac, char **av)
+{
+ /*tex Save to pass along to topenin. */
+ lmt_print_state.selector = terminal_selector_code;
+ lmt_environment_state.argc = aux_utf8_setargv(&lmt_environment_state.argv, av, ac);
+ /* initializations */
+ lmt_engine_state.lua_only = 0;
+ lmt_engine_state.lua_init = 0;
+ lmt_engine_state.startup_filename = NULL;
+ lmt_engine_state.startup_jobname = NULL;
+ lmt_engine_state.engine_name = luametatex_name_lowercase;
+ lmt_engine_state.dump_name = NULL;
+ lmt_engine_state.luatex_banner = lmt_memory_strdup(lmt_version_state.banner);
+ /* preparations */
+ lmt_environment_state.ownpath = aux_utf8_getownpath(lmt_environment_state.argv[0]);
+ enginelib_splitnames();
+ aux_set_run_time();
+ /*tex
+ Some options must be initialized before options are parsed. We don't need that many as we
+ can delegate to \LUA.
+ */
+ /*tex Parse the commandline. */
+ enginelib_parse_options();
+ /*tex Forget about locales. */
+ enginelib_set_locale();
+ /*tex Initialize the \LUA\ instance and keys. */
+ lmt_initialize();
+ /*tex This can be redone later. */
+ lmt_initialize_functions(0);
+ lmt_initialize_properties(0);
+ /*tex For word handlers. */
+ lmt_initialize_languages();
+ /*tex Here start the key definitions (will become functions). */
+ lmt_initialize_interface();
+ lmt_nodelib_initialize();
+ lmt_tokenlib_initialize();
+ lmt_fontlib_initialize();
+ /*tex Collect arguments. */
+ enginelib_prepare_cmdline(lmt_environment_state.luatex_lua_offset);
+ if (lmt_engine_state.startup_filename && ! aux_is_readable(lmt_engine_state.startup_filename)) {
+ lmt_memory_free(lmt_engine_state.startup_filename);
+ lmt_engine_state.startup_filename = NULL;
+ }
+ /*tex
+ Now run the file (in \LUATEX\ there is a special \TEX\ table pushed with limited
+ functionality (initialize, run, finish) but the normal tex helpers are not unhidden so
+ basically one has no \TEX. We no longer have that.
+ */
+ if (lmt_engine_state.startup_filename) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (lmt_engine_state.lua_only) {
+ if (luaL_loadfile(L, lmt_engine_state.startup_filename)) {
+ tex_emergency_message("lua error", "startup file: %s", lmt_error_string(L, -1));
+ tex_emergency_exit();
+ } else if (lua_pcall(L, 0, 0, 0)) {
+ tex_emergency_message("lua error", "function call: %s", lmt_error_string(L, -1));
+ lmt_traceback(L);
+ tex_emergency_exit();
+ } else {
+ /*tex We're okay. */
+ exit(lmt_error_state.default_exit_code);
+ }
+ } else {
+ /*tex a normal tex run */
+ if (luaL_loadfile(L, lmt_engine_state.startup_filename)) {
+ tex_emergency_message("lua error", "startup file: %s", lmt_error_string(L, -1));
+ tex_emergency_exit();
+ } else if (lua_pcall(L, 0, 0, 0)) {
+ tex_emergency_message("lua error", "function call: %s", lmt_error_string(L, -1));
+ lmt_traceback(L);
+ tex_emergency_exit();
+ }
+ enginelib_update_options();
+ tex_check_fmt_name();
+ }
+ } else if (lmt_engine_state.lua_init) {
+ tex_emergency_message("startup error", "no valid startup file given, quitting");
+ tex_emergency_exit();
+ } else {
+ tex_check_fmt_name();
+ }
+}
+
+/*tex
+
+ For practical and historical reasons some of the initalization and checking is split. The
+ mainbody routine call out to these functions. The timing is sort of tricky: we can use a start
+ up script, that sets some configuration parameters, and for sure some callbacks, and these, in
+ turn, are then responsible for follow up actions like telling where to find the format file
+ (when a dump is loaded) or startup file (when we're in virgin mode). When we are in neither of
+ these modes the engine is just a \LUA\ interpreter which means that only a subset of libraries
+ is initialized.
+
+*/
+
+static void tex_engine_get_config_numbers(const char *name, int *minimum, int *maximum, int *size, int *step)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (L && size) {
+ int stacktop = lua_gettop(L);
+ if (lua_getglobal(L, "texconfig") == LUA_TTABLE) {
+ switch (lua_getfield(L, -1, name)) {
+ case LUA_TNUMBER:
+ if (size) {
+ *size = (int) lmt_roundnumber(L, -1);
+ }
+ break;
+ case LUA_TTABLE:
+ if (size && lua_getfield(L, -1, "size")) {
+ *size = (int) lmt_roundnumber(L, -1);
+ }
+ lua_pop(L, 1);
+ if (size && lua_getfield(L, -1, "plus")) {
+ *size += (int) lmt_roundnumber(L, -1);
+ }
+ lua_pop(L, 1);
+ if (step && lua_getfield(L, -1, "step")) {
+ int stp = (int) lmt_roundnumber(L, -1);
+ if (stp > *step) {
+ *step = stp;
+ }
+ }
+ break;
+ }
+ if (minimum && *size < *minimum) {
+ *size = *minimum;
+ } else if (maximum && *size > *maximum) {
+ *size = *maximum;
+ }
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+void tex_engine_set_memory_data(const char *name, memory_data *data)
+{
+ tex_engine_get_config_numbers(name, &data->minimum, &data->maximum, &data->size, &data->step);
+}
+
+void tex_engine_set_limits_data(const char *name, limits_data *data)
+{
+ tex_engine_get_config_numbers(name, &data->minimum, &data->maximum, &data->size, NULL);
+}
+
+void tex_engine_get_config_boolean(const char *name, int *target)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (L) {
+ int stacktop = lua_gettop(L);
+ if (lua_getglobal(L, "texconfig") == LUA_TTABLE) {
+ switch (lua_getfield(L, -1, name)) {
+ case LUA_TBOOLEAN:
+ *target = lua_toboolean(L, -1);
+ break;
+ case LUA_TNUMBER:
+ *target = (lua_tointeger(L, -1) == 0 ? 0 : 1);
+ break;
+ }
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+void tex_engine_get_config_number(const char *name, int *target)
+{
+ tex_engine_get_config_numbers(name, NULL, NULL, target, NULL);
+}
+
+void tex_engine_get_config_string(const char *name, char **target)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (L) {
+ int stacktop = lua_gettop(L);
+ if (lua_getglobal(L, "texconfig") == LUA_TTABLE) {
+ if (lua_getfield(L, -1, name) == LUA_TSTRING) {
+ *target = lmt_memory_strdup(lua_tostring(L, -1));
+ }
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+int tex_engine_run_config_function(const char *name)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (L) {
+ if (lua_getglobal(L, "texconfig") == LUA_TTABLE) {
+ if (lua_getfield(L, -1, name) == LUA_TFUNCTION) {
+ if (! lua_pcall(L, 0, 0, 0)) {
+ return 1;
+ } else {
+ /*tex
+ We can't be more precise here as it's called before \TEX\ initialization
+ happens.
+ */
+ tex_emergency_message("lua", "this went wrong: %s\n", lmt_error_string(L, -1));
+ tex_emergency_exit();
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void tex_engine_check_configuration(void)
+{
+ tex_engine_run_config_function("init");
+}
+
+void lmt_make_table(
+ lua_State *L,
+ const char *tab,
+ const char *mttab,
+ lua_CFunction getfunc,
+ lua_CFunction setfunc
+)
+{
+ lua_pushstring(L, tab); /*tex |[{<tex>},"dimen"]| */
+ lua_newtable(L); /*tex |[{<tex>},"dimen",{}]| */
+ lua_settable(L, -3); /*tex |[{<tex>}]| */
+ lua_pushstring(L, tab); /*tex |[{<tex>},"dimen"]| */
+ lua_gettable(L, -2); /*tex |[{<tex>},{<dimen>}]| */
+ luaL_newmetatable(L, mttab); /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
+ lua_pushstring(L, "__index"); /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index"]| */
+ lua_pushcfunction(L, getfunc); /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__index","getdimen"]| */
+ lua_settable(L, -3); /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
+ lua_pushstring(L, "__newindex"); /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex"]| */
+ lua_pushcfunction(L, setfunc); /*tex |[{<tex>},{<dimen>},{<dimen_m>},"__newindex","setdimen"]| */
+ lua_settable(L, -3); /*tex |[{<tex>},{<dimen>},{<dimen_m>}]| */
+ lua_setmetatable(L, -2); /*tex |[{<tex>},{<dimen>}]| : assign the metatable */
+ lua_pop(L, 1); /*tex |[{<tex>}]| : clean the stack */
+}
+
+static void *enginelib_aux_luaalloc(
+ void *ud, /*tex Not used, but passed by \LUA. */
+ void *ptr, /*tex The old pointer. */
+ size_t osize, /*tex The old size. */
+ size_t nsize /*tex The new size. */
+)
+{
+ (void) ud;
+ lmt_lua_state.used_bytes += (int) (nsize - osize);
+ if (lmt_lua_state.used_bytes > lmt_lua_state.used_bytes_max) {
+ lmt_lua_state.used_bytes_max = lmt_lua_state.used_bytes;
+ }
+ /*tex Quite some reallocs happen in \LUA. */
+ if (nsize == 0) {
+ /* printf("free %i\n",(int) osize); */
+ lmt_memory_free(ptr);
+ return NULL;
+ } else if (osize == 0) {
+ /* printf("malloc %i\n",(int) nsize); */
+ return lmt_memory_malloc(nsize);
+ } else {
+ /* printf("realloc %i -> %i\n",(int)osize,(int)nsize); */
+ return lmt_memory_realloc(ptr, nsize);
+ }
+}
+
+static int enginelib_aux_luapanic(lua_State *L)
+{
+ (void) L;
+ tex_emergency_message("lua", "panic: unprotected error in call to Lua API (%s)\n", lmt_error_string(L, -1));
+ return tex_emergency_exit();
+}
+
+static const luaL_Reg lmt_libs_lua_function_list[] = {
+ { "_G", luaopen_base },
+ { "package", luaopen_package },
+ { "table", luaopen_table },
+ { "io", luaopen_io },
+ { "os", luaopen_os },
+ { "string", luaopen_string },
+ { "math", luaopen_math },
+ { "debug", luaopen_debug },
+ { "lpeg", luaopen_lpeg },
+ { "utf8", luaopen_utf8 },
+ { "coroutine", luaopen_coroutine },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_extra_function_list[] = {
+ { "md5", luaopen_md5 },
+ { "sha2", luaopen_sha2 },
+ { "aes", luaopen_aes },
+ { "basexx", luaopen_basexx },
+ { "lfs", luaopen_filelib }, /* for practical reasons we keep this namespace */
+ { "fio", luaopen_fio },
+ { "sio", luaopen_sio },
+ { "sparse", luaopen_sparse },
+ { "xzip", luaopen_xzip },
+ { "xmath", luaopen_xmath },
+ { "xcomplex", luaopen_xcomplex },
+ { "xdecimal", luaopen_xdecimal },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_socket_function_list[] = {
+ { "socket", luaopen_socket_core },
+ { "mime", luaopen_mime_core },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_more_function_list[] = {
+ { "lua", luaopen_lua },
+ { "luac", luaopen_luac },
+ { "status", luaopen_status },
+ { "texio", luaopen_texio },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_tex_function_list[] = {
+ { "tex", luaopen_tex },
+ { "token", luaopen_token },
+ { "node", luaopen_node },
+ { "callback", luaopen_callback },
+ { "font", luaopen_font },
+ { "language", luaopen_language },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_mp_function_list[] = {
+ { "mplib", luaopen_mplib },
+ { NULL, NULL },
+};
+
+static const luaL_Reg lmt_libs_pdf_function_list[] = {
+ { "pdfe", luaopen_pdfe },
+ { "pdfdecode", luaopen_pdfdecode },
+ { "pngdecode", luaopen_pngdecode },
+ { NULL, NULL },
+};
+
+/*tex
+
+ So, we have different library initialization lists for the the two \TEX\ modes (ini and normal)
+ and \LUA\ mode (interpeter). It's not pretty yet but it might become better over time.
+
+ */
+
+static void enginelib_luaopen_liblist(lua_State *L, const luaL_Reg *lib)
+{
+ for (; lib->func; lib++) {
+ luaL_requiref(L, lib->name, lib->func, 1);
+ lua_setglobal(L, lib->name);
+ }
+}
+
+/*tex
+
+ In order to overcome (expected) debates about security we disable loading libraries unless
+ explicitly enabled (as in \LUATEX). An exception are the optional libraries, but as these
+ interfaces are rather bound to the cannonical \LUAMETATEX\ source code we can control these
+ from \CONTEXT\ of needed because before users can run code, we can block support of these
+ libraries. On the other hand, we have no reason to distrust the few that can (optionally) be
+ used (they also cannot clash with different \LUA\ versions).
+
+ \starttyping
+ package.loadlib = nil|
+ package.searchers[4] = nil
+ package.searchers[3] = nil
+ \stoptyping
+
+*/
+
+static int loadlib_warning(lua_State *L)
+{
+ (void) L;
+ tex_normal_error("lua loadlib", "you can only load external libraries when --permitloadlib is given");
+ return 0;
+}
+
+static void enginelib_disable_loadlib(lua_State *L)
+{
+ int top = lua_gettop(L);
+ lua_getglobal(L, "package");
+ lua_pushliteral(L, "loadlib");
+ lua_pushcfunction(L, &loadlib_warning);
+ lua_rawset(L, -3);
+ lua_pushliteral(L, "searchers");
+ lua_rawget(L, -2);
+ lua_pushnil(L);
+ lua_rawseti(L, -2, 4);
+ lua_pushnil(L);
+ lua_rawseti(L, -2, 3);
+ lua_settop(L, top);
+}
+
+void lmt_initialize(void)
+{
+ lua_State *L = lua_newstate(enginelib_aux_luaalloc, NULL);
+ if (L) {
+ /*tex By default we use the generational garbage collector. */
+ lua_gc(L, LUA_GCGEN, 0, 0);
+ /* */
+ lmt_lua_state.bytecode_max = -1;
+ lmt_lua_state.bytecode_bytes = 0;
+ lmt_lua_state.lua_instance = L;
+ /* */
+ lua_atpanic(L, &enginelib_aux_luapanic);
+ /*tex Initialize the internalized strings. */
+ lmt_initialize_shared_keys(L);
+ lmt_initialize_metapost_keys(L);
+ /*tex This initializes all the 'simple' libraries: */
+ enginelib_luaopen_liblist(L, lmt_libs_lua_function_list);
+ /*tex This initializes all the 'extra' libraries: */
+ enginelib_luaopen_liblist(L, lmt_libs_extra_function_list);
+ /*tex These are special: we extend them. */
+ luaextend_os(L);
+ luaextend_io(L);
+ luaextend_string(L);
+ /*tex Loading the socket library is a bit odd (old stuff). */
+ enginelib_luaopen_liblist(L, lmt_libs_socket_function_list);
+ /*tex This initializes the 'tex' related libraries that have some luaonly functionality */
+ enginelib_luaopen_liblist(L, lmt_libs_more_function_list);
+ /*tex This initializes the 'tex' related libraries. */
+ if (! lmt_engine_state.lua_only) {
+ enginelib_luaopen_liblist(L, lmt_libs_tex_function_list);
+ }
+ if (! lmt_engine_state.permit_loadlib) {
+ enginelib_disable_loadlib(L);
+ }
+ /*tex Optional stuff. */
+ luaopen_optional(L);
+ /*tex This initializes the 'metapost' related libraries. */
+ enginelib_luaopen_liblist(L, lmt_libs_mp_function_list);
+ /*tex This initializes the 'pdf' related libraries. */
+ enginelib_luaopen_liblist(L, lmt_libs_pdf_function_list);
+ /*tex This one can become optional! */
+ luaextend_xcomplex(L);
+ /*tex We're nearly done! In this table we're going to put some info: */
+ lua_createtable(L, 0, 0);
+ lua_setglobal(L, "texconfig");
+ /* Maybe this will embed the checkstack function that some libs need. */
+ /* lua_checkstack(L, 1); */
+ } else {
+ tex_emergency_message("system", "the Lua state can't be created");
+ tex_emergency_exit();
+ }
+}
+
+int lmt_traceback(lua_State *L)
+{
+ const char *msg = lua_tostring(L, 1);
+ luaL_traceback(L, L, msg ? msg : "<no message>", 1);
+ return 1;
+}
+
+void lmt_error(
+ lua_State *L,
+ const char *where, /*tex The message has two parts. */
+ int detail, /*tex A function slot or callback index or ... */
+ int is_fatal /*tex We quit if this is the case */
+)
+{
+ char* err = NULL;
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ const char *luaerr = lua_tostring(L, -1);
+ size_t len = strlen(luaerr) + strlen(where) + 32; /*tex Add some slack. */
+ err = (char *) lmt_memory_malloc((unsigned) len);
+ if (err) {
+ if (detail >= 0) {
+ snprintf(err, len, "%s [%i]: %s", where, detail, luaerr);
+ } else {
+ snprintf(err, len, "%s: %s", where, luaerr);
+ }
+ if (lmt_error_state.last_lua_error) {
+ lmt_memory_free(lmt_error_state.last_lua_error);
+ }
+ }
+ lmt_error_state.last_lua_error = err;
+ }
+ if (is_fatal > 0) {
+ /*
+ Normally a memory error from lua. The pool may overflow during the |maketexlstring()|,
+ but we are crashing anyway so we may as well abort on the pool size. It is probably
+ too risky to show the error context now but we can imagine some more granularity.
+ */
+ tex_normal_error("lua", err ? err : where);
+ /*tex
+ This should never be reached, so there is no need to close, so let's make sure of
+ that!
+ */
+ /* lua_close(L); */
+ }
+ else {
+ tex_normal_warning("lua", err ? err : where);
+ }
+}
+
+/*tex
+
+ As with other dump related actions, this module provides its relevant properties. A dump is
+ just that: variables written to a stream, and an undump reads instead. Some basic checking
+ happens in these functions.
+
+*/
+
+void lmt_dump_engine_info(dumpstream f)
+{
+ /*tex We align |engine_name| to 4 bytes with one or more trailing |NUL|. */
+ int x = (int) strlen(lmt_engine_state.engine_name);
+ if (x > 0) {
+ char *format_engine = lmt_memory_malloc((size_t) x + 5);
+ if (format_engine) {
+ memcpy(format_engine, lmt_engine_state.engine_name, (size_t) x + 1);
+ for (int k = x; k <= x + 3; k++) {
+ format_engine[k] = 0;
+ }
+ x = x + 4 - (x % 4);
+ dump_int(f, x);
+ dump_things(f, format_engine[0], x);
+ lmt_memory_free(format_engine);
+ return;
+ }
+ }
+ tex_normal_error("system","dumping engine info failed");
+}
+
+void lmt_undump_engine_info(dumpstream f)
+{
+ int x;
+ undump_int(f, x);
+ if ((x > 1) && (x < 256)) {
+ char *format_engine = lmt_memory_malloc((size_t) x);
+ if (format_engine) {
+ undump_things(f, format_engine[0], x);
+ format_engine[x - 1] = 0;
+ if (strcmp(lmt_engine_state.engine_name, format_engine)) {
+ lmt_memory_free(format_engine);
+ goto BAD;
+ } else {
+ lmt_memory_free(format_engine);
+ return;
+ }
+ }
+ }
+ BAD:
+ tex_fatal_undump_error("engine");
+}
+
+const char *lmt_error_string(lua_State* L, int index)
+{
+ const char *s = lua_tostring(L, index);
+ return s ? s : "unknown error";
+}
diff --git a/source/luametatex/source/lua/lmtenginelib.h b/source/luametatex/source/lua/lmtenginelib.h
new file mode 100644
index 000000000..a6aef849c
--- /dev/null
+++ b/source/luametatex/source/lua/lmtenginelib.h
@@ -0,0 +1,41 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LUAINIT_H
+# define LMT_LUAINIT_H
+
+typedef struct engine_state_info {
+ int lua_init;
+ int lua_only;
+ const char *luatex_banner;
+ const char *engine_name;
+ char *startup_filename;
+ char *startup_jobname;
+ char *dump_name; /* could move to dump_state */
+ int utc_time; /* kind of obsolete, could be a callback */
+ int permit_loadlib;
+} engine_state_info;
+
+extern engine_state_info lmt_engine_state;
+
+extern void tex_engine_initialize (int ac, char **av);
+extern char *tex_engine_input_filename (void);
+extern void tex_engine_check_configuration (void);
+
+extern void tex_engine_get_config_boolean (const char *name, int *target);
+extern void tex_engine_get_config_number (const char *name, int *target);
+extern void tex_engine_get_config_string (const char *name, char **target);
+extern int tex_engine_run_config_function (const char *name);
+extern void tex_engine_set_memory_data (const char *name, memory_data *data);
+extern void tex_engine_set_limits_data (const char *name, limits_data *data);
+
+extern void lmt_make_table (lua_State *L, const char *tab, const char *mttab, lua_CFunction getfunc, lua_CFunction setfunc);
+extern int lmt_traceback (lua_State *L);
+extern void lmt_error (lua_State *L, const char *where, int detail, int fatal);
+extern void lmt_initialize (void);
+extern void lmt_dump_engine_info (dumpstream f);
+extern void lmt_undump_engine_info (dumpstream f);
+extern const char *lmt_error_string (lua_State *L, int index);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtfontlib.c b/source/luametatex/source/lua/lmtfontlib.c
new file mode 100644
index 000000000..09429d98a
--- /dev/null
+++ b/source/luametatex/source/lua/lmtfontlib.c
@@ -0,0 +1,1020 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ There is not that much font related code because much is delegated to \LUA. We're actually back
+ to original \TEX, where only dimensions matter, plus some basic information about constructing
+ (base mode) ligatures and (base mode) kerning. Also, we need to store some math specific
+ properties of glyphs so that the math machinery can do its work.
+
+ Compared to traditional \TEX\ the most impressive extension is the amount of new math parameters.
+ There are also some new concepts, like staircase kerns. In the typesetting related code this is
+ reflected in dedicated code paths.
+
+ The code is different from the code in \LUATEX. Because we don't have a backend built in, we need
+ to store less. Also, there are quite some optimizations so that large fonts consume less memory.
+ After all, we have whatever is available already in \LUA\ tables. The engine only needs a few
+ dimensions to work with, plus some ligature and kern information for old school fonts, and when
+ applicable some additional data that relates to math. So, for instance, we no longer allocate
+ memory when we have no math.
+
+ We start with some tables to which we might do more with this data and add more entries at some
+ point. We are prepared.
+
+ */
+
+
+void lmt_fontlib_initialize(void) {
+ /* nothing */
+}
+
+static int valid_math_parameter(lua_State *L, int narg) {
+ const char *s = lua_tostring(L, narg);
+ if (s) {
+ for (int i = 1; lmt_interface.math_font_parameter_values[i].name; i++) {
+ if (lmt_interface.math_font_parameter_values[i].name == s) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+ Most of these special ligature indicators have never been used by fonts but they are part of
+ \TEX's legacy so of course we keep them around!
+
+*/
+
+static const char *lmt_ligature_type_strings[] = {
+ "=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL
+};
+
+static int fontlib_aux_count_hash_items(lua_State *L)
+{
+ int n = 0;
+ if (lua_type(L, -1) == LUA_TTABLE) {
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ n++;
+ lua_pop(L, 1);
+ }
+ }
+ return n;
+}
+
+/*tex
+
+ These macros set a field in the font or character records. Watch how we make a copy of a string!
+
+*/
+
+/*
+# define set_numeric_field_by_index(target,name,dflt) \
+ lua_key_rawgeti(name); \
+ target = (lua_type(L, -1) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : dflt ; \
+ lua_pop(L, 1);
+
+# define set_boolean_field_by_index(target,name,dflt) \
+ lua_key_rawgeti(name); \
+ target = (lua_type(L, -1) == LUA_TBOOLEAN) ? lua_toboolean(L, -1) : dflt ; \
+ lua_pop(L, 1);
+
+# define set_string_field_by_index(target,name) \
+ lua_key_rawgeti(name); \
+ target = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1) : NULL ; \
+ lua_pop(L, 1);
+*/
+
+# define set_numeric_field_by_index(target,name,dflt) \
+ lua_push_key(name); \
+ target = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : dflt ; \
+ lua_pop(L, 1);
+
+# define set_boolean_field_by_index(target,name,dflt) \
+ lua_push_key(name); \
+ target = (lua_rawget(L, -2) == LUA_TBOOLEAN) ? lua_toboolean(L, -1) : dflt ; \
+ lua_pop(L, 1);
+
+# define set_string_field_by_index(target,name) \
+ lua_push_key(name); \
+ target = (lua_rawget(L, -2) == LUA_TSTRING) ? lua_tostring(L, -1) : NULL ; \
+ lua_pop(L, 1);
+
+# define set_any_field_by_index(target,name) \
+ lua_push_key(name); \
+ target = (lua_rawget(L, -2) != LUA_TNIL); \
+ lua_pop(L, 1);
+
+/*tex
+
+ Font parameters can be set by number or by name. There are seven basic \TEX\ parameters in text
+ mode but in math mode there can be numerous.
+
+*/
+
+static void fontlib_aux_read_lua_parameters(lua_State *L, int f)
+{
+ lua_push_key(parameters);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ /*tex We determine the the number of parameters in the |max(nofintegerkeys(L), 7)|. */
+ int maxindex = 7;
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ int i = (int) lua_tointeger(L, -2);
+ if (i > maxindex) {
+ maxindex = i;
+ }
+ }
+ lua_pop(L, 1);
+ }
+ /*tex
+ We enlarge the parameter array. The first zeven values are already initialized to zero
+ when the font structure is allocated.
+ */
+ if (maxindex > 7) {
+ tex_set_font_parameters(f, maxindex);
+ }
+ /*tex
+ First we pick up the numeric entries. The values set with keys can later overload
+ these. It's there for old times sake, because numeric parameters are gone.
+ */
+ for (int i = 1; i <= maxindex; i++) {
+ if (lua_rawgeti(L, -1, i) == LUA_TNUMBER) {
+ halfword value = lmt_roundnumber(L, -1);
+ tex_set_font_parameter(f, i, value);
+ }
+ lua_pop(L, 1);
+ }
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ halfword value = lua_type(L, -1) == LUA_TNUMBER ? lmt_roundnumber(L, -1) : 0;
+ switch (lua_type(L, -2)) {
+ case LUA_TSTRING:
+ {
+ /* These can overload the already set-by-index values. */
+ const char *s = lua_tostring(L, -2);
+ if (lua_key_eq(s, slant)) {
+ tex_set_font_parameter(f, slant_code, value);
+ } else if (lua_key_eq(s, space)) {
+ tex_set_font_parameter(f, space_code, value);
+ } else if (lua_key_eq(s, spacestretch)) {
+ tex_set_font_parameter(f, space_stretch_code, value);
+ } else if (lua_key_eq(s, spaceshrink)) {
+ tex_set_font_parameter(f, space_shrink_code, value);
+ } else if (lua_key_eq(s, xheight)) {
+ tex_set_font_parameter(f, ex_height_code, value);
+ } else if (lua_key_eq(s, quad)) {
+ tex_set_font_parameter(f, em_width_code, value);
+ } else if (lua_key_eq(s, extraspace)) {
+ tex_set_font_parameter(f, extra_space_code, value);
+ }
+ }
+ break;
+ case LUA_TNUMBER:
+ {
+ /* Math fonts can have more than 7. */
+ int index = (int) lua_tointeger(L, -2);
+ if (index >= 8) {
+ tex_set_font_parameter(f, index, value);
+ }
+ }
+ break;
+ }
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+
+}
+
+static void fontlib_aux_read_lua_math_parameters(lua_State *L, int f)
+{
+ lua_push_key(MathConstants);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ int n = (int) lmt_roundnumber(L, -1);
+ int i = 0;
+ switch (lua_type(L, -2)) {
+ case LUA_TSTRING:
+ i = valid_math_parameter(L, -2);
+ break;
+ case LUA_TNUMBER:
+ i = (int) lua_tointeger(L, -2);
+ break;
+ }
+ if (i > 0) {
+ // set_font_math_parameter(f, i, n);
+ tex_set_font_math_parameters(f, i);
+ // if (n > undefined_math_parameter || i < - undefined_math_parameter) {
+ // n = undefined_math_parameter;
+ // }
+ font_math_parameter(f, i) = n;
+ }
+ lua_pop(L, 1);
+ }
+ set_font_oldmath(f, 0);
+ } else {
+ set_font_oldmath(f, 1);
+ }
+ lua_pop(L, 1);
+}
+
+/*tex
+
+ Math kerns are tables that specify a staircase. There are upto four such lists, one for each
+ corner. Here is a complete example:
+
+ \starttyping
+ mathkerns = {
+ bottom_left = { { height = 420, kern = 80 }, { height = 520, kern = 4 } },
+ bottom_right = { { height = 0, kern = 48 } },
+ top_left = { { height = 620, kern = 0 }, { height = 720, kern = -80 } },
+ top_right = { { height = 676, kern = 115 }, { height = 776, kern = 45 } },
+ }
+ \stoptyping
+
+*/
+
+static void fontlib_aux_store_math_kerns(lua_State *L, int index, charinfo *co, int id)
+{
+ lua_push_key_by_index(index);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ lua_Integer k = lua_rawlen(L, -1);
+ if (k > 0) {
+ for (lua_Integer l = 1; l <= k; l++) {
+ if (lua_rawgeti(L, -1, l) == LUA_TTABLE) {
+ scaled ht, krn;
+// set_numeric_field_by_index(ht, height, min_infinity);
+// set_numeric_field_by_index(krn, kern, min_infinity);
+// if (krn > min_infinity && ht > min_infinity) {
+// tex_add_charinfo_math_kern(co, id, ht, krn);
+// }
+ set_numeric_field_by_index(ht, height, 0);
+ set_numeric_field_by_index(krn, kern, 0);
+ if (krn || ht) {
+ tex_add_charinfo_math_kern(co, id, ht, krn);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ }
+ lua_pop(L, 1);
+}
+
+static void fontlib_aux_font_char_from_lua(lua_State *L, halfword f, int i, int has_math)
+{
+ if (lua_istable(L, -1)) {
+ /*tex We need an intermediate veriable: */
+ scaled target;
+ int state;
+ charinfo *co = tex_get_charinfo(f, i);
+ set_any_field_by_index(state, callback);
+ set_charinfo_tag(co, state ? callback_tag : 0);
+ set_numeric_field_by_index(target, width, 0);
+ set_charinfo_width(co, target);
+ set_numeric_field_by_index(target, height, 0);
+ set_charinfo_height(co, target);
+ set_numeric_field_by_index(target, depth, 0);
+ set_charinfo_depth(co, target);
+ set_numeric_field_by_index(target, italic, 0);
+ set_charinfo_italic(co, target);
+ set_numeric_field_by_index(target, expansion, 1000);
+ set_charinfo_expansion(co, target);
+ set_numeric_field_by_index(target, leftprotrusion, 0);
+ set_charinfo_leftprotrusion(co, target);
+ set_numeric_field_by_index(target, rightprotrusion, 0);
+ set_charinfo_rightprotrusion(co, target);
+ set_charinfo_tag(co, 0);
+ if (has_math) {
+ tex_char_malloc_mathinfo(co);
+ set_numeric_field_by_index(target, smaller, 0);
+ set_charinfo_smaller(co, target);
+ set_numeric_field_by_index(target, vitalic, 0);
+ set_charinfo_vertical_italic(co, target);
+ /* */
+ set_numeric_field_by_index(target, topleft, 0);
+ set_charinfo_top_left_kern(co, target);
+ set_numeric_field_by_index(target, topright, 0);
+ set_charinfo_top_right_kern(co, target);
+ set_numeric_field_by_index(target, bottomright, 0);
+ set_charinfo_bottom_right_kern(co, target);
+ set_numeric_field_by_index(target, bottomleft, 0);
+ set_charinfo_bottom_left_kern(co, target);
+ /* */
+ set_numeric_field_by_index(target, leftmargin, 0);
+ set_charinfo_left_margin(co, target);
+ set_numeric_field_by_index(target, rightmargin, 0);
+ set_charinfo_right_margin(co, target);
+ set_numeric_field_by_index(target, topmargin, 0);
+ set_charinfo_top_margin(co, target);
+ set_numeric_field_by_index(target, bottommargin, 0);
+ set_charinfo_bottom_margin(co, target);
+ /* */
+ // set_numeric_field_by_index(target, options, 0);
+ // set_charinfo_options(co, target);
+ set_numeric_field_by_index(target, topaccent, INT_MIN);
+ set_charinfo_top_accent(co, target);
+ set_numeric_field_by_index(target, bottomaccent, INT_MIN);
+ set_charinfo_bottom_accent(co, target);
+ set_numeric_field_by_index(target, flataccent, INT_MIN);
+ set_charinfo_flat_accent(co, target);
+ set_numeric_field_by_index(target, next, -1);
+ if (target >= 0) {
+ set_charinfo_tag(co, list_tag);
+ set_charinfo_remainder(co, target);
+ }
+ lua_push_key(extensible);
+ switch (lua_rawget(L, -2)) {
+ case LUA_TTABLE:
+ {
+ int top, bottom, middle, extender;
+ set_numeric_field_by_index(top, top, 0);
+ set_numeric_field_by_index(bottom, bottom, 0);
+ set_numeric_field_by_index(middle, middle, 0);
+ set_numeric_field_by_index(extender, extender, 0);
+ if (top || bottom || middle || extender) {
+ set_charinfo_tag(co, extension_tag);
+ tex_set_charinfo_extensible(co, top, bottom, middle, extender);
+ } else {
+ tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid extensible field", font_name(f), (int) i);
+ }
+ }
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, -2)) {
+ set_charinfo_tag(co, extend_last_tag);
+ }
+ break;
+ }
+ lua_pop(L, 1);
+ lua_push_key(hparts);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ set_charinfo_tag(co, extension_tag);
+ tex_set_charinfo_horizontal_parts(co, NULL);
+ for (lua_Integer k = 1; ; k++) {
+ if (lua_rawgeti(L, -1, k) == LUA_TTABLE) {
+ int glyph, startconnect, endconnect, advance, extender;
+ extinfo *h;
+ set_numeric_field_by_index(glyph, glyph, 0);
+ set_numeric_field_by_index(extender, extender, 0);
+ set_numeric_field_by_index(startconnect, start, 0);
+ set_numeric_field_by_index(endconnect, end, 0);
+ set_numeric_field_by_index(advance, advance, 0);
+ h = tex_new_charinfo_part(glyph, startconnect, endconnect, advance, extender);
+ tex_add_charinfo_horizontal_part(co, h);
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ }
+ lua_pop(L, 1);
+ lua_push_key(vparts);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ set_charinfo_tag(co, extension_tag);
+ tex_set_charinfo_vertical_parts(co, NULL);
+ for (lua_Integer k = 1; ; k++) {
+ if (lua_rawgeti(L, -1, k) == LUA_TTABLE) {
+ int glyph, startconnect, endconnect, advance, extender;
+ extinfo *h;
+ set_numeric_field_by_index(glyph, glyph, 0);
+ set_numeric_field_by_index(extender, extender, 0);
+ set_numeric_field_by_index(startconnect, start, 0);
+ set_numeric_field_by_index(endconnect, end, 0);
+ set_numeric_field_by_index(advance, advance, 0);
+ h = tex_new_charinfo_part(glyph, startconnect, endconnect, advance, extender);
+ tex_add_charinfo_vertical_part(co, h);
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ }
+ lua_pop(L, 1);
+ lua_push_key(mathkerns);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ fontlib_aux_store_math_kerns(L, lua_key_index(topleft), co, top_left_kern);
+ fontlib_aux_store_math_kerns(L, lua_key_index(topright), co, top_right_kern);
+ fontlib_aux_store_math_kerns(L, lua_key_index(bottomright), co, bottom_right_kern);
+ fontlib_aux_store_math_kerns(L, lua_key_index(bottomleft), co, bottom_left_kern);
+ }
+ lua_pop(L, 1);
+ }
+ /*tex Maybe some kerns: */
+ lua_push_key(kerns);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ int count = fontlib_aux_count_hash_items(L);
+ if (count > 0) {
+ /*tex The kerns table is still on stack. */
+ kerninfo *ckerns = lmt_memory_calloc((size_t) count + 1, sizeof(kerninfo));
+ if (ckerns) {
+ int ctr = 0;
+ /*tex Traverse the hash. */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ int k = non_boundary_char;
+ switch (lua_type(L, -2)) {
+ case LUA_TNUMBER:
+ /*tex Adjacent char: */
+ k = (int) lua_tointeger(L, -2);
+ if (k < 0) {
+ k = non_boundary_char;
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, -2);
+ if (lua_key_eq(s, rightboundary)) {
+ k = right_boundary_char;
+ if (! font_has_right_boundary(f)) {
+ set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char));
+ }
+ }
+ }
+ break;
+ }
+ target = lmt_roundnumber(L, -1);
+ if (k != non_boundary_char) {
+ set_kern_item(ckerns[ctr], k, target);
+ ctr++;
+ } else {
+ tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kern field", font_name(f), (int) i);
+ }
+ lua_pop(L, 1);
+ }
+ /*tex A guard against empty tables. */
+ if (ctr > 0) {
+ set_kern_item(ckerns[ctr], end_kern, 0);
+ set_charinfo_kerns(co, ckerns);
+ } else {
+ tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kerns field", font_name(f), (int) i);
+ }
+ } else {
+ tex_overflow_error("font", (count + 1) * sizeof(kerninfo));
+ }
+ }
+ }
+ lua_pop(L, 1);
+ /*tex Sometimes ligatures: */
+ lua_push_key(ligatures);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ int count = fontlib_aux_count_hash_items(L);
+ if (count > 0) {
+ /*tex The ligatures table still on stack. */
+ ligatureinfo *cligs = lmt_memory_calloc((size_t) count + 1, sizeof(ligatureinfo));
+ if (cligs) {
+ int ctr = 0;
+ /*tex Traverse the hash. */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ int k = non_boundary_char;
+ int r = -1;
+ switch (lua_type(L, -2)) {
+ case LUA_TNUMBER:
+ /*tex Adjacent char: */
+ k = (int) lua_tointeger(L, -2);
+ if (k < 0) {
+ k = non_boundary_char;
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, -2);
+ if (lua_key_eq(s, rightboundary)) {
+ k = right_boundary_char;
+ if (! font_has_right_boundary(f)) {
+ set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char));
+ }
+ }
+ }
+ break;
+ }
+ if (lua_istable(L, -1)) {
+ /*tex Ligature: */
+ set_numeric_field_by_index(r, char, -1);
+ }
+ if (r != -1 && k != non_boundary_char) {
+ int ligtarget = 0;
+ lua_push_key(type);
+ switch (lua_rawget(L, -2)) {
+ case LUA_TNUMBER:
+ ligtarget = lmt_tointeger(L, -1);
+ break;
+ case LUA_TSTRING:
+ {
+ const char *value = lua_tostring(L, -1);
+ int index = 0;
+ while (lmt_ligature_type_strings[index]) {
+ if (strcmp(lmt_ligature_type_strings[index], value) == 0) {
+ ligtarget = index;
+ break;
+ } else {
+ index++;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ lua_pop(L, 1);
+ set_ligature_item(cligs[ctr], (ligtarget * 2) + 1, k, r);
+ ctr++;
+ } else {
+ tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligature field", font_name(f), (int) i);
+ }
+ /*tex The iterator value: */
+ lua_pop(L, 1);
+ }
+ /*tex A guard against empty tables. */
+ if (ctr > 0) {
+ set_ligature_item(cligs[ctr], 0, end_of_ligature_code, 0);
+ set_charinfo_ligatures(co, cligs);
+ } else {
+ tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligatures field", font_name(f), (int) i);
+ }
+ } else {
+ tex_overflow_error("font", (count + 1) * sizeof(ligatureinfo));
+ }
+ }
+ }
+ lua_pop(L, 1);
+ }
+}
+
+/*tex
+
+ The caller has to fix the state of the lua stack when there is an error!
+
+*/
+
+static int lmt_font_from_lua(lua_State *L, int f)
+{
+ /*tex The table is at stack |index -1| */
+ const char *nstr ;
+ set_string_field_by_index(nstr, name);
+ tex_set_font_name(f, nstr);
+ if (nstr) {
+ const char *ostr = NULL;
+ int no_math = 0;
+ int j;
+ set_string_field_by_index(ostr, original);
+ tex_set_font_original(f, ostr ? ostr : nstr);
+ set_numeric_field_by_index(j, designsize, 655360);
+ set_font_design_size(f, j);
+ set_numeric_field_by_index(j, size, font_design_size(f));
+ set_font_size(f, j);
+ set_boolean_field_by_index(j, oldmath, 0);
+ set_font_oldmath(f, j);
+ set_boolean_field_by_index(j, compactmath, 0);
+ set_font_compactmath(f, j);
+ set_numeric_field_by_index(j, mathcontrol, 0);
+ set_font_mathcontrol(f, j);
+ set_numeric_field_by_index(j, textcontrol, 0);
+ set_font_textcontrol(f, j);
+ set_numeric_field_by_index(j, textscale, 0);
+ set_font_textsize(f, j);
+ set_numeric_field_by_index(j, scriptscale, 0);
+ set_font_scriptsize(f, j);
+ set_numeric_field_by_index(j, scriptscriptscale, 0);
+ set_font_scriptscriptsize(f, j);
+ set_numeric_field_by_index(j, hyphenchar, default_hyphen_char_par);
+ set_font_hyphen_char(f, j);
+ set_numeric_field_by_index(j, skewchar, default_skew_char_par);
+ set_font_skew_char(f, j);
+ set_boolean_field_by_index(no_math, nomath, 0);
+ fontlib_aux_read_lua_parameters(L, f);
+ if (no_math) {
+ set_font_oldmath(f, 1);
+ } else {
+ fontlib_aux_read_lua_math_parameters(L, f);
+ set_boolean_field_by_index(j, oldmath, 0);
+ set_font_oldmath(f, j);
+ }
+ /*tex The characters. */
+ lua_push_key(characters);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ /*tex Find the array size values; |num| holds the number of characters to add. */
+ int num = 0;
+ int last = 0;
+ int first = -1;
+ /*tex The first key: */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_isnumber(L, -2)) {
+ int i = (int) lua_tointeger(L, -2);
+ if (i >= 0 && lua_istable(L, -1)) {
+ num++;
+ if (i > last) {
+ last = i;
+ }
+ if (first < 0) {
+ first = i;
+ }
+ if (first >= 0 && i < first) {
+ first = i;
+ }
+ }
+ }
+ lua_pop(L, 1);
+ }
+ if (num > 0) {
+ int fstep = 0;
+ tex_font_malloc_charinfo(f, num);
+ set_font_first_character(f, first);
+ set_font_last_character(f, last);
+ /*tex The first key: */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ switch (lua_type(L, -2)) {
+ case LUA_TNUMBER:
+ {
+ int i = lmt_tointeger(L, -2);
+ if (i >= 0) {
+ fontlib_aux_font_char_from_lua(L, f, i, ! no_math);
+ }
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *b = lua_tostring(L, -2);
+ if (lua_key_eq(b, leftboundary)) {
+ fontlib_aux_font_char_from_lua(L, f, left_boundary_char, ! no_math);
+ } else if (lua_key_eq(b, rightboundary)) {
+ fontlib_aux_font_char_from_lua(L, f, right_boundary_char, ! no_math);
+ }
+ }
+ break;
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ /*tex
+
+ Handle font expansion last: We permits virtual fonts to use expansion as one
+ can always turn it off.
+
+ */
+ set_numeric_field_by_index(fstep, step, 0);
+ if (fstep > 0) {
+ int fstretch = 0;
+ int fshrink = 0;
+ if (fstep > 100) {
+ fstep = 100;
+ }
+ set_numeric_field_by_index(fshrink, shrink, 0);
+ set_numeric_field_by_index(fstretch, stretch, 0);
+ if (fshrink < 0) {
+ fshrink = 0;
+ } else if (fshrink > 500) {
+ fshrink = 500;
+ }
+ fshrink -= (fshrink % fstep);
+ if (fshrink < 0) {
+ fshrink = 0;
+ }
+ if (fstretch < 0) {
+ fstretch = 0;
+ } else if (fstretch > 1000) {
+ fstretch = 1000;
+ }
+ fstretch -= (fstretch % fstep);
+ if (fstretch < 0) {
+ fstretch = 0;
+ }
+ set_font_step(f, fstep);
+ set_font_max_stretch(f, fstretch);
+ set_font_max_shrink(f, fshrink);
+ }
+ } else {
+ tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no characters", f, font_name(f));
+ }
+ } else {
+ tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no character table", f, font_name(f));
+ }
+ return 1;
+ } else {
+ return tex_formatted_error("font", "lua-loaded font '%d' has no name!", f);
+ }
+}
+
+static int lmt_characters_from_lua(lua_State *L, int f)
+{
+ int no_math;
+ /*tex Speedup: */
+ set_boolean_field_by_index(no_math, nomath, 0);
+ /*tex The characters: */
+ lua_push_key(characters);
+ if (lua_rawget(L, -2) == LUA_TTABLE) {
+ /*tex Find the array size values; |num| has the amount. */
+ int num = 0;
+ int todo = 0;
+ int bc = font_first_character(f);
+ int ec = font_last_character(f);
+ /*tex First key: */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_isnumber(L, -2)) {
+ int i = lmt_tointeger(L, -2);
+ if (i >= 0 && lua_istable(L, -1)) {
+ todo++;
+ if (! quick_char_exists(f, i)) {
+ num++;
+ if (i > ec) {
+ ec = i;
+ }
+ if (bc < 0) {
+ bc = i;
+ }
+ if (bc >= 0 && i < bc) {
+ bc = i;
+ }
+ }
+ }
+ }
+ lua_pop(L, 1);
+ }
+ if (todo > 0) {
+ tex_font_malloc_charinfo(f, num);
+ set_font_first_character(f, bc);
+ set_font_last_character(f, ec);
+ /*tex First key: */
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ int i = lmt_tointeger(L, -2);
+ if (i >= 0) {
+ if (quick_char_exists(f, i)) {
+ charinfo *co = tex_get_charinfo(f, i);
+ set_charinfo_ligatures(co, NULL);
+ set_charinfo_kerns(co, NULL);
+ set_charinfo_math(co, NULL);
+ tex_set_charinfo_vertical_parts(co, NULL);
+ tex_set_charinfo_horizontal_parts(co, NULL);
+ }
+ fontlib_aux_font_char_from_lua(L, f, i, ! no_math);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ }
+ }
+ return 1;
+}
+
+/*tex
+
+ The font library has helpers for defining the font and setting or getting the current font.
+ Internally fonts are represented by font identifiers: numbers. The zero value represents the
+ predefined |nullfont| instance. The only way to load a font in \LUAMETATEX\ is to use \LUA.
+
+*/
+
+static int fontlib_current(lua_State *L)
+{
+ int i = lmt_optinteger(L, 1, 0);
+ if (i > 0) {
+ if (tex_is_valid_font(i)) {
+ tex_set_cur_font(0, i);
+ } else {
+ luaL_error(L, "expected a valid font id");
+ }
+ }
+ lua_pushinteger(L, cur_font_par);
+ return 1;
+}
+
+static int fontlib_max(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_font_max_id());
+ return 1;
+}
+
+static int fontlib_setfont(lua_State *L)
+{
+ int i = lmt_checkinteger(L, 1);
+ if (i) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ if (! tex_is_valid_font(i)) {
+ return luaL_error(L, "font with id %d is not a valid font", i);
+ // } else if (font_touched(i)) {
+ // return luaL_error(L, "font with id %d has been accessed already, changing it is forbidden", i);
+ } else {
+ lua_settop(L, 2);
+ lmt_font_from_lua(L, i);
+ }
+ }
+ return 0;
+}
+
+static int fontlib_addcharacters(lua_State *L)
+{
+ int i = lmt_checkinteger(L, 1);
+ if (i) {
+ luaL_checktype(L, 2, LUA_TTABLE);
+ if (tex_is_valid_font(i)) {
+ lua_settop(L, 2);
+ lmt_characters_from_lua(L, i);
+ } else {
+ return luaL_error(L, "invalid font id %d passed", i);
+ }
+ }
+ return 0;
+}
+
+/*tex |font.define(table)| */
+
+static int fontlib_define(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ int i = lmt_optinteger(L, 2, 0);
+ if (! i) {
+ i = tex_new_font();
+ } else if (! tex_is_valid_font(i)) {
+ return luaL_error(L, "invalid font id %d passed", i);
+ }
+ lua_settop(L, 1);
+ if (lmt_font_from_lua(L, i)) {
+ lua_pushinteger(L, i);
+ return 1;
+ } else {
+ lua_pop(L, 1);
+ tex_delete_font(i);
+ return luaL_error(L, "font creation failed, error in table");
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int fontlib_id(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ int cs = tex_string_locate(s, l, 0);
+ int f = -1;
+ if (cs == undefined_control_sequence || cs == undefined_cs_cmd || eq_type(cs) != set_font_cmd) {
+ lua_pushliteral(L, "not a valid font csname");
+ } else {
+ f = eq_value(cs);
+ }
+ lua_pushinteger(L, f);
+ return 1;
+ } else {
+ return luaL_error(L, "expected font csname string as argument");
+ }
+}
+
+/*tex
+
+ This returns the expected (!) next |fontid|, a first arg |true| will keep the id. This not
+ really robust as of course fonts can be defined in the meantime! In principle |define| could
+ handle that but then I also need to add similar functionality to \LUATEX.
+
+*/
+
+static int fontlib_nextid(lua_State *L)
+{
+ int keep = lua_toboolean(L, 1);
+ int id = tex_new_font();
+ lua_pushinteger(L, id);
+ if (! keep) {
+ tex_delete_font(id);
+ }
+ return 1;
+}
+
+/*tex
+
+ These are not really that useful but can be used to (for instance) mess with the nullfont
+ parameters that occasionally are used as defaults. We don't increase the font parameter array
+ when the upper bound is larger than the initial size. You can forget about that kind of abuse
+ in \LUAMETATEX.
+
+*/
+
+static int fontlib_aux_valid_fontdimen(lua_State *L, halfword *fnt, halfword *n)
+{
+ *fnt = lmt_tohalfword(L, 1);
+ *n = lmt_tohalfword(L, 2);
+ if (*n > 0 && *n <= font_parameter_count(*fnt)) {
+ return 1;
+ } else {
+ return luaL_error(L, "font with id %i has only %d fontdimens", fnt, n);
+ }
+}
+
+static int fontlib_setfontdimen(lua_State *L)
+{
+ halfword fnt, n;
+ if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) {
+ tex_set_font_parameter(fnt, n, lmt_tohalfword(L, 3));
+ }
+ return 0;
+}
+
+static int fontlib_getfontdimen(lua_State *L)
+{
+ halfword fnt, n;
+ if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) {
+ lua_pushinteger(L, font_parameter(fnt, n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int fontlib_getmathspec(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);
+ if (eq_type(cs) == mathspec_cmd) {
+ halfword ms = eq_value(cs);
+ if (ms) {
+ mathcodeval m = tex_get_math_spec(ms);
+ lua_pushinteger(L, m.class_value);
+ lua_pushinteger(L, m.family_value);
+ lua_pushinteger(L, m.character_value);
+ return 3;
+ }
+ }
+ }
+ return 0;
+}
+
+static int fontlib_getfontspec(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);
+ if (eq_type(cs) == fontspec_cmd) {
+ halfword fs = eq_value(cs);
+ if (fs) {
+ lua_pushinteger(L, font_spec_identifier(fs));
+ lua_pushinteger(L, font_spec_scale(fs));
+ lua_pushinteger(L, font_spec_x_scale(fs));
+ lua_pushinteger(L, font_spec_y_scale(fs));
+ return 4;
+ }
+ }
+ }
+ return 0;
+}
+
+static int fontlib_getmathindex(lua_State *L) {
+ halfword index = -1;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ index = valid_math_parameter(L, 1);
+ break;
+ case LUA_TNUMBER:
+ index = lmt_tointeger(L, 1);
+ break;
+ }
+ if (index > 0 && index < math_parameter_last_code) {
+ lua_pushinteger(L, index);
+ lua_pushboolean(L, index >= math_parameter_first_engine_code); /* true == engine */
+ } else {
+ lua_pushinteger(L, 0);
+ lua_pushboolean(L, 0);
+ }
+ return 2;
+}
+
+static const struct luaL_Reg fontlib_function_list[] = {
+ { "current", fontlib_current },
+ { "max", fontlib_max },
+ { "setfont", fontlib_setfont },
+ { "addcharacters", fontlib_addcharacters },
+ { "define", fontlib_define },
+ { "nextid", fontlib_nextid },
+ { "id", fontlib_id },
+ { "getfontdimen", fontlib_getfontdimen },
+ { "setfontdimen", fontlib_setfontdimen },
+ { "getfontspec", fontlib_getfontspec },
+ { "getmathspec", fontlib_getmathspec },
+ { "getmathindex", fontlib_getmathindex },
+ { NULL, NULL },
+};
+
+int luaopen_font(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, fontlib_function_list, 0);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtfontlib.h b/source/luametatex/source/lua/lmtfontlib.h
new file mode 100644
index 000000000..e76fe9197
--- /dev/null
+++ b/source/luametatex/source/lua/lmtfontlib.h
@@ -0,0 +1,10 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LUAFONTLIB_H
+# define LUAFONTLIB_H
+
+extern void lmt_fontlib_initialize (void);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtinterface.c b/source/luametatex/source/lua/lmtinterface.c
new file mode 100644
index 000000000..1aef54563
--- /dev/null
+++ b/source/luametatex/source/lua/lmtinterface.c
@@ -0,0 +1,544 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ There isn't much here because most happens in the header file. Here we also set up the
+ environment in which we run, which depends in the operating system used.
+
+*/
+
+# include "luametatex.h"
+
+lua_state_info lmt_lua_state = {
+ .lua_instance = NULL,
+ .used_bytes = 0,
+ .used_bytes_max = 0,
+ .function_table_id = 0,
+ .function_callback_count = 0,
+ .value_callback_count = 0,
+ .bytecode_callback_count = 0,
+ .local_callback_count = 0,
+ .saved_callback_count = 0,
+ .file_callback_count = 0,
+ .direct_callback_count = 0,
+ .message_callback_count = 0,
+ .function_table_size = 0,
+ .bytecode_bytes = 0,
+ .bytecode_max = 0,
+ .version_number = (int) LUA_VERSION_NUM,
+ .release_number = (int) LUA_VERSION_RELEASE_NUM,
+ .used_buffer = NULL,
+ .integer_size = sizeof(lua_Integer),
+};
+
+/*tex
+ Some more can move here, or we can move some to modules instead. It's a very stepwise
+ process because things need to keep running.
+*/
+
+lmt_keys_info lmt_keys;
+
+lmt_interface_info lmt_interface = {
+ .pack_type_values = NULL,
+ .group_code_values = NULL,
+ .par_context_values = NULL,
+ .par_begin_values = NULL,
+ .par_mode_values = NULL,
+ .math_style_name_values = NULL,
+ .math_style_variant_values = NULL,
+ .lua_function_values = NULL,
+ .direction_values = NULL,
+ .node_fill_values = NULL,
+ .page_contribute_values = NULL,
+ .math_style_values = NULL,
+ .math_parameter_values = NULL,
+ .field_type_values = NULL,
+ .node_data = NULL,
+ .command_names = NULL,
+} ;
+
+value_info *lmt_aux_allocate_value_info(size_t last)
+{
+ value_info *v = lmt_memory_calloc(last + 2, sizeof(value_info));
+ set_value_entry_nop(v, last + 1);
+ return v;
+}
+
+void lmt_initialize_interface(void)
+{
+ lmt_interface.pack_type_values = lmt_aux_allocate_value_info(packing_adapted);
+
+ # define set_pack_type_value(n,k) lmt_interface.pack_type_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_pack_type_value(packing_exactly, exactly);
+ set_pack_type_value(packing_additional, additional);
+ set_pack_type_value(packing_expanded, expanded);
+ set_pack_type_value(packing_substitute, substitute);
+ set_pack_type_value(packing_adapted, adapted);
+
+ lmt_interface.group_code_values = lmt_aux_allocate_value_info(lua_group);
+
+ # define set_group_code_value(n,k) lmt_interface.group_code_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_group_code_value(bottom_level_group, bottomlevel);
+ set_group_code_value(simple_group, simple);
+ set_group_code_value(hbox_group, hbox);
+ set_group_code_value(adjusted_hbox_group, adjustedhbox);
+ set_group_code_value(vbox_group, vbox);
+ set_group_code_value(vtop_group, vtop);
+ set_group_code_value(align_group, align);
+ set_group_code_value(no_align_group, noalign);
+ set_group_code_value(output_group, output);
+ set_group_code_value(math_group, math);
+ set_group_code_value(discretionary_group, discretionary);
+ set_group_code_value(insert_group, insert);
+ set_group_code_value(vadjust_group, vadjust);
+ set_group_code_value(vcenter_group, vcenter);
+ set_group_code_value(math_fraction_group, mathfraction);
+ set_group_code_value(math_operator_group, mathoperator);
+ set_group_code_value(math_choice_group, mathchoice);
+ set_group_code_value(also_simple_group, alsosimple);
+ set_group_code_value(semi_simple_group, semisimple);
+ set_group_code_value(math_simple_group, mathsimple);
+ set_group_code_value(math_shift_group, mathshift);
+ set_group_code_value(math_fence_group, mathfence);
+ set_group_code_value(local_box_group, localbox);
+ set_group_code_value(split_off_group, splitoff);
+ set_group_code_value(split_keep_group, splitkeep);
+ set_group_code_value(preamble_group, preamble);
+ set_group_code_value(align_set_group, alignset);
+ set_group_code_value(finish_row_group, finishrow);
+ set_group_code_value(lua_group, lua);
+
+ lmt_interface.par_context_values = lmt_aux_allocate_value_info(reset_par_context);
+
+ # define set_par_context_value(n,k) lmt_interface.par_context_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_par_context_value(normal_par_context, normal);
+ set_par_context_value(vmode_par_context, vmode);
+ set_par_context_value(vbox_par_context, vbox);
+ set_par_context_value(vtop_par_context, vtop);
+ set_par_context_value(vcenter_par_context, vcenter);
+ set_par_context_value(vadjust_par_context, vadjust);
+ set_par_context_value(insert_par_context, insert);
+ set_par_context_value(output_par_context, output);
+ set_par_context_value(align_par_context, align);
+ set_par_context_value(no_align_par_context, noalign);
+ set_par_context_value(span_par_context, span);
+ set_par_context_value(reset_par_context, reset);
+
+ lmt_interface.page_context_values = lmt_aux_allocate_value_info(alignment_page_context);
+
+ # define set_page_context_value(n,k) lmt_interface.page_context_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_page_context_value(box_page_context, box);
+ set_page_context_value(end_page_context, end);
+ set_page_context_value(vadjust_page_context, vadjust);
+ set_page_context_value(penalty_page_context, penalty);
+ set_page_context_value(boundary_page_context, boundary);
+ set_page_context_value(insert_page_context, insert);
+ set_page_context_value(hmode_par_page_context, hmodepar);
+ set_page_context_value(vmode_par_page_context, vmodepar);
+ set_page_context_value(begin_paragraph_page_context, beginparagraph);
+ set_page_context_value(before_display_page_context, beforedisplay);
+ set_page_context_value(after_display_page_context, afterdisplay);
+ set_page_context_value(after_output_page_context, afteroutput);
+ set_page_context_value(alignment_page_context, alignment);
+
+ lmt_interface.append_line_context_values = lmt_aux_allocate_value_info(post_migrate_append_line_context);
+
+ # define set_append_line_context_value(n,k) lmt_interface.append_line_context_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_append_line_context_value(box_append_line_context, box);
+ set_append_line_context_value(pre_box_append_line_context, prebox);
+ set_append_line_context_value(pre_adjust_append_line_context, preadjust);
+ set_append_line_context_value(post_adjust_append_line_context, postadjust);
+ set_append_line_context_value(pre_migrate_append_line_context, premigrate);
+ set_append_line_context_value(post_migrate_append_line_context, postmigrate);
+
+ lmt_interface.alignment_context_values = lmt_aux_allocate_value_info(wrapup_pass_alignment_context);
+
+ # define set_alignment_context_value(n,k) lmt_interface.alignment_context_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_alignment_context_value(preamble_pass_alignment_context, preamble);
+ set_alignment_context_value(preroll_pass_alignment_context, preroll);
+ set_alignment_context_value(package_pass_alignment_context, package);
+ set_alignment_context_value(wrapup_pass_alignment_context, wrapup);
+
+ lmt_interface.par_begin_values = lmt_aux_allocate_value_info(vrule_char_par_begin);
+
+ # define set_par_begin_value(n,k) lmt_interface.par_begin_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_par_begin_value(normal_par_begin, normal);
+ set_par_begin_value(force_par_begin, force);
+ set_par_begin_value(indent_par_begin, indent);
+ set_par_begin_value(no_indent_par_begin, noindent);
+ set_par_begin_value(math_char_par_begin, mathchar);
+ set_par_begin_value(char_par_begin, char);
+ set_par_begin_value(boundary_par_begin, boundary);
+ set_par_begin_value(space_par_begin, space);
+ set_par_begin_value(math_par_begin, math);
+ set_par_begin_value(kern_par_begin, kern);
+ set_par_begin_value(hskip_par_begin, hskip);
+ set_par_begin_value(un_hbox_char_par_begin, unhbox);
+ set_par_begin_value(valign_char_par_begin, valign);
+ set_par_begin_value(vrule_char_par_begin, vrule);
+
+ lmt_interface.par_mode_values = lmt_aux_allocate_value_info(math_par_subtype);
+
+ # define set_par_mode_value(n,k) lmt_interface.par_mode_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_par_mode_value(vmode_par_par_subtype, vmodepar);
+ set_par_mode_value(local_box_par_subtype, localbox);
+ set_par_mode_value(hmode_par_par_subtype, hmodepar);
+ set_par_mode_value(penalty_par_subtype, penalty);
+ set_par_mode_value(math_par_subtype, math);
+
+ lmt_interface.math_style_name_values = lmt_aux_allocate_value_info(cramped_script_script_style);
+
+ # define set_math_style_name_value(n,k) lmt_interface.math_style_name_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_math_style_name_value(display_style, display);
+ set_math_style_name_value(cramped_display_style, crampeddisplay);
+ set_math_style_name_value(text_style, text);
+ set_math_style_name_value(cramped_text_style, crampedtext);
+ set_math_style_name_value(script_style, script);
+ set_math_style_name_value(cramped_script_style, crampedscript);
+ set_math_style_name_value(script_script_style, scriptscript);
+ set_math_style_name_value(cramped_script_script_style, crampedscriptscript);
+
+ lmt_interface.math_style_variant_values = lmt_aux_allocate_value_info(math_double_superscript_variant);
+
+ # define set_math_style_variant_value(n,k) lmt_interface.math_style_variant_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_math_style_variant_value(math_normal_style_variant, normal);
+ set_math_style_variant_value(math_cramped_style_variant, cramped);
+ set_math_style_variant_value(math_subscript_style_variant, subscript);
+ set_math_style_variant_value(math_superscript_style_variant, superscript);
+ set_math_style_variant_value(math_small_style_variant, small);
+ set_math_style_variant_value(math_smaller_style_variant, smaller);
+ set_math_style_variant_value(math_numerator_style_variant, numerator);
+ set_math_style_variant_value(math_denominator_style_variant, denominator);
+ set_math_style_variant_value(math_double_superscript_variant, doublesuperscript);
+
+ lmt_interface.lua_function_values = lmt_aux_allocate_value_info(lua_value_direct_code);
+
+ # define set_lua_function_value(n,k) lmt_interface.lua_function_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_lua_function_value(lua_value_none_code, none);
+ set_lua_function_value(lua_value_integer_code, integer);
+ set_lua_function_value(lua_value_cardinal_code, cardinal);
+ set_lua_function_value(lua_value_dimension_code, dimension);
+ set_lua_function_value(lua_value_skip_code, skip);
+ set_lua_function_value(lua_value_boolean_code, boolean);
+ set_lua_function_value(lua_value_float_code, float);
+ set_lua_function_value(lua_value_string_code, string);
+ set_lua_function_value(lua_value_node_code, node);
+ set_lua_function_value(lua_value_direct_code, direct);
+
+ lmt_interface.direction_values = lmt_aux_allocate_value_info(dir_righttoleft);
+
+ # define set_direction_value(n,k) lmt_interface.direction_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_direction_value(dir_lefttoright, lefttoright);
+ set_direction_value(dir_righttoleft, righttoleft);
+
+ lmt_interface.field_type_values = lmt_aux_allocate_value_info(attribute_field);
+
+ # define set_field_type_value(n,k) lmt_interface.field_type_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_field_type_value(nil_field, nil);
+ set_field_type_value(integer_field, integer);
+ set_field_type_value(dimension_field, dimension);
+ set_field_type_value(glue_field, glue);
+ set_field_type_value(number_field, number);
+ set_field_type_value(string_field, string);
+ set_field_type_value(boolean_field, boolean);
+ set_field_type_value(function_field, function);
+ set_field_type_value(node_field, node);
+ set_field_type_value(node_list_field, nodelist);
+ set_field_type_value(token_field, token);
+ set_field_type_value(token_list_field, tokenlist);
+ set_field_type_value(attribute_field, attribute);
+
+ lmt_interface.node_fill_values = lmt_aux_allocate_value_info(filll_glue_order);
+
+ # define set_node_fill_value(n,k) lmt_interface.node_fill_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_node_fill_value(normal_glue_order, normal);
+ set_node_fill_value(fi_glue_order, fi);
+ set_node_fill_value(fil_glue_order, fil);
+ set_node_fill_value(fill_glue_order, fill);
+ set_node_fill_value(filll_glue_order, filll);
+
+ lmt_interface.page_contribute_values = lmt_aux_allocate_value_info(contribute_rule);
+
+ # define set_page_contribute_value(n,k) lmt_interface.page_contribute_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_page_contribute_value(contribute_nothing, empty);
+ set_page_contribute_value(contribute_insert, insert);
+ set_page_contribute_value(contribute_box, box);
+ set_page_contribute_value(contribute_rule, rule);
+
+ lmt_interface.math_style_values = lmt_aux_allocate_value_info(cramped_script_script_style);
+
+ # define set_math_style_value(n,k) lmt_interface.math_style_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_math_style_value(display_style, display);
+ set_math_style_value(cramped_display_style, crampeddisplay);
+ set_math_style_value(text_style, text);
+ set_math_style_value(cramped_text_style, crampedtext);
+ set_math_style_value(script_style, script);
+ set_math_style_value(cramped_script_style, crampedscript);
+ set_math_style_value(script_script_style, scriptscript);
+ set_math_style_value(cramped_script_script_style, crampedscriptscript);
+
+ lmt_interface.math_indirect_values = lmt_aux_allocate_value_info(last_math_indirect);
+
+ # define set_math_indirect_value(n,k) lmt_interface.math_indirect_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .id = n }
+
+ set_math_indirect_value(indirect_math_unset, unset);
+ set_math_indirect_value(indirect_math_regular, regular);
+ set_math_indirect_value(indirect_math_integer, integer);
+ set_math_indirect_value(indirect_math_dimension, dimension);
+ set_math_indirect_value(indirect_math_gluespec, gluespec);
+ set_math_indirect_value(indirect_math_mugluespec, mugluespec);
+ set_math_indirect_value(indirect_math_register_integer, registerinteger);
+ set_math_indirect_value(indirect_math_register_dimension, registerdimension);
+ set_math_indirect_value(indirect_math_register_gluespec, registergluespec);
+ set_math_indirect_value(indirect_math_register_mugluespec, registermugluespec);
+ set_math_indirect_value(indirect_math_internal_integer, internalinteger);
+ set_math_indirect_value(indirect_math_internal_dimension, internaldimension);
+ set_math_indirect_value(indirect_math_internal_dimension, internalgluespec);
+ set_math_indirect_value(indirect_math_internal_mugluespec, internalmugluespec);
+
+ lmt_interface.math_parameter_values = lmt_aux_allocate_value_info(last_math_parameter);
+
+ # define set_math_parameter_value(n,t,k) lmt_interface.math_parameter_values[n] = (value_info) { .lua = lua_key_index(k), .name = lua_key(k), .type = t }
+
+ set_math_parameter_value(math_parameter_quad, math_dimen_parameter, quad);
+ set_math_parameter_value(math_parameter_axis, math_dimen_parameter, axis);
+ set_math_parameter_value(math_parameter_accent_base_height, math_dimen_parameter, accentbaseheight);
+ set_math_parameter_value(math_parameter_accent_base_depth, math_dimen_parameter, accentbasedepth);
+ set_math_parameter_value(math_parameter_flattened_accent_base_height, math_dimen_parameter, flattenedaccentbaseheight);
+ set_math_parameter_value(math_parameter_flattened_accent_base_depth, math_dimen_parameter, flattenedaccentbasedepth);
+ set_math_parameter_value(math_parameter_x_scale, math_int_parameter, xscale);
+ set_math_parameter_value(math_parameter_y_scale, math_int_parameter, yscale);
+ set_math_parameter_value(math_parameter_operator_size, math_dimen_parameter, operatorsize);
+ set_math_parameter_value(math_parameter_overbar_kern, math_dimen_parameter, overbarkern);
+ set_math_parameter_value(math_parameter_overbar_rule, math_dimen_parameter, overbarrule);
+ set_math_parameter_value(math_parameter_overbar_vgap, math_dimen_parameter, overbarvgap);
+ set_math_parameter_value(math_parameter_underbar_kern, math_dimen_parameter, underbarkern);
+ set_math_parameter_value(math_parameter_underbar_rule, math_dimen_parameter, underbarrule);
+ set_math_parameter_value(math_parameter_underbar_vgap, math_dimen_parameter, underbarvgap);
+ set_math_parameter_value(math_parameter_radical_kern, math_dimen_parameter, radicalkern);
+ set_math_parameter_value(math_parameter_radical_rule, math_dimen_parameter, radicalrule);
+ set_math_parameter_value(math_parameter_radical_vgap, math_dimen_parameter, radicalvgap);
+ set_math_parameter_value(math_parameter_radical_degree_before, math_dimen_parameter, radicaldegreebefore);
+ set_math_parameter_value(math_parameter_radical_degree_after, math_dimen_parameter, radicaldegreeafter);
+ set_math_parameter_value(math_parameter_radical_degree_raise, math_int_parameter, radicaldegreeraise);
+ set_math_parameter_value(math_parameter_radical_extensible_after, math_dimen_parameter, radicalextensibleafter);
+ set_math_parameter_value(math_parameter_radical_extensible_before, math_dimen_parameter, radicalextensiblebefore);
+ set_math_parameter_value(math_parameter_stack_vgap, math_dimen_parameter, stackvgap);
+ set_math_parameter_value(math_parameter_stack_num_up, math_dimen_parameter, stacknumup);
+ set_math_parameter_value(math_parameter_stack_denom_down, math_dimen_parameter, stackdenomdown);
+ set_math_parameter_value(math_parameter_fraction_rule, math_dimen_parameter, fractionrule);
+ set_math_parameter_value(math_parameter_fraction_num_vgap, math_dimen_parameter, fractionnumvgap);
+ set_math_parameter_value(math_parameter_fraction_num_up, math_dimen_parameter, fractionnumup);
+ set_math_parameter_value(math_parameter_fraction_denom_vgap, math_dimen_parameter, fractiondenomvgap);
+ set_math_parameter_value(math_parameter_fraction_denom_down, math_dimen_parameter, fractiondenomdown);
+ set_math_parameter_value(math_parameter_fraction_del_size, math_dimen_parameter, fractiondelsize);
+ set_math_parameter_value(math_parameter_skewed_fraction_hgap, math_dimen_parameter, skewedfractionhgap);
+ set_math_parameter_value(math_parameter_skewed_fraction_vgap, math_dimen_parameter, skewedfractionvgap);
+ set_math_parameter_value(math_parameter_limit_above_vgap, math_dimen_parameter, limitabovevgap);
+ set_math_parameter_value(math_parameter_limit_above_bgap, math_dimen_parameter, limitabovebgap);
+ set_math_parameter_value(math_parameter_limit_above_kern, math_dimen_parameter, limitabovekern);
+ set_math_parameter_value(math_parameter_limit_below_vgap, math_dimen_parameter, limitbelowvgap);
+ set_math_parameter_value(math_parameter_limit_below_bgap, math_dimen_parameter, limitbelowbgap);
+ set_math_parameter_value(math_parameter_limit_below_kern, math_dimen_parameter, limitbelowkern);
+ set_math_parameter_value(math_parameter_nolimit_sup_factor, math_dimen_parameter, nolimitsupfactor);
+ set_math_parameter_value(math_parameter_nolimit_sub_factor, math_dimen_parameter, nolimitsubfactor);
+ set_math_parameter_value(math_parameter_under_delimiter_vgap, math_dimen_parameter, underdelimitervgap);
+ set_math_parameter_value(math_parameter_under_delimiter_bgap, math_dimen_parameter, underdelimiterbgap);
+ set_math_parameter_value(math_parameter_over_delimiter_vgap, math_dimen_parameter, overdelimitervgap);
+ set_math_parameter_value(math_parameter_over_delimiter_bgap, math_dimen_parameter, overdelimiterbgap);
+ set_math_parameter_value(math_parameter_subscript_shift_drop, math_dimen_parameter, subshiftdrop);
+ set_math_parameter_value(math_parameter_superscript_shift_drop, math_dimen_parameter, supshiftdrop);
+ set_math_parameter_value(math_parameter_subscript_shift_down, math_dimen_parameter, subshiftdown);
+ set_math_parameter_value(math_parameter_subscript_superscript_shift_down, math_dimen_parameter, subsupshiftdown);
+ set_math_parameter_value(math_parameter_subscript_top_max, math_dimen_parameter, subtopmax);
+ set_math_parameter_value(math_parameter_superscript_shift_up, math_dimen_parameter, supshiftup);
+ set_math_parameter_value(math_parameter_superscript_bottom_min, math_dimen_parameter, supbottommin);
+ set_math_parameter_value(math_parameter_superscript_subscript_bottom_max, math_dimen_parameter, supsubbottommax);
+ set_math_parameter_value(math_parameter_subscript_superscript_vgap, math_dimen_parameter, subsupvgap);
+ set_math_parameter_value(math_parameter_space_before_script, math_dimen_parameter, spacebeforescript);
+ set_math_parameter_value(math_parameter_space_after_script, math_dimen_parameter, spaceafterscript);
+ set_math_parameter_value(math_parameter_connector_overlap_min, math_dimen_parameter, connectoroverlapmin);
+
+ /*tex
+
+ Gone are the many:
+
+ \starttyping
+ set_math_parameter_value(math_parameter_ordinary_ordinary_spacing, math_muglue_parameter, ordordspacing);
+ \stoptyping
+
+ thanks to the more generic multiple class mechanism.
+
+ */
+
+ set_math_parameter_value(math_parameter_extra_superscript_shift, math_dimen_parameter, extrasuperscriptshift);
+ set_math_parameter_value(math_parameter_extra_subscript_shift, math_dimen_parameter, extrasubscriptshift);
+ set_math_parameter_value(math_parameter_extra_superprescript_shift, math_dimen_parameter, extrasuperprescriptshift);
+ set_math_parameter_value(math_parameter_extra_subprescript_shift, math_dimen_parameter, extrasubprescriptshift);
+
+ set_math_parameter_value(math_parameter_prime_raise, math_int_parameter, primeraise);
+ set_math_parameter_value(math_parameter_prime_raise_composed, math_int_parameter, primeraisecomposed);
+ set_math_parameter_value(math_parameter_prime_shift_up, math_dimen_parameter, primeshiftup);
+ set_math_parameter_value(math_parameter_prime_shift_drop, math_dimen_parameter, primeshiftdrop);
+ set_math_parameter_value(math_parameter_prime_space_after, math_dimen_parameter, primespaceafter);
+ set_math_parameter_value(math_parameter_prime_width, math_int_parameter, primewidth);
+
+ set_math_parameter_value(math_parameter_rule_height, math_dimen_parameter, ruleheight);
+ set_math_parameter_value(math_parameter_rule_depth, math_dimen_parameter, ruledepth);
+
+ set_math_parameter_value(math_parameter_superscript_shift_distance, math_dimen_parameter, superscriptshiftdistance);
+ set_math_parameter_value(math_parameter_subscript_shift_distance, math_dimen_parameter, subscriptshiftdistance);
+ set_math_parameter_value(math_parameter_superprescript_shift_distance, math_dimen_parameter, presuperscriptshiftdistance);
+ set_math_parameter_value(math_parameter_subprescript_shift_distance, math_dimen_parameter, presubscriptshiftdistance);
+
+ set_math_parameter_value(math_parameter_extra_superscript_space, math_dimen_parameter, extrasuperscriptspace);
+ set_math_parameter_value(math_parameter_extra_subscript_space, math_dimen_parameter, extrasubscriptspace);
+ set_math_parameter_value(math_parameter_extra_superprescript_space, math_dimen_parameter, extrasuperprescriptspace);
+ set_math_parameter_value(math_parameter_extra_subprescript_space, math_dimen_parameter, extrasubprescriptspace);
+
+ set_math_parameter_value(math_parameter_skewed_delimiter_tolerance, math_dimen_parameter, skeweddelimitertolerance);
+
+ set_math_parameter_value(math_parameter_accent_top_shift_up, math_dimen_parameter, accenttopshiftup);
+ set_math_parameter_value(math_parameter_accent_bottom_shift_down, math_dimen_parameter, accentbottomshiftdown);
+ set_math_parameter_value(math_parameter_accent_top_overshoot, math_int_parameter, accenttopovershoot);
+ set_math_parameter_value(math_parameter_accent_bottom_overshoot, math_int_parameter, accentbottomovershoot);
+ set_math_parameter_value(math_parameter_accent_superscript_drop, math_dimen_parameter, accentsuperscriptdrop);
+ set_math_parameter_value(math_parameter_accent_superscript_percent, math_int_parameter, accentsuperscriptpercent);
+ set_math_parameter_value(math_parameter_accent_extend_margin, math_int_parameter, accentextendmargin);
+ set_math_parameter_value(math_parameter_flattened_accent_top_shift_up, math_dimen_parameter, flattenedaccenttopshiftup);
+ set_math_parameter_value(math_parameter_flattened_accent_bottom_shift_down, math_dimen_parameter, flattenedaccentbottomshiftdown);
+
+ set_math_parameter_value(math_parameter_delimiter_percent, math_int_parameter, delimiterpercent);
+ set_math_parameter_value(math_parameter_delimiter_shortfall, math_dimen_parameter, delimitershortfall);
+
+ set_math_parameter_value(math_parameter_over_line_variant, math_style_parameter, overlinevariant);
+ set_math_parameter_value(math_parameter_under_line_variant, math_style_parameter, underlinevariant);
+ set_math_parameter_value(math_parameter_over_delimiter_variant, math_style_parameter, overdelimitervariant);
+ set_math_parameter_value(math_parameter_under_delimiter_variant, math_style_parameter, underdelimitervariant);
+ set_math_parameter_value(math_parameter_delimiter_over_variant, math_style_parameter, delimiterovervariant);
+ set_math_parameter_value(math_parameter_delimiter_under_variant, math_style_parameter, delimiterundervariant);
+ set_math_parameter_value(math_parameter_h_extensible_variant, math_style_parameter, hextensiblevariant);
+ set_math_parameter_value(math_parameter_v_extensible_variant, math_style_parameter, vextensiblevariant);
+ set_math_parameter_value(math_parameter_fraction_variant, math_style_parameter, fractionvariant);
+ set_math_parameter_value(math_parameter_radical_variant, math_style_parameter, radicalvariant);
+ set_math_parameter_value(math_parameter_degree_variant, math_style_parameter, degreevariant);
+ set_math_parameter_value(math_parameter_accent_variant, math_style_parameter, accentvariant);
+ set_math_parameter_value(math_parameter_top_accent_variant, math_style_parameter, topaccentvariant);
+ set_math_parameter_value(math_parameter_bottom_accent_variant, math_style_parameter, bottomaccentvariant);
+ set_math_parameter_value(math_parameter_overlay_accent_variant, math_style_parameter, overlayaccentvariant);
+ set_math_parameter_value(math_parameter_numerator_variant, math_style_parameter, numeratorvariant);
+ set_math_parameter_value(math_parameter_denominator_variant, math_style_parameter, denominatorvariant);
+ set_math_parameter_value(math_parameter_superscript_variant, math_style_parameter, superscriptvariant);
+ set_math_parameter_value(math_parameter_subscript_variant, math_style_parameter, subscriptvariant);
+ set_math_parameter_value(math_parameter_prime_variant, math_style_parameter, primevariant);
+ set_math_parameter_value(math_parameter_stack_variant, math_style_parameter, stackvariant);
+
+ lmt_interface.math_font_parameter_values = lmt_aux_allocate_value_info(math_parameter_last_code + 1);
+
+ # define set_math_font_parameter(n, t) lmt_interface.math_font_parameter_values[n] = (value_info) { .lua = lua_key_index(n), .name = lua_key(n), .type = t }
+
+ set_math_font_parameter(ScriptPercentScaleDown, math_int_parameter);
+ set_math_font_parameter(ScriptScriptPercentScaleDown, math_int_parameter);
+ set_math_font_parameter(DelimitedSubFormulaMinHeight, math_dimen_parameter);
+ set_math_font_parameter(DisplayOperatorMinHeight, math_dimen_parameter);
+ set_math_font_parameter(MathLeading, math_dimen_parameter);
+ set_math_font_parameter(AxisHeight, math_dimen_parameter);
+ set_math_font_parameter(AccentBaseHeight, math_dimen_parameter);
+ set_math_font_parameter(AccentBaseDepth, math_dimen_parameter);
+ set_math_font_parameter(FlattenedAccentBaseHeight, math_dimen_parameter);
+ set_math_font_parameter(FlattenedAccentBaseDepth, math_dimen_parameter);
+ set_math_font_parameter(SubscriptShiftDown, math_dimen_parameter);
+ set_math_font_parameter(SubscriptTopMax, math_dimen_parameter);
+ set_math_font_parameter(SubscriptBaselineDropMin, math_dimen_parameter);
+ set_math_font_parameter(SuperscriptShiftUp, math_dimen_parameter);
+ set_math_font_parameter(SuperscriptShiftUpCramped, math_dimen_parameter);
+ set_math_font_parameter(SuperscriptBottomMin, math_dimen_parameter);
+ set_math_font_parameter(SuperscriptBaselineDropMax, math_dimen_parameter);
+ set_math_font_parameter(SubSuperscriptGapMin, math_dimen_parameter);
+ set_math_font_parameter(SuperscriptBottomMaxWithSubscript, math_dimen_parameter);
+ set_math_font_parameter(SpaceBeforeScript, math_dimen_parameter);
+ set_math_font_parameter(SpaceAfterScript, math_dimen_parameter);
+ set_math_font_parameter(UpperLimitGapMin, math_dimen_parameter);
+ set_math_font_parameter(UpperLimitBaselineRiseMin, math_dimen_parameter);
+ set_math_font_parameter(LowerLimitGapMin, math_dimen_parameter);
+ set_math_font_parameter(LowerLimitBaselineDropMin, math_dimen_parameter);
+ set_math_font_parameter(StackTopShiftUp, math_dimen_parameter);
+ set_math_font_parameter(StackTopDisplayStyleShiftUp, math_dimen_parameter);
+ set_math_font_parameter(StackBottomShiftDown, math_dimen_parameter);
+ set_math_font_parameter(StackBottomDisplayStyleShiftDown, math_dimen_parameter);
+ set_math_font_parameter(StackGapMin, math_dimen_parameter);
+ set_math_font_parameter(StackDisplayStyleGapMin, math_dimen_parameter);
+ set_math_font_parameter(StretchStackTopShiftUp, math_dimen_parameter);
+ set_math_font_parameter(StretchStackBottomShiftDown, math_dimen_parameter);
+ set_math_font_parameter(StretchStackGapAboveMin, math_dimen_parameter);
+ set_math_font_parameter(StretchStackGapBelowMin, math_dimen_parameter);
+ set_math_font_parameter(FractionNumeratorShiftUp, math_dimen_parameter);
+ set_math_font_parameter(FractionNumeratorDisplayStyleShiftUp, math_dimen_parameter);
+ set_math_font_parameter(FractionDenominatorShiftDown, math_dimen_parameter);
+ set_math_font_parameter(FractionDenominatorDisplayStyleShiftDown, math_dimen_parameter);
+ set_math_font_parameter(FractionNumeratorGapMin, math_dimen_parameter);
+ set_math_font_parameter(FractionNumeratorDisplayStyleGapMin, math_dimen_parameter);
+ set_math_font_parameter(FractionRuleThickness, math_dimen_parameter);
+ set_math_font_parameter(FractionDenominatorGapMin, math_dimen_parameter);
+ set_math_font_parameter(FractionDenominatorDisplayStyleGapMin, math_dimen_parameter);
+ set_math_font_parameter(SkewedFractionHorizontalGap, math_dimen_parameter);
+ set_math_font_parameter(SkewedFractionVerticalGap, math_dimen_parameter);
+ set_math_font_parameter(OverbarVerticalGap, math_dimen_parameter);
+ set_math_font_parameter(OverbarRuleThickness, math_dimen_parameter);
+ set_math_font_parameter(OverbarExtraAscender, math_dimen_parameter);
+ set_math_font_parameter(UnderbarVerticalGap, math_dimen_parameter);
+ set_math_font_parameter(UnderbarRuleThickness, math_dimen_parameter);
+ set_math_font_parameter(UnderbarExtraDescender, math_dimen_parameter);
+ set_math_font_parameter(RadicalVerticalGap, math_dimen_parameter);
+ set_math_font_parameter(RadicalDisplayStyleVerticalGap, math_dimen_parameter);
+ set_math_font_parameter(RadicalRuleThickness, math_dimen_parameter);
+ set_math_font_parameter(RadicalExtraAscender, math_dimen_parameter);
+ set_math_font_parameter(RadicalKernBeforeDegree, math_dimen_parameter);
+ set_math_font_parameter(RadicalKernAfterDegree, math_dimen_parameter);
+ set_math_font_parameter(RadicalDegreeBottomRaisePercent, math_int_parameter);
+ set_math_font_parameter(RadicalKernAfterExtensible, math_dimen_parameter);
+ set_math_font_parameter(RadicalKernBeforeExtensible, math_dimen_parameter);
+ set_math_font_parameter(MinConnectorOverlap, math_dimen_parameter);
+ set_math_font_parameter(SubscriptShiftDownWithSuperscript, math_dimen_parameter);
+ set_math_font_parameter(FractionDelimiterSize, math_dimen_parameter);
+ set_math_font_parameter(FractionDelimiterDisplayStyleSize, math_dimen_parameter);
+ set_math_font_parameter(NoLimitSubFactor, math_int_parameter);
+ set_math_font_parameter(NoLimitSupFactor, math_int_parameter);
+ set_math_font_parameter(PrimeRaisePercent, math_int_parameter);
+ set_math_font_parameter(PrimeRaiseComposedPercent, math_int_parameter);
+ set_math_font_parameter(PrimeShiftUp, math_dimen_parameter);
+ set_math_font_parameter(PrimeShiftUpCramped, math_dimen_parameter);
+ set_math_font_parameter(PrimeBaselineDropMax, math_dimen_parameter);
+ set_math_font_parameter(PrimeSpaceAfter, math_dimen_parameter);
+ set_math_font_parameter(PrimeWidthPercent, math_int_parameter);
+ set_math_font_parameter(SkewedDelimiterTolerance, math_dimen_parameter);
+ set_math_font_parameter(AccentTopShiftUp, math_dimen_parameter);
+ set_math_font_parameter(AccentBottomShiftDown, math_dimen_parameter);
+ set_math_font_parameter(AccentTopOvershoot, math_int_parameter);
+ set_math_font_parameter(AccentBottomOvershoot, math_int_parameter);
+ set_math_font_parameter(AccentSuperscriptDrop, math_dimen_parameter);
+ set_math_font_parameter(AccentSuperscriptPercent, math_int_parameter);
+ set_math_font_parameter(AccentExtendMargin, math_dimen_parameter);
+ set_math_font_parameter(FlattenedAccentTopShiftUp, math_dimen_parameter);
+ set_math_font_parameter(FlattenedAccentBottomShiftDown, math_dimen_parameter);
+ set_math_font_parameter(DelimiterPercent, math_int_parameter);
+ set_math_font_parameter(DelimiterShortfall, math_dimen_parameter);
+}
diff --git a/source/luametatex/source/lua/lmtinterface.h b/source/luametatex/source/lua/lmtinterface.h
new file mode 100644
index 000000000..e55b03e84
--- /dev/null
+++ b/source/luametatex/source/lua/lmtinterface.h
@@ -0,0 +1,1754 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LINTERFACE_H
+# define LMT_LINTERFACE_H
+
+# define lmt_linterface_inline 1
+
+/*tex
+
+ In this file we collect all kind of interface stuff related to \LUA. It is a large file because
+ we also create \LUA\ string entries which speeds up the interfacing.
+
+*/
+
+# include "lua.h"
+# include "lauxlib.h"
+# include "lualib.h"
+
+/*tex Just in case: */
+
+extern int tex_formatted_error (const char *t, const char *fmt, ...);
+extern void tex_formatted_warning (const char *t, const char *fmt, ...);
+extern void tex_emergency_message (const char *t, const char *fmt, ...);
+
+/*tex
+
+ In the beginning we had multiple \LUA\ states but that didn't work out well if you want to
+ intercace to the \TEX\ kernel. So, we dropped that and now have only one main state.
+
+*/
+
+typedef struct lua_state_info {
+ lua_State *lua_instance;
+ int used_bytes;
+ int used_bytes_max;
+ int function_table_id;
+ int function_callback_count;
+ int value_callback_count;
+ int bytecode_callback_count;
+ int local_callback_count;
+ int saved_callback_count;
+ int file_callback_count;
+ int direct_callback_count;
+ int message_callback_count;
+ int function_table_size;
+ int bytecode_bytes;
+ int bytecode_max;
+ int version_number;
+ int release_number;
+ luaL_Buffer *used_buffer;
+ int integer_size;
+} lua_state_info ;
+
+extern lua_state_info lmt_lua_state;
+
+/*tex The libraries are opened and initialized by the following functions. */
+
+extern int luaopen_tex (lua_State *L);
+extern int luaopen_texio (lua_State *L);
+extern int luaopen_language (lua_State *L);
+extern int luaopen_filelib (lua_State *L);
+extern int luaopen_lpeg (lua_State *L);
+extern int luaopen_md5 (lua_State *L);
+extern int luaopen_sha2 (lua_State *L);
+extern int luaopen_aes (lua_State *L);
+extern int luaopen_basexx (lua_State *L);
+extern int luaopen_xmath (lua_State *L);
+extern int luaopen_xcomplex (lua_State *L);
+extern int luaopen_xdecimal (lua_State *L);
+extern int luaopen_xzip (lua_State *L);
+extern int luaopen_socket_core (lua_State *L);
+extern int luaopen_mime_core (lua_State *L);
+extern int luaopen_pdfe (lua_State *L);
+extern int luaopen_pngdecode (lua_State *L);
+extern int luaopen_pdfdecode (lua_State *L);
+extern int luaopen_mplib (lua_State *L);
+extern int luaopen_fio (lua_State *L);
+extern int luaopen_sio (lua_State *L);
+extern int luaopen_sparse (lua_State *L);
+extern int luaopen_callback (lua_State *L);
+extern int luaopen_lua (lua_State *L);
+extern int luaopen_luac (lua_State *L);
+extern int luaopen_status (lua_State *L);
+extern int luaopen_font (lua_State *L);
+extern int luaopen_node (lua_State *L);
+extern int luaopen_token (lua_State *L);
+extern int luaopen_optional (lua_State *L);
+extern int luaopen_library (lua_State *L);
+
+extern int luaextend_os (lua_State *L);
+extern int luaextend_io (lua_State *L);
+extern int luaextend_string (lua_State *L);
+extern int luaextend_xcomplex (lua_State *L);
+
+/*tex
+
+ We finetune the string hasher. When playing with \LUAJIT\ we found that its hashes was pretty
+ inefficient and geared for \URL's! So there we switched to the \LUA\ hasher an din the process
+ we also did experiments with the |LUA_HASHLIMIT| parameter. There's an (already old) article
+ about that in one of the \LUATEX\ histories.
+
+ */
+
+# if !defined(LUAI_HASHLIMIT)
+# define LUAI_HASHLIMIT 6
+# endif
+
+/*tex
+
+ Now comes a large section. Here we create and use macros that preset the access pointers
+ (indices) to keys which is faster in comparison. We also handle the arrays that hold the
+ information about what fields there are in nodes. This is code from the early times when we
+ found out that especially when a lot of access by key happens performance is hit because
+ strings get rehashed. It might be that in the meantime \LUA\ suffers less from this but we
+ keep this abstraction anyway.
+
+ As we share this mechanism between all modules and because, although it is built from
+ components, \LUAMETATEX\ is a whole, we now also moved the \MPLIB\ keys into the same
+ namespace which saves code and macros. Some are shared anyway.
+
+ There is also a bit of metatable lookup involved but we only do that for those modules for
+ which it's critical.
+
+ Contrary to \LUATEX\ we use a struct to collect the indices and keys instead of global
+ pointers. This hides the symbols better.
+
+*/
+
+// todo: add L to some below
+
+# define lua_key_eq(a,b) (a == lmt_keys.ptr_##b)
+# define lua_key_index(a) lmt_keys.idx_##a
+# define lua_key(a) lmt_keys.ptr_##a
+
+# define init_lua_key(L,a) \
+ lua_pushliteral(L, #a); \
+ lmt_keys.ptr_##a = lua_tolstring(L, -1, NULL); \
+ lmt_keys.idx_##a = luaL_ref(L, LUA_REGISTRYINDEX);
+
+# define init_lua_key_alias(L,a,b) \
+ lua_pushliteral(L, b); \
+ lmt_keys.ptr_##a = lua_tolstring(L, -1, NULL); \
+ lmt_keys.idx_##a = luaL_ref(L, LUA_REGISTRYINDEX);
+
+# define make_lua_key_ptr(L,a) const char *ptr_##a
+# define make_lua_key_idx(L,a) int idx_##a
+
+# define make_lua_key_ptr_alias(L,a,b) const char *ptr_##a
+# define make_lua_key_idx_alias(L,a,b) int idx_##a
+
+# define make_lua_key
+# define make_lua_key_alias
+
+# define lua_push_key(a) lua_rawgeti(L, LUA_REGISTRYINDEX, lua_key_index(a));
+# define lua_push_key_by_index(a) lua_rawgeti(L, LUA_REGISTRYINDEX, a);
+
+# define lua_get_metatablelua(a) do { \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lua_key_index(a)); \
+ lua_gettable(L, LUA_REGISTRYINDEX); \
+} while (0)
+
+# define lua_push_number(L,x) lua_pushnumber (L, (lua_Number) (x))
+# define lua_push_integer(L,x) lua_pushinteger(L, (int) (x))
+# define lua_push_halfword(L,x) lua_pushinteger(L, (lua_Integer) (x))
+
+# define lua_push_number_at_index(L,i,x) \
+ lua_pushnumber(L, (lua_Number) (x)); \
+ lua_rawseti(L, -2, i);
+
+# define lua_push_integer_at_index(L,i,x) \
+ lua_pushinteger(L, (x)); \
+ lua_rawseti(L, -2, i);
+
+# define lua_push_key_at_index(L,k,i) \
+ lua_pushinteger(L, i); \
+ lua_push_key(k); \
+ lua_rawset(L, -3);
+
+# define lua_push_nil_at_key(L,k) \
+ lua_push_key(k); \
+ lua_pushnil(L); \
+ lua_rawset(L, -3);
+
+# define lua_push_number_at_key(L,k,x) \
+ lua_push_key(k); \
+ lua_pushnumber(L, (lua_Number) (x)); \
+ lua_rawset(L, -3);
+
+# define lua_push_integer_at_key(L,k,x) \
+ lua_push_key(k); \
+ lua_pushinteger(L, (x)); \
+ lua_rawset(L, -3);
+
+# define lua_push_string_at_key(L,k,s) \
+ lua_push_key(k); \
+ lua_pushstring(L, s); \
+ lua_rawset(L, -3);
+
+# define mlua_push_lstring_at_key(L,k,s,l) \
+ lua_push_key(k); \
+ lua_pushlstring(L, s, l); \
+ lua_rawset(L, -3);
+
+# define lua_push_boolean_at_key(L,k,b) \
+ lua_push_key(k); \
+ lua_pushboolean(L, b); \
+ lua_rawset(L, -3);
+
+# define lua_push_value_at_key(L,k,v) \
+ lua_push_key(k); \
+ lua_push_key(v); \
+ lua_rawset(L, -3);
+
+# define lua_push_svalue_at_key(L,k,v) \
+ lua_push_key(k); \
+ lua_push_key_by_index(v); \
+ lua_rawset(L, -3);
+
+# define lua_push_specification_at_key(L,k,x) \
+ lua_push_key(k); \
+ lmt_push_specification(L, x, 0); \
+ lua_rawset(L, -3);
+
+/*tex We put some here. These are not public (read: names can change). */
+
+/*tex Used in |lmtnodelib|. */
+
+# define NODE_METATABLE_INSTANCE "node.instance"
+# define NODE_PROPERTIES_DIRECT "node.properties"
+# define NODE_PROPERTIES_INDIRECT "node.properties.indirect"
+# define NODE_PROPERTIES_INSTANCE "node.properties.instance"
+
+/*tex Used in |lmttokenlib|. */
+
+# define TOKEN_METATABLE_INSTANCE "token.instance"
+# define TOKEN_METATABLE_PACKAGE "token.package"
+
+/*tex Used in |lmtepdflib|. */
+
+# define PDFE_METATABLE_INSTANCE "pdfe.instance"
+# define PDFE_METATABLE_DICTIONARY "pdfe.dictionary"
+# define PDFE_METATABLE_ARRAY "pdfe.array"
+# define PDFE_METATABLE_STREAM "pdfe.stream"
+# define PDFE_METATABLE_REFERENCE "pdfe.reference"
+
+/*tex Used in |lmtmplib|. */
+
+# define MP_METATABLE_INSTANCE "mp.instance"
+# define MP_METATABLE_FIGURE "mp.figure"
+# define MP_METATABLE_OBJECT "mp.object"
+
+/*tex Used in |lmtsparselib|. */
+
+# define SPARSE_METATABLE_INSTANCE "sparse.instance"
+
+/*tex
+
+ Currently we sometimes use numbers and sometimes strings in node properties. We can make that
+ consistent by having a check on number and if not then assign a string. The strings are
+ prehashed and we make a bunch of lua tables that have these values. We can preassign these at
+ startup time.
+
+*/
+
+typedef struct value_info {
+ union {
+ int id;
+ int type;
+ int value;
+ };
+ int lua;
+ const char *name;
+} value_info;
+
+typedef struct node_info {
+ int id;
+ int size;
+ value_info *subtypes;
+ value_info *fields;
+ const char *name;
+ int lua;
+ int visible;
+ int first;
+ int last;
+} node_info;
+
+typedef struct command_item {
+ int id;
+ int lua;
+ const char *name;
+ int kind;
+ int min;
+ int max;
+ int base;
+ int fixedvalue;
+} command_item;
+
+# define unknown_node 0xFFFF
+# define unknown_subtype 0xFFFF
+# define unknown_field 0xFFFF
+# define unknown_value 0xFFFF
+
+# define set_value_entry_nop(target,n) target[n].lua = 0; target[n].name = NULL; target[n].value = unknown_value;
+# define set_value_entry_key(target,n,k) target[n].lua = lua_key_index(k); target[n].name = lua_key(k); target[n].value = n;
+# define set_value_entry_lua(target,n,k) target[n].lua = lua_key_index(k); target[n].name = lua_key(k);
+# define set_value_entry_val(target,n,v,k) target[n].lua = lua_key_index(k); target[n].name = lua_key(k); target[n].value = v;
+
+extern value_info *lmt_aux_allocate_value_info(size_t last);
+
+typedef struct lmt_interface_info {
+ /*tex These are mostly used in lmtnodelib. */
+ value_info *pack_type_values;
+ value_info *group_code_values;
+ value_info *par_context_values;
+ value_info *page_context_values;
+ value_info *append_line_context_values;
+ value_info *alignment_context_values;
+ value_info *par_begin_values;
+ value_info *par_mode_values;
+ value_info *math_style_name_values;
+ value_info *math_style_variant_values;
+ /* value_info *noad_option_values; */
+ /* value_info *glyph_option_values; */
+ /*tex These are mostly used in lmttokenlib. */
+ value_info *lua_function_values;
+ value_info *direction_values;
+ value_info *node_fill_values;
+ value_info *page_contribute_values;
+ value_info *math_style_values;
+ value_info *math_parameter_values;
+ value_info *math_font_parameter_values;
+ value_info *math_indirect_values;
+ value_info *field_type_values;
+ /*tex Here's one for nodes. */
+ node_info *node_data;
+ /*tex And this one is for tokens. */
+ command_item *command_names;
+} lmt_interface_info ;
+
+extern lmt_interface_info lmt_interface;
+
+# define lmt_push_pack_type(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.pack_type_values [n].lua)
+# define lmt_push_group_code(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.group_code_values [n].lua)
+# define lmt_push_par_context(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.par_context_values [n].lua)
+# define lmt_push_page_context(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.page_context_values [n].lua)
+# define lmt_push_append_line_context(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.append_line_context_values[n].lua)
+# define lmt_push_alignment_context(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.alignment_context_values [n].lua)
+# define lmt_push_par_begin(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.par_begin_values [n].lua)
+# define lmt_push_par_mode(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.par_mode_values [n].lua)
+# define lmt_push_math_style_name(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_style_name_values [n].lua)
+# define lmt_push_math_style_variant(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_style_variant_values [n].lua)
+# define lmt_push_math_noad_option(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_noad_option_values [n].lua)
+# define lmt_push_lua_function_values(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.lua_function_values [n].lua)
+# define lmt_push_direction(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.direction_values [n].lua)
+# define lmt_push_node_fill(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.node_fill_values [n].lua)
+# define lmt_push_page_contribute(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.page_contribute_values [n].lua)
+# define lmt_push_math_style(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_style_values [n].lua)
+# define lmt_push_math_parameter(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_parameter_values [n].lua)
+# define lmt_push_math_font_parameter(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_font_parameter_values[n].lua)
+# define lmt_push_math_indirect(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.math_indirect_values [n].lua)
+# define lmt_push_field_type(L,n) lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_interface.field_type_values [n].lua)
+
+# define lmt_name_of_pack_type(n) lmt_interface.pack_type_values [n].name
+# define lmt_name_of_group_code(n) lmt_interface.group_code_values [n].name
+# define lmt_name_of_par_context(n) lmt_interface.par_context_values [n].name
+# define lmt_name_of_page_context(n) lmt_interface.page_context_values [n].name
+# define lmt_name_of_append_line_context(n) lmt_interface.append_line_context_values[n].name
+# define lmt_name_of_alignment_context(n) lmt_interface.alignment_context_values [n].name
+# define lmt_name_of_par_begin(n) lmt_interface.par_begin_values [n].name
+# define lmt_name_of_par_mode(n) lmt_interface.par_mode_values [n].name
+# define lmt_name_of_math_style_name(n) lmt_interface.math_style_name_values [n].name
+# define lmt_name_of_math_style_variant(n) lmt_interface.math_style_variant_values [n].name
+# define lmt_name_of_math_noad_option(n) lmt_interface.math_noad_option_values [n].name
+# define lmt_name_of_lua_function_values(n) lmt_interface.lua_function_values [n].name
+# define lmt_name_of_direction(n) lmt_interface.direction_values [n].name
+# define lmt_name_of_node_fill(n) lmt_interface.node_fill_values [n].name
+# define lmt_name_of_page_contribute(n) lmt_interface.page_contribute_values [n].name
+# define lmt_name_of_math_style(n) lmt_interface.math_style_values [n].name
+# define lmt_name_of_math_parameter(n) lmt_interface.math_parameter_values [n].name
+# define lmt_name_of_math_font_parameter(n) lmt_interface.math_font_parameter_values[n].name
+# define lmt_name_of_math_indirect(n) lmt_interface.math_indirect_values [n].name
+# define lmt_name_of_field_type(n) lmt_interface.field_type_values [n].name
+
+/*tex This list will be made smaller because not all values need the boost. */
+
+# define declare_shared_lua_keys(L) \
+/* */\
+make_lua_key(L, __index);\
+make_lua_key(L, above);\
+make_lua_key(L, abovedisplayshortskip);\
+make_lua_key(L, abovedisplayskip);\
+make_lua_key(L, accent);\
+make_lua_key(L, AccentBaseHeight);\
+make_lua_key(L, AccentBaseDepth);\
+make_lua_key(L, accentbaseheight);\
+make_lua_key(L, accentbasedepth);\
+make_lua_key(L, AccentTopShiftUp);\
+make_lua_key(L, accenttopshiftup);\
+make_lua_key(L, AccentTopOvershoot);\
+make_lua_key(L, accenttopovershoot);\
+make_lua_key(L, adapted);\
+make_lua_key(L, FlattenedAccentTopShiftUp);\
+make_lua_key(L, flattenedaccenttopshiftup);\
+make_lua_key(L, AccentBottomShiftDown);\
+make_lua_key(L, accentbottomshiftdown);\
+make_lua_key(L, AccentBottomOvershoot);\
+make_lua_key(L, accentbottomovershoot);\
+make_lua_key(L, AccentSuperscriptDrop);\
+make_lua_key(L, accentsuperscriptdrop);\
+make_lua_key(L, AccentSuperscriptPercent);\
+make_lua_key(L, accentsuperscriptpercent);\
+make_lua_key(L, AccentExtendMargin);\
+make_lua_key(L, accentextendmargin);\
+make_lua_key(L, FlattenedAccentBottomShiftDown);\
+make_lua_key(L, flattenedaccentbottomshiftdown);\
+make_lua_key(L, accentkern);\
+make_lua_key(L, accentvariant);\
+make_lua_key(L, active);\
+make_lua_key(L, active_char);\
+make_lua_key(L, adapttoleftsize);\
+make_lua_key(L, adapttorightsize);\
+make_lua_key(L, additional);\
+make_lua_key(L, adjdemerits);\
+make_lua_key(L, adjust);\
+make_lua_key(L, adjustedhbox);\
+make_lua_key(L, adjustspacing);\
+make_lua_key(L, adjustspacingshrink);\
+make_lua_key(L, adjustspacingstep);\
+make_lua_key(L, adjustspacingstretch);\
+make_lua_key(L, advance);\
+make_lua_key(L, after_output);\
+make_lua_key(L, after_something);\
+make_lua_key(L, afterdisplay);\
+make_lua_key(L, afterdisplaypenalty);\
+make_lua_key(L, afteroutput);\
+make_lua_key(L, aliased);\
+make_lua_key(L, align);\
+make_lua_key(L, alignrecord);\
+make_lua_key(L, alignstack);\
+make_lua_key(L, alignhead);\
+make_lua_key(L, alignment);\
+make_lua_key(L, alignment_tab);\
+make_lua_key(L, alignset);\
+make_lua_key(L, alsosimple);\
+make_lua_key(L, anchor);\
+make_lua_key(L, argument);\
+make_lua_key(L, arithmic);\
+make_lua_key(L, attr);\
+make_lua_key(L, attribute);\
+make_lua_key(L, attribute_list);\
+make_lua_key(L, attributelist);\
+make_lua_key(L, auto);\
+make_lua_key(L, automatic);\
+make_lua_key(L, automaticpenalty);\
+make_lua_key(L, axis);\
+make_lua_key(L, AxisHeight);\
+make_lua_key(L, baselineskip);\
+make_lua_key(L, beforedisplay);\
+make_lua_key(L, beforedisplaypenalty);\
+make_lua_key(L, begin_group);\
+make_lua_key(L, begin_local);\
+make_lua_key(L, begin_paragraph);\
+make_lua_key(L, beginmath);\
+make_lua_key(L, beginparagraph);\
+make_lua_key(L, belowdisplayshortskip);\
+make_lua_key(L, belowdisplayskip);\
+make_lua_key(L, bend_tolerance);\
+make_lua_key(L, bestinsert);\
+make_lua_key(L, bestpagebreak);\
+make_lua_key(L, bestsize);\
+make_lua_key(L, bin);\
+make_lua_key(L, binbinspacing);\
+make_lua_key(L, binclosespacing);\
+make_lua_key(L, binfracspacing);\
+make_lua_key(L, bininnerspacing);\
+make_lua_key(L, binmiddlespacing);\
+make_lua_key(L, binopenspacing);\
+make_lua_key(L, binopspacing);\
+make_lua_key(L, binordspacing);\
+make_lua_key(L, binpunctspacing);\
+make_lua_key(L, binradspacing);\
+make_lua_key(L, binrelspacing);\
+make_lua_key(L, boolean);\
+make_lua_key(L, bottomaccent);\
+make_lua_key(L, bottomaccentvariant);\
+make_lua_key(L, bothflexible);\
+make_lua_key(L, bottom);\
+make_lua_key(L, bottomleft);\
+make_lua_key(L, bottomlevel);\
+make_lua_key(L, bottommargin);\
+make_lua_key(L, bottomright);\
+make_lua_key(L, boundary);\
+make_lua_key(L, box);\
+make_lua_key(L, broken);\
+make_lua_key(L, brokeninsert);\
+make_lua_key(L, brokenpenalty);\
+make_lua_key(L, bytecode);\
+make_lua_key(L, call);\
+make_lua_key(L, callback);\
+make_lua_key(L, cancel);\
+make_lua_key(L, cardinal);\
+make_lua_key(L, case_shift);\
+make_lua_key(L, catalog);\
+make_lua_key(L, Catalog);\
+make_lua_key(L, catcode_table);\
+make_lua_key(L, category);\
+make_lua_key(L, cell);\
+make_lua_key(L, char);\
+make_lua_key(L, char_given);\
+make_lua_key(L, char_number);\
+make_lua_key(L, character);\
+make_lua_key(L, characters);\
+make_lua_key(L, choice);\
+make_lua_key(L, class);\
+make_lua_key(L, cleaders);\
+make_lua_key(L, close);\
+make_lua_key(L, closebinspacing);\
+make_lua_key(L, closeclosespacing);\
+make_lua_key(L, closefracspacing);\
+make_lua_key(L, closeinnerspacing);\
+make_lua_key(L, closemiddlespacing);\
+make_lua_key(L, closeopenspacing);\
+make_lua_key(L, closeopspacing);\
+make_lua_key(L, closeordspacing);\
+make_lua_key(L, closepunctspacing);\
+make_lua_key(L, closeradspacing);\
+make_lua_key(L, closerelspacing);\
+make_lua_key(L, clubpenalties);\
+make_lua_key(L, clubpenalty);\
+make_lua_key(L, cmd);\
+make_lua_key(L, cmdname);\
+make_lua_key(L, collapse);\
+make_lua_key(L, combine_toks);\
+make_lua_key(L, command);\
+make_lua_key(L, comment);\
+make_lua_key(L, compactmath);\
+make_lua_key(L, compound);\
+make_lua_key(L, condition);\
+make_lua_key(L, conditional);\
+make_lua_key(L, conditionalmathskip);\
+make_lua_key(L, connectoroverlapmin);\
+make_lua_key(L, container);\
+make_lua_key(L, contributehead);\
+make_lua_key(L, convert);\
+make_lua_key(L, correctionskip);\
+make_lua_key(L, cost);\
+make_lua_key(L, count);\
+make_lua_key(L, cramped);\
+make_lua_key(L, crampeddisplay);\
+make_lua_key(L, crampedscript);\
+make_lua_key(L, crampedscriptscript);\
+make_lua_key(L, crampedtext);\
+make_lua_key(L, cs_name);\
+make_lua_key(L, csname);\
+make_lua_key(L, current);\
+make_lua_key(L, data);\
+make_lua_key(L, deep_frozen_cs_dont_expand);\
+make_lua_key(L, deep_frozen_cs_end_template);\
+make_lua_key(L, def);\
+make_lua_key(L, define_char_code);\
+make_lua_key(L, define_family);\
+make_lua_key(L, define_font);\
+make_lua_key(L, define_lua_call);\
+make_lua_key(L, degree);\
+make_lua_key(L, degreevariant);\
+make_lua_key(L, DelimitedSubFormulaMinHeight);\
+make_lua_key(L, delimiter);\
+make_lua_key(L, delimited);\
+make_lua_key(L, delimiter_number);\
+make_lua_key(L, delimiterpercent);\
+make_lua_key(L, DelimiterPercent);\
+make_lua_key(L, delimitershortfall);\
+make_lua_key(L, DelimiterShortfall);\
+make_lua_key(L, delimiterover);\
+make_lua_key(L, delimiterovervariant);\
+make_lua_key(L, delimiterunder);\
+make_lua_key(L, delimiterundervariant);\
+make_lua_key(L, delta);\
+make_lua_key(L, demerits);\
+make_lua_key(L, denominator);\
+make_lua_key(L, denominatorvariant);\
+make_lua_key(L, depth);\
+make_lua_key(L, designsize);\
+make_lua_key(L, dimension);\
+make_lua_key(L, dir);\
+make_lua_key(L, direct);\
+make_lua_key(L, direction);\
+make_lua_key(L, directory);\
+make_lua_key(L, disc);\
+make_lua_key(L, discpart);\
+make_lua_key(L, discretionary);\
+make_lua_key(L, display);\
+make_lua_key(L, DisplayOperatorMinHeight);\
+make_lua_key(L, displaywidowpenalties);\
+make_lua_key(L, displaywidowpenalty);\
+make_lua_key(L, doffset);\
+make_lua_key(L, doublehyphendemerits);\
+make_lua_key(L, doublesuperscript);\
+make_lua_key(L, emergencystretch);\
+make_lua_key(L, empty);\
+make_lua_key(L, end);\
+make_lua_key(L, end_cs_name);\
+make_lua_key(L, end_file);\
+make_lua_key(L, end_group);\
+make_lua_key(L, end_job);\
+make_lua_key(L, end_line);\
+make_lua_key(L, end_local);\
+make_lua_key(L, end_match);\
+make_lua_key(L, end_paragraph);\
+make_lua_key(L, end_template);\
+make_lua_key(L, endmath);\
+make_lua_key(L, equation);\
+make_lua_key(L, equation_number);\
+make_lua_key(L, equationnumber);\
+make_lua_key(L, equationnumberpenalty);\
+make_lua_key(L, escape);\
+make_lua_key(L, etex);\
+make_lua_key(L, exact);\
+make_lua_key(L, exactly);\
+make_lua_key(L, expand_after);\
+make_lua_key(L, expandable);\
+make_lua_key(L, expanded);\
+make_lua_key(L, expansion);\
+make_lua_key(L, explicit);\
+make_lua_key(L, explicit_space);\
+make_lua_key(L, explicitpenalty);\
+make_lua_key(L, expression);\
+make_lua_key(L, extender);\
+make_lua_key(L, extensible);\
+make_lua_key(L, extraspace);\
+make_lua_key(L, extrasubprescriptshift);\
+make_lua_key(L, extrasubprescriptspace);\
+make_lua_key(L, extrasubscriptshift);\
+make_lua_key(L, extrasubscriptspace);\
+make_lua_key(L, extrasuperprescriptshift);\
+make_lua_key(L, extrasuperprescriptspace);\
+make_lua_key(L, extrasuperscriptshift);\
+make_lua_key(L, extrasuperscriptspace);\
+make_lua_key(L, fam);\
+make_lua_key(L, feedbackcompound);\
+make_lua_key(L, fence);\
+make_lua_key(L, fenced);\
+make_lua_key(L, fi);\
+make_lua_key(L, fil);\
+make_lua_key(L, file);\
+make_lua_key(L, fill);\
+make_lua_key(L, filll);\
+make_lua_key(L, finalhyphendemerits);\
+make_lua_key(L, finalpenalty);\
+make_lua_key(L, finishrow);\
+make_lua_key(L, first);\
+make_lua_key(L, fixedboth);\
+make_lua_key(L, fixedbottom);\
+make_lua_key(L, fixedtop);\
+make_lua_key(L, flags);\
+make_lua_key(L, FlattenedAccentBaseHeight);\
+make_lua_key(L, flattenedaccentbaseheight);\
+make_lua_key(L, FlattenedAccentBaseDepth);\
+make_lua_key(L, flattenedaccentbasedepth);\
+make_lua_key(L, flataccent);\
+make_lua_key(L, float);\
+make_lua_key(L, followedbyspace);\
+make_lua_key(L, font);\
+make_lua_key(L, fontkern);\
+make_lua_key(L, fontspec);\
+make_lua_key(L, force);\
+make_lua_key(L, forcecheck);\
+make_lua_key(L, forcehandler);\
+make_lua_key(L, forcerulethickness);\
+make_lua_key(L, fracbinspacing);\
+make_lua_key(L, fracclosespacing);\
+make_lua_key(L, fracfracspacing);\
+make_lua_key(L, fracinnerspacing);\
+make_lua_key(L, fracmiddlespacing);\
+make_lua_key(L, fracopenspacing);\
+make_lua_key(L, fracopspacing);\
+make_lua_key(L, fracordspacing);\
+make_lua_key(L, fracpunctspacing);\
+make_lua_key(L, fracradspacing);\
+make_lua_key(L, fracrelspacing);\
+make_lua_key(L, fraction);\
+make_lua_key(L, FractionDelimiterDisplayStyleSize);\
+make_lua_key(L, FractionDelimiterSize);\
+make_lua_key(L, fractiondelsize);\
+make_lua_key(L, fractiondenomdown);\
+make_lua_key(L, FractionDenominatorDisplayStyleGapMin);\
+make_lua_key(L, FractionDenominatorDisplayStyleShiftDown);\
+make_lua_key(L, FractionDenominatorGapMin);\
+make_lua_key(L, FractionDenominatorShiftDown);\
+make_lua_key(L, fractiondenomvgap);\
+make_lua_key(L, FractionNumeratorDisplayStyleGapMin);\
+make_lua_key(L, FractionNumeratorDisplayStyleShiftUp);\
+make_lua_key(L, FractionNumeratorGapMin);\
+make_lua_key(L, FractionNumeratorShiftUp);\
+make_lua_key(L, fractionnumup);\
+make_lua_key(L, fractionnumvgap);\
+make_lua_key(L, fractionrule);\
+make_lua_key(L, FractionRuleThickness);\
+make_lua_key(L, fractionvariant);\
+make_lua_key(L, frozen);\
+make_lua_key(L, function);\
+make_lua_key(L, geometry);\
+make_lua_key(L, get_mark);\
+make_lua_key(L, ghost);\
+make_lua_key(L, gleaders);\
+make_lua_key(L, global);\
+make_lua_key(L, glue);\
+make_lua_key(L, glueorder);\
+make_lua_key(L, glueset);\
+make_lua_key(L, gluesign);\
+make_lua_key(L, gluespec);\
+make_lua_key(L, glyph);\
+make_lua_key(L, group);\
+make_lua_key(L, h);\
+make_lua_key(L, halign);\
+make_lua_key(L, hangafter);\
+make_lua_key(L, hangindent);\
+make_lua_key(L, hbox);\
+make_lua_key(L, hdelimiter);\
+make_lua_key(L, head);\
+make_lua_key(L, height);\
+make_lua_key(L, hextensible);\
+make_lua_key(L, hextensiblevariant);\
+make_lua_key(L, hlist);\
+make_lua_key(L, hmodepar);\
+make_lua_key(L, hmove);\
+make_lua_key(L, hoffset);\
+make_lua_key(L, holdhead);\
+make_lua_key(L, horizontal);\
+make_lua_key(L, horizontalmathkern);\
+make_lua_key(L, hrule);\
+make_lua_key(L, hsize);\
+make_lua_key(L, hskip);\
+make_lua_key(L, hparts);\
+make_lua_key(L, hyphenate);\
+make_lua_key(L, hyphenated);\
+make_lua_key(L, hyphenation);\
+make_lua_key(L, hyphenationmode);\
+make_lua_key(L, hyphenchar);\
+make_lua_key(L, id);\
+make_lua_key(L, ifstack);\
+make_lua_key(L, if_test);\
+make_lua_key(L, ignore);\
+make_lua_key(L, ignore_something);\
+make_lua_key(L, ignorebounds);\
+make_lua_key(L, ignored);\
+make_lua_key(L, image);\
+make_lua_key(L, immediate);\
+make_lua_key(L, immutable);\
+make_lua_key(L, indent);\
+make_lua_key(L, indentskip);\
+make_lua_key(L, index);\
+make_lua_key(L, Info);\
+make_lua_key(L, info);\
+make_lua_key(L, inner);\
+make_lua_key(L, innerbinspacing);\
+make_lua_key(L, innerclosespacing);\
+make_lua_key(L, innerfracspacing);\
+make_lua_key(L, innerinnerspacing);\
+make_lua_key(L, innermiddlespacing);\
+make_lua_key(L, inneropenspacing);\
+make_lua_key(L, inneropspacing);\
+make_lua_key(L, innerordspacing);\
+make_lua_key(L, innerpunctspacing);\
+make_lua_key(L, innerradspacing);\
+make_lua_key(L, innerrelspacing);\
+make_lua_key(L, input);\
+make_lua_key(L, insert);\
+make_lua_key(L, insertheights);\
+make_lua_key(L, insertpenalties);\
+make_lua_key(L, instance);\
+make_lua_key(L, integer);\
+make_lua_key(L, interlinepenalties);\
+make_lua_key(L, interlinepenalty);\
+make_lua_key(L, intermathskip);\
+make_lua_key(L, internal_attribute);\
+make_lua_key(L, internal_attribute_reference);\
+make_lua_key(L, internal_box_reference);\
+make_lua_key(L, internal_dimen);\
+make_lua_key(L, internal_dimen_reference);\
+make_lua_key(L, internal_glue);\
+make_lua_key(L, internal_glue_reference);\
+make_lua_key(L, internal_int);\
+make_lua_key(L, internal_int_reference);\
+make_lua_key(L, internal_mu_glue);\
+make_lua_key(L, internal_mu_glue_reference);\
+make_lua_key(L, internal_toks);\
+make_lua_key(L, internal_toks_reference);\
+make_lua_key(L, internaldimension);\
+make_lua_key(L, internalinteger);\
+make_lua_key(L, internalgluespec);\
+make_lua_key(L, internalmugluespec);\
+make_lua_key(L, invalid_char);\
+make_lua_key(L, italic);\
+make_lua_key(L, italic_correction);\
+make_lua_key(L, italiccorrection);\
+make_lua_key(L, iterator_value);\
+make_lua_key(L, kern);\
+make_lua_key(L, kerns);\
+make_lua_key(L, noadstate);\
+make_lua_key(L, language);\
+make_lua_key(L, largechar);\
+make_lua_key(L, largefamily);\
+make_lua_key(L, last);\
+make_lua_key(L, lastinsert);\
+make_lua_key(L, lastlinefit);\
+make_lua_key(L, lazyligatures);\
+make_lua_key(L, leader);\
+make_lua_key(L, leaders);\
+make_lua_key(L, leastpagecost);\
+make_lua_key(L, left);\
+make_lua_key(L, left_brace);\
+make_lua_key(L, leftboundary);\
+make_lua_key(L, leftbox);\
+make_lua_key(L, leftboxwidth);\
+make_lua_key(L, lefthangskip);\
+make_lua_key(L, leftmargin);\
+make_lua_key(L, leftmarginkern);\
+make_lua_key(L, leftprotrusion);\
+make_lua_key(L, leftskip);\
+make_lua_key(L, lefttoright);\
+make_lua_key(L, legacy);\
+make_lua_key(L, let);\
+make_lua_key(L, letter);\
+make_lua_key(L, level);\
+make_lua_key(L, lhmin);\
+make_lua_key(L, ligature);\
+make_lua_key(L, ligatures);\
+make_lua_key(L, limitabovebgap);\
+make_lua_key(L, limitabovekern);\
+make_lua_key(L, limitabovevgap);\
+make_lua_key(L, limitbelowbgap);\
+make_lua_key(L, limitbelowkern);\
+make_lua_key(L, limitbelowvgap);\
+make_lua_key(L, limits);\
+make_lua_key(L, line);\
+make_lua_key(L, linebreakpenalty);\
+make_lua_key(L, linepenalty);\
+make_lua_key(L, lineskip);\
+make_lua_key(L, lineskiplimit);\
+make_lua_key(L, list);\
+make_lua_key(L, local);\
+make_lua_key(L, local_box);\
+make_lua_key(L, localbox);\
+make_lua_key(L, log);\
+make_lua_key(L, logfile);\
+make_lua_key(L, looseness);\
+make_lua_key(L, LowerLimitBaselineDropMin);\
+make_lua_key(L, LowerLimitGapMin);\
+make_lua_key(L, lua);\
+make_lua_key(L, lua_call);\
+make_lua_key(L, lua_function_call);\
+make_lua_key(L, lua_local_call);\
+make_lua_key(L, lua_protected_call);\
+make_lua_key(L, lua_value);\
+make_lua_key(L, luatex);\
+make_lua_key(L, macro);\
+make_lua_key(L, make_box);\
+make_lua_key(L, mark);\
+make_lua_key(L, match);\
+make_lua_key(L, math);\
+make_lua_key(L, mathspec);\
+make_lua_key(L, math_accent);\
+make_lua_key(L, math_char_given);\
+make_lua_key(L, math_char_number);\
+make_lua_key(L, math_char_xgiven);\
+make_lua_key(L, math_choice);\
+make_lua_key(L, math_component);\
+make_lua_key(L, math_fence);\
+make_lua_key(L, math_fraction);\
+make_lua_key(L, math_modifier);\
+make_lua_key(L, math_radical);\
+make_lua_key(L, math_script);\
+make_lua_key(L, math_shift);\
+make_lua_key(L, math_shift_cs);\
+make_lua_key(L, math_style);\
+make_lua_key(L, mathtextchar);\
+make_lua_key(L, mathchar);\
+make_lua_key(L, mathchoice);\
+make_lua_key(L, MathConstants);\
+make_lua_key(L, mathcontrol);\
+make_lua_key(L, mathdir);\
+make_lua_key(L, mathfence);\
+make_lua_key(L, mathfraction);\
+make_lua_key(L, mathkerns);\
+make_lua_key(L, MathLeading);\
+make_lua_key(L, mathoperator);\
+make_lua_key(L, mathpack);\
+make_lua_key(L, mathpostpenalty);\
+make_lua_key(L, mathprepenalty);\
+make_lua_key(L, mathshapekern);\
+make_lua_key(L, mathshift);\
+make_lua_key(L, mathsimple);\
+make_lua_key(L, mathskip);\
+make_lua_key(L, mathstyle);\
+make_lua_key(L, medmuskip);\
+make_lua_key(L, message);\
+make_lua_key(L, middle);\
+make_lua_key(L, middlebinspacing);\
+make_lua_key(L, middlebox);\
+make_lua_key(L, middleclosespacing);\
+make_lua_key(L, middlefracspacing);\
+make_lua_key(L, middleinnerspacing);\
+make_lua_key(L, middlemiddlespacing);\
+make_lua_key(L, middleopenspacing);\
+make_lua_key(L, middleopspacing);\
+make_lua_key(L, middleordspacing);\
+make_lua_key(L, middlepunctspacing);\
+make_lua_key(L, middleradspacing);\
+make_lua_key(L, middlerelspacing);\
+make_lua_key(L, MinConnectorOverlap);\
+make_lua_key(L, mkern);\
+make_lua_key(L, mode);\
+make_lua_key(L, modeline);\
+make_lua_key(L, modifier);\
+make_lua_key(L, move_tolerance);\
+make_lua_key(L, mrule);\
+make_lua_key(L, mskip);\
+make_lua_key(L, muglue);\
+make_lua_key(L, mugluespec);\
+make_lua_key(L, mutable);\
+make_lua_key(L, name);\
+make_lua_key(L, nestedlist);\
+make_lua_key(L, new);\
+make_lua_key(L, next);\
+make_lua_key(L, nil);\
+make_lua_key(L, no);\
+make_lua_key(L, no_expand);\
+make_lua_key(L, noad);\
+make_lua_key(L, noalign);\
+make_lua_key(L, noaligned);\
+make_lua_key(L, noaxis);\
+make_lua_key(L, nocheck);\
+make_lua_key(L, nooverflow);\
+make_lua_key(L, node);\
+make_lua_key(L, nodelist);\
+make_lua_key(L, noindent);\
+make_lua_key(L, nolimits);\
+make_lua_key(L, nolimitsubfactor);\
+make_lua_key(L, NoLimitSubFactor);\
+make_lua_key(L, nolimitsupfactor);\
+make_lua_key(L, NoLimitSupFactor);\
+make_lua_key(L, nomath);\
+make_lua_key(L, none);\
+make_lua_key(L, normal);\
+make_lua_key(L, norule);\
+make_lua_key(L, noruling);\
+make_lua_key(L, noscript);\
+make_lua_key(L, nosubprescript);\
+make_lua_key(L, nosubscript);\
+make_lua_key(L, nosuperprescript);\
+make_lua_key(L, nosuperscript);\
+make_lua_key(L, nucleus);\
+make_lua_key(L, number);\
+make_lua_key(L, numerator);\
+make_lua_key(L, numeratorvariant);\
+make_lua_key(L, oldmath);\
+make_lua_key(L, op);\
+make_lua_key(L, opbinspacing);\
+make_lua_key(L, opclosespacing);\
+make_lua_key(L, open);\
+make_lua_key(L, openbinspacing);\
+make_lua_key(L, openclosespacing);\
+make_lua_key(L, openfracspacing);\
+make_lua_key(L, openinnerspacing);\
+make_lua_key(L, openmiddlespacing);\
+make_lua_key(L, openopenspacing);\
+make_lua_key(L, openopspacing);\
+make_lua_key(L, openordspacing);\
+make_lua_key(L, openpunctspacing);\
+make_lua_key(L, openradspacing);\
+make_lua_key(L, openrelspacing);\
+make_lua_key(L, openupdepth);\
+make_lua_key(L, openupheight);\
+make_lua_key(L, operator);\
+make_lua_key(L, operatorsize);\
+make_lua_key(L, opfracspacing);\
+make_lua_key(L, opinnerspacing);\
+make_lua_key(L, opmiddlespacing);\
+make_lua_key(L, opopenspacing);\
+make_lua_key(L, opopspacing);\
+make_lua_key(L, opordspacing);\
+make_lua_key(L, oppunctspacing);\
+make_lua_key(L, opradspacing);\
+make_lua_key(L, oprelspacing);\
+make_lua_key(L, options);\
+make_lua_key(L, ord);\
+make_lua_key(L, ordbinspacing);\
+make_lua_key(L, ordclosespacing);\
+make_lua_key(L, ordfracspacing);\
+make_lua_key(L, ordinnerspacing);\
+make_lua_key(L, ordmiddlespacing);\
+make_lua_key(L, ordopenspacing);\
+make_lua_key(L, ordopspacing);\
+make_lua_key(L, ordordspacing);\
+make_lua_key(L, ordpunctspacing);\
+make_lua_key(L, ordradspacing);\
+make_lua_key(L, ordrelspacing);\
+make_lua_key(L, orientation);\
+make_lua_key(L, original);\
+make_lua_key(L, orphanpenalties);\
+make_lua_key(L, orphanpenalty);\
+make_lua_key(L, other_char);\
+make_lua_key(L, outline);\
+make_lua_key(L, output);\
+make_lua_key(L, over);\
+make_lua_key(L, OverbarExtraAscender);\
+make_lua_key(L, overbarkern);\
+make_lua_key(L, overbarrule);\
+make_lua_key(L, OverbarRuleThickness);\
+make_lua_key(L, OverbarVerticalGap);\
+make_lua_key(L, overbarvgap);\
+make_lua_key(L, overdelimiter);\
+make_lua_key(L, overdelimiterbgap);\
+make_lua_key(L, overdelimitervariant);\
+make_lua_key(L, overdelimitervgap);\
+make_lua_key(L, overlayaccent);\
+make_lua_key(L, overlayaccentvariant);\
+make_lua_key(L, overlinevariant);\
+make_lua_key(L, overloaded);\
+make_lua_key(L, page);\
+make_lua_key(L, package);\
+make_lua_key(L, pagediscardshead);\
+make_lua_key(L, pagehead);\
+make_lua_key(L, pageinserthead);\
+make_lua_key(L, Pages);\
+make_lua_key(L, pages);\
+make_lua_key(L, par);\
+make_lua_key(L, parameter);\
+make_lua_key(L, parameter_reference);\
+make_lua_key(L, parameters);\
+make_lua_key(L, parfillleftskip);\
+make_lua_key(L, parfillrightskip);\
+make_lua_key(L, parinitleftskip);\
+make_lua_key(L, parinitrightskip);\
+make_lua_key(L, parfillskip);\
+make_lua_key(L, parindent);\
+make_lua_key(L, parshape);\
+make_lua_key(L, parskip);\
+make_lua_key(L, passive);\
+make_lua_key(L, pdfe);\
+make_lua_key(L, penalty);\
+make_lua_key(L, permanent);\
+make_lua_key(L, permitall);\
+make_lua_key(L, permitglue);\
+make_lua_key(L, permitmathreplace);\
+make_lua_key(L, phantom);\
+make_lua_key(L, post);\
+make_lua_key(L, post_linebreak);\
+make_lua_key(L, postadjust);\
+make_lua_key(L, postadjusthead);\
+make_lua_key(L, postmigrate);\
+make_lua_key(L, postmigratehead);\
+make_lua_key(L, pre);\
+make_lua_key(L, pre_align);\
+make_lua_key(L, preadjust);\
+make_lua_key(L, preadjusthead);\
+make_lua_key(L, preamble);\
+make_lua_key(L, prebox);\
+make_lua_key(L, preferfontthickness);\
+make_lua_key(L, prefix);\
+make_lua_key(L, premigrate);\
+make_lua_key(L, premigratehead);\
+make_lua_key(L, prepost);\
+make_lua_key(L, preroll);\
+make_lua_key(L, presub);\
+make_lua_key(L, presubscriptshiftdistance);\
+make_lua_key(L, presup);\
+make_lua_key(L, presuperscriptshiftdistance);\
+make_lua_key(L, pretolerance);\
+make_lua_key(L, prev);\
+make_lua_key(L, prevdepth);\
+make_lua_key(L, prevgraf);\
+make_lua_key(L, prime);\
+make_lua_key(L, PrimeBaselineDropMax);\
+make_lua_key(L, primeraise);\
+make_lua_key(L, PrimeRaisePercent);\
+make_lua_key(L, primeraisecomposed);\
+make_lua_key(L, PrimeRaiseComposedPercent);\
+make_lua_key(L, primeshiftdrop);\
+make_lua_key(L, PrimeShiftUp);\
+make_lua_key(L, primeshiftup);\
+make_lua_key(L, PrimeShiftUpCramped);\
+make_lua_key(L, primespaceafter);\
+make_lua_key(L, PrimeSpaceAfter);\
+make_lua_key(L, primewidth);\
+make_lua_key(L, PrimeWidthPercent);\
+make_lua_key(L, primevariant);\
+make_lua_key(L, primitive);\
+make_lua_key(L, protected);\
+make_lua_key(L, protected_call);\
+make_lua_key(L, protrudechars);\
+make_lua_key(L, protrusion);\
+make_lua_key(L, properties);\
+make_lua_key(L, ptr);\
+make_lua_key(L, punct);\
+make_lua_key(L, punctbinspacing);\
+make_lua_key(L, punctclosespacing);\
+make_lua_key(L, punctfracspacing);\
+make_lua_key(L, punctinnerspacing);\
+make_lua_key(L, punctmiddlespacing);\
+make_lua_key(L, punctopenspacing);\
+make_lua_key(L, punctopspacing);\
+make_lua_key(L, punctordspacing);\
+make_lua_key(L, punctpunctspacing);\
+make_lua_key(L, punctradspacing);\
+make_lua_key(L, punctrelspacing);\
+make_lua_key(L, quad);\
+make_lua_key(L, radbinspacing);\
+make_lua_key(L, radclosespacing);\
+make_lua_key(L, radfracspacing);\
+make_lua_key(L, radical);\
+make_lua_key(L, radicaldegreeafter);\
+make_lua_key(L, radicaldegreebefore);\
+make_lua_key(L, radicalextensibleafter);\
+make_lua_key(L, radicalextensiblebefore);\
+make_lua_key(L, RadicalKernAfterExtensible);\
+make_lua_key(L, RadicalKernBeforeExtensible);\
+make_lua_key(L, RadicalDegreeBottomRaisePercent);\
+make_lua_key(L, radicaldegreeraise);\
+make_lua_key(L, RadicalDisplayStyleVerticalGap);\
+make_lua_key(L, RadicalExtraAscender);\
+make_lua_key(L, radicalkern);\
+make_lua_key(L, RadicalKernAfterDegree);\
+make_lua_key(L, RadicalKernBeforeDegree);\
+make_lua_key(L, radicalrule);\
+make_lua_key(L, RadicalRuleThickness);\
+make_lua_key(L, radicalvariant);\
+make_lua_key(L, RadicalVerticalGap);\
+make_lua_key(L, radicalvgap);\
+make_lua_key(L, radinnerspacing);\
+make_lua_key(L, radmiddlespacing);\
+make_lua_key(L, radopenspacing);\
+make_lua_key(L, radopspacing);\
+make_lua_key(L, radordspacing);\
+make_lua_key(L, radpunctspacing);\
+make_lua_key(L, radradspacing);\
+make_lua_key(L, radrelspacing);\
+make_lua_key(L, reader);\
+make_lua_key(L, register);\
+make_lua_key(L, register_attribute);\
+make_lua_key(L, register_attribute_reference);\
+make_lua_key(L, register_box);\
+make_lua_key(L, register_box_reference);\
+make_lua_key(L, register_dimen);\
+make_lua_key(L, register_dimen_reference);\
+make_lua_key(L, register_glue);\
+make_lua_key(L, register_glue_reference);\
+make_lua_key(L, register_int);\
+make_lua_key(L, register_int_reference);\
+make_lua_key(L, register_mu_glue);\
+make_lua_key(L, register_mu_glue_reference);\
+make_lua_key(L, register_toks);\
+make_lua_key(L, register_toks_reference);\
+make_lua_key(L, registerdimension);\
+make_lua_key(L, registerinteger);\
+make_lua_key(L, registergluespec);\
+make_lua_key(L, registermugluespec);\
+make_lua_key(L, regular);\
+make_lua_key(L, rel);\
+make_lua_key(L, relax);\
+make_lua_key(L, relbinspacing);\
+make_lua_key(L, relclosespacing);\
+make_lua_key(L, relfracspacing);\
+make_lua_key(L, relinnerspacing);\
+make_lua_key(L, relmiddlespacing);\
+make_lua_key(L, relopenspacing);\
+make_lua_key(L, relopspacing);\
+make_lua_key(L, relordspacing);\
+make_lua_key(L, relpunctspacing);\
+make_lua_key(L, relradspacing);\
+make_lua_key(L, relrelspacing);\
+make_lua_key(L, remove_item);\
+make_lua_key(L, repeat);\
+make_lua_key(L, replace);\
+make_lua_key(L, reserved);\
+make_lua_key(L, reset);\
+make_lua_key(L, rhmin);\
+make_lua_key(L, right);\
+make_lua_key(L, right_brace);\
+make_lua_key(L, rightboundary);\
+make_lua_key(L, rightbox);\
+make_lua_key(L, rightboxwidth);\
+make_lua_key(L, righthangskip);\
+make_lua_key(L, rightmargin);\
+make_lua_key(L, rightmarginkern);\
+make_lua_key(L, rightprotrusion);\
+make_lua_key(L, rightskip);\
+make_lua_key(L, righttoleft);\
+make_lua_key(L, root);\
+make_lua_key(L, rooted);\
+make_lua_key(L, rule);\
+make_lua_key(L, rulebasedmathskip);\
+make_lua_key(L, ruledepth);\
+make_lua_key(L, ruleheight);\
+make_lua_key(L, same);\
+make_lua_key(L, saved);\
+make_lua_key(L, scale);\
+make_lua_key(L, script);\
+make_lua_key(L, scriptorder);\
+make_lua_key(L, ScriptPercentScaleDown);\
+make_lua_key(L, scripts);\
+make_lua_key(L, scriptscale);\
+make_lua_key(L, scriptscript);\
+make_lua_key(L, ScriptScriptPercentScaleDown);\
+make_lua_key(L, scriptscriptscale);\
+make_lua_key(L, second);\
+make_lua_key(L, semisimple);\
+make_lua_key(L, set);\
+make_lua_key(L, set_auxiliary);\
+make_lua_key(L, set_box);\
+make_lua_key(L, set_box_property);\
+make_lua_key(L, set_font);\
+make_lua_key(L, set_font_property);\
+make_lua_key(L, set_interaction);\
+make_lua_key(L, set_mark);\
+make_lua_key(L, set_math_parameter);\
+make_lua_key(L, set_page_property);\
+make_lua_key(L, set_specification);\
+make_lua_key(L, shapingpenaltiesmode);\
+make_lua_key(L, shapingpenalty);\
+make_lua_key(L, shift);\
+make_lua_key(L, shiftedsubscript);\
+make_lua_key(L, shiftedsuperscript);\
+make_lua_key(L, shiftedsubprescript);\
+make_lua_key(L, shiftedsuperprescript);\
+make_lua_key(L, shorthand_def);\
+make_lua_key(L, shrink);\
+make_lua_key(L, shrinkorder);\
+make_lua_key(L, simple);\
+make_lua_key(L, size);\
+make_lua_key(L, skewchar);\
+make_lua_key(L, skeweddelimitertolerance);\
+make_lua_key(L, SkewedDelimiterTolerance);\
+make_lua_key(L, skewedfractionhgap);\
+make_lua_key(L, SkewedFractionHorizontalGap);\
+make_lua_key(L, SkewedFractionVerticalGap);\
+make_lua_key(L, skewedfractionvgap);\
+make_lua_key(L, skip);\
+make_lua_key(L, slant);\
+make_lua_key(L, small);\
+make_lua_key(L, smallchar);\
+make_lua_key(L, smaller);\
+make_lua_key(L, smallfamily);\
+make_lua_key(L, some_item);\
+make_lua_key(L, source);\
+make_lua_key(L, space);\
+make_lua_key(L, SpaceAfterScript);\
+make_lua_key(L, spaceafterscript);\
+make_lua_key(L, spacebeforescript);\
+make_lua_key(L, SpaceBeforeScript);\
+make_lua_key(L, spacefactor);\
+make_lua_key(L, spacer);\
+make_lua_key(L, spaceshrink);\
+make_lua_key(L, spaceskip);\
+make_lua_key(L, spacestretch);\
+make_lua_key(L, span);\
+make_lua_key(L, spec);\
+make_lua_key(L, specification);\
+make_lua_key(L, specification_reference);\
+make_lua_key(L, split);\
+make_lua_key(L, split_insert);\
+make_lua_key(L, splitbottom);\
+make_lua_key(L, splitdiscardshead);\
+make_lua_key(L, splitfirst);\
+make_lua_key(L, splitkeep);\
+make_lua_key(L, splitoff);\
+make_lua_key(L, splittopskip);\
+make_lua_key(L, stack);\
+make_lua_key(L, StackBottomDisplayStyleShiftDown);\
+make_lua_key(L, StackBottomShiftDown);\
+make_lua_key(L, stackdenomdown);\
+make_lua_key(L, StackDisplayStyleGapMin);\
+make_lua_key(L, StackGapMin);\
+make_lua_key(L, stacknumup);\
+make_lua_key(L, StackTopDisplayStyleShiftUp);\
+make_lua_key(L, StackTopShiftUp);\
+make_lua_key(L, stackvariant);\
+make_lua_key(L, stackvgap);\
+make_lua_key(L, start);\
+make_lua_key(L, state);\
+make_lua_key(L, step);\
+make_lua_key(L, stretch);\
+make_lua_key(L, stretchorder);\
+make_lua_key(L, StretchStackBottomShiftDown);\
+make_lua_key(L, StretchStackGapAboveMin);\
+make_lua_key(L, StretchStackGapBelowMin);\
+make_lua_key(L, StretchStackTopShiftUp);\
+make_lua_key(L, strictend);\
+make_lua_key(L, strictstart);\
+make_lua_key(L, string);\
+make_lua_key(L, strut);\
+make_lua_key(L, style);\
+make_lua_key(L, sub);\
+make_lua_key(L, subbox);\
+make_lua_key(L, submlist);\
+make_lua_key(L, subpre);\
+make_lua_key(L, subscript);\
+make_lua_key(L, SubscriptBaselineDropMin);\
+make_lua_key(L, subscriptshiftdistance);\
+make_lua_key(L, SubscriptShiftDown);\
+make_lua_key(L, SubscriptShiftDownWithSuperscript);\
+make_lua_key(L, SubscriptTopMax);\
+make_lua_key(L, subscriptvariant);\
+make_lua_key(L, subshiftdown);\
+make_lua_key(L, subshiftdrop);\
+make_lua_key(L, substitute);\
+make_lua_key(L, SubSuperscriptGapMin);\
+make_lua_key(L, subsupshiftdown);\
+make_lua_key(L, subsupvgap);\
+make_lua_key(L, subtopmax);\
+make_lua_key(L, subtype);\
+make_lua_key(L, sup);\
+make_lua_key(L, supbottommin);\
+make_lua_key(L, superscript);\
+make_lua_key(L, SuperscriptBaselineDropMax);\
+make_lua_key(L, SuperscriptBottomMaxWithSubscript);\
+make_lua_key(L, SuperscriptBottomMin);\
+make_lua_key(L, superscriptshiftdistance);\
+make_lua_key(L, SuperscriptShiftUp);\
+make_lua_key(L, SuperscriptShiftUpCramped);\
+make_lua_key(L, superscriptvariant);\
+make_lua_key(L, suppre);\
+make_lua_key(L, supshiftdrop);\
+make_lua_key(L, supshiftup);\
+make_lua_key(L, supsubbottommax);\
+make_lua_key(L, surround);\
+make_lua_key(L, syllable);\
+make_lua_key(L, tabskip);\
+make_lua_key(L, tail);\
+make_lua_key(L, target);\
+make_lua_key(L, temp);\
+make_lua_key(L, temphead);\
+make_lua_key(L, terminal);\
+make_lua_key(L, terminal_and_logfile);\
+make_lua_key(L, tex);\
+make_lua_key(L, tex_nest);\
+make_lua_key(L, text);\
+make_lua_key(L, textcontrol);\
+make_lua_key(L, textscale);\
+make_lua_key(L, the);\
+make_lua_key(L, thickmuskip);\
+make_lua_key(L, thinmuskip);\
+make_lua_key(L, tok);\
+make_lua_key(L, token);\
+make_lua_key(L, tokenlist);\
+make_lua_key(L, tolerance);\
+make_lua_key(L, tolerant);\
+make_lua_key(L, tolerant_call);\
+make_lua_key(L, tolerant_protected_call);\
+make_lua_key(L, top);\
+make_lua_key(L, topaccent);\
+make_lua_key(L, topaccentvariant);\
+make_lua_key(L, topleft);\
+make_lua_key(L, topmargin);\
+make_lua_key(L, topright);\
+make_lua_key(L, topskip);\
+make_lua_key(L, total);\
+make_lua_key(L, tracingparagraphs);\
+make_lua_key(L, trailer);\
+make_lua_key(L, Trailer);\
+make_lua_key(L, type);\
+make_lua_key(L, uchyph);\
+make_lua_key(L, uleaders);\
+make_lua_key(L, un_hbox);\
+make_lua_key(L, un_vbox);\
+make_lua_key(L, undefined_cs);\
+make_lua_key(L, under);\
+make_lua_key(L, UnderbarExtraDescender);\
+make_lua_key(L, underbarkern);\
+make_lua_key(L, underbarrule);\
+make_lua_key(L, UnderbarRuleThickness);\
+make_lua_key(L, UnderbarVerticalGap);\
+make_lua_key(L, underbarvgap);\
+make_lua_key(L, underdelimiter);\
+make_lua_key(L, underdelimiterbgap);\
+make_lua_key(L, underdelimitervariant);\
+make_lua_key(L, underdelimitervgap);\
+make_lua_key(L, underlinevariant);\
+make_lua_key(L, unhbox);\
+make_lua_key(L, unhyphenated);\
+make_lua_key(L, unknown);\
+make_lua_key(L, unpacklist);\
+make_lua_key(L, unrolllist);\
+make_lua_key(L, unset);\
+make_lua_key(L, untraced);\
+make_lua_key(L, unvbox);\
+make_lua_key(L, uppercase);\
+make_lua_key(L, UpperLimitBaselineRiseMin);\
+make_lua_key(L, UpperLimitGapMin);\
+make_lua_key(L, user);\
+make_lua_key(L, userkern);\
+make_lua_key(L, userpenalty);\
+make_lua_key(L, userskip);\
+make_lua_key(L, v);\
+make_lua_key(L, vadjust);\
+make_lua_key(L, valign);\
+make_lua_key(L, value);\
+make_lua_key(L, variable);\
+make_lua_key(L, vbox);\
+make_lua_key(L, vcenter);\
+make_lua_key(L, vdelimiter);\
+make_lua_key(L, vertical);\
+make_lua_key(L, verticalmathkern);\
+make_lua_key(L, vextensible);\
+make_lua_key(L, vextensiblevariant);\
+make_lua_key(L, vitalic);\
+make_lua_key(L, vlist);\
+make_lua_key(L, vmode);\
+make_lua_key(L, vmodepar);\
+make_lua_key(L, vmove);\
+make_lua_key(L, void);\
+make_lua_key(L, vrule);\
+make_lua_key(L, vskip);\
+make_lua_key(L, vtop);\
+make_lua_key(L, vparts);\
+make_lua_key(L, whatsit);\
+make_lua_key(L, widowpenalties);\
+make_lua_key(L, widowpenalty);\
+make_lua_key(L, width);\
+make_lua_key(L, woffset);\
+make_lua_key(L, word);\
+make_lua_key(L, wordpenalty);\
+make_lua_key(L, wrapup);\
+make_lua_key(L, xheight);\
+make_lua_key(L, xleaders);\
+make_lua_key(L, xoffset);\
+make_lua_key(L, xray);\
+make_lua_key(L, xscale);\
+make_lua_key(L, xspaceskip);\
+make_lua_key(L, yoffset);\
+make_lua_key(L, yscale);\
+make_lua_key(L, zerospaceskip);\
+/* */ \
+make_lua_key_alias(L, empty_string, "");\
+/* */ \
+make_lua_key_alias(L, node_instance, NODE_METATABLE_INSTANCE);\
+make_lua_key_alias(L, node_properties, NODE_PROPERTIES_DIRECT);\
+make_lua_key_alias(L, node_properties_indirect, NODE_PROPERTIES_INDIRECT);\
+/* */ \
+make_lua_key_alias(L, token_instance, TOKEN_METATABLE_INSTANCE);\
+make_lua_key_alias(L, token_package, TOKEN_METATABLE_PACKAGE);\
+/* */ \
+make_lua_key_alias(L, sparse_instance, SPARSE_METATABLE_INSTANCE);\
+/* */ \
+make_lua_key_alias(L, pdfe_instance, PDFE_METATABLE_INSTANCE);\
+make_lua_key_alias(L, pdfe_dictionary, PDFE_METATABLE_DICTIONARY);\
+make_lua_key_alias(L, pdfe_array, PDFE_METATABLE_ARRAY);\
+make_lua_key_alias(L, pdfe_stream, PDFE_METATABLE_STREAM);\
+make_lua_key_alias(L, pdfe_reference, PDFE_METATABLE_REFERENCE);\
+/* done */
+
+# define declare_metapost_lua_keys(L) \
+/* */\
+/* (L, close); */\
+make_lua_key(L, color);\
+make_lua_key(L, curl);\
+make_lua_key(L, curled);\
+make_lua_key(L, curved);\
+make_lua_key(L, cycle);\
+make_lua_key(L, dash);\
+make_lua_key(L, dashes);\
+/* (L, depth); */\
+make_lua_key(L, direction_x);\
+make_lua_key(L, direction_y);\
+make_lua_key(L, elliptical);\
+make_lua_key(L, end_cycle);\
+make_lua_key(L, endpoint);\
+make_lua_key(L, error);\
+make_lua_key(L, error_line);\
+/* (L, explicit); */\
+make_lua_key(L, extensions);\
+make_lua_key(L, fig);\
+/* (L, fill); */\
+make_lua_key(L, find_file);\
+/* (L, font); */\
+make_lua_key(L, given);\
+make_lua_key(L, halt_on_error);\
+make_lua_key(L, hash);\
+/* (L, height); */\
+make_lua_key(L, htap);\
+make_lua_key(L, interaction);\
+make_lua_key(L, internals);\
+make_lua_key(L, job_name);\
+make_lua_key(L, knots);\
+make_lua_key(L, left_curl);\
+make_lua_key(L, left_tension);\
+make_lua_key(L, left_type);\
+make_lua_key(L, left_x);\
+make_lua_key(L, left_y);\
+make_lua_key(L, linecap);\
+make_lua_key(L, linejoin);\
+make_lua_key(L, make_text);\
+make_lua_key(L, math_mode);\
+make_lua_key(L, memory);\
+make_lua_key(L, miterlimit);\
+make_lua_key(L, nodes);\
+make_lua_key(L, offset);\
+/* (L, open); */\
+make_lua_key(L, open_file);\
+/* (L, outline); */\
+make_lua_key(L, pairs);\
+make_lua_key(L, path);\
+make_lua_key(L, pen);\
+make_lua_key(L, postscript);\
+make_lua_key(L, prescript);\
+make_lua_key(L, print_line);\
+make_lua_key(L, random_seed);\
+/* (L, reader); */\
+make_lua_key(L, right_curl);\
+make_lua_key(L, right_tension);\
+make_lua_key(L, right_type);\
+make_lua_key(L, right_x);\
+make_lua_key(L, right_y);\
+make_lua_key(L, run_error);\
+make_lua_key(L, run_internal);\
+make_lua_key(L, run_logger);\
+make_lua_key(L, run_overload);\
+make_lua_key(L, run_script);\
+make_lua_key(L, run_warning);\
+make_lua_key(L, rx);\
+make_lua_key(L, ry);\
+make_lua_key(L, show_mode);\
+make_lua_key(L, stacking);\
+make_lua_key(L, start_bounds);\
+make_lua_key(L, start_clip);\
+make_lua_key(L, start_group);\
+make_lua_key(L, status);\
+make_lua_key(L, stop_bounds);\
+make_lua_key(L, stop_clip);\
+make_lua_key(L, stop_group);\
+make_lua_key(L, strings);\
+make_lua_key(L, sx);\
+make_lua_key(L, sy);\
+make_lua_key(L, symbols);\
+/* (L, text); */\
+make_lua_key(L, text_mode);\
+make_lua_key(L, tokens);\
+make_lua_key(L, transform);\
+make_lua_key(L, tx);\
+make_lua_key(L, ty);\
+/* (L, type); */\
+make_lua_key(L, utf8_mode);\
+make_lua_key(L, warning);\
+/* (L, width); */\
+make_lua_key(L, writer);\
+make_lua_key(L, x_coord);\
+make_lua_key(L, y_coord);\
+/* */\
+make_lua_key_alias(L, mplib_instance, MP_METATABLE_INSTANCE);\
+make_lua_key_alias(L, mplib_figure, MP_METATABLE_FIGURE);\
+make_lua_key_alias(L, mplib_object, MP_METATABLE_OBJECT);\
+/* done */
+
+/*tex
+ We want them properly aligned so we put pointers and indices in blocks.
+*/
+
+typedef struct lmt_keys {
+# undef make_lua_key
+# undef make_lua_key_alias
+# define make_lua_key make_lua_key_ptr
+# define make_lua_key_alias make_lua_key_ptr_alias
+declare_shared_lua_keys(NULL)
+declare_metapost_lua_keys(NULL)
+# undef make_lua_key
+# undef make_lua_key_alias
+# define make_lua_key make_lua_key_idx
+# define make_lua_key_alias make_lua_key_idx_alias
+declare_shared_lua_keys(NULL)
+declare_metapost_lua_keys(NULL)
+# undef make_lua_key
+# undef make_lua_key_alias
+} lmt_keys_info ;
+
+extern lmt_keys_info lmt_keys;
+
+# define make_lua_key init_lua_key
+# define make_lua_key_alias init_lua_key_alias
+
+# define lmt_initialize_shared_keys declare_shared_lua_keys
+# define lmt_initialize_metapost_keys declare_metapost_lua_keys
+
+/*tex
+
+ We use round from |math.h| because when in a macro we check for sign we (depending on
+ optimization) can fetch numbers multiple times. I need to measure this a bit more as inlining
+ looks a bit faster on for instance |experiment.tex| but of course the bin becomes (some 10K)
+ larger.
+
+*/
+
+//define lmt_rounded(d) (lua_Integer) (round(d))
+//define lmt_roundedfloat(f) (lua_Integer) (round((double) f))
+
+# define lmt_rounded(d) (lua_Integer) (llround(d))
+# define lmt_roundedfloat(f) (lua_Integer) (llround((double) f))
+
+
+# define lmt_tolong(L,i) (long) lua_tointeger(L,i)
+# define lmt_checklong(L,i) (long) luaL_checkinteger(L,i)
+# define lmt_optlong(L,i,j) (long) luaL_optinteger(L,i,j)
+
+# define lmt_tointeger(L,i) (int) lua_tointeger(L,i)
+# define lmt_checkinteger(L,i) (int) luaL_checkinteger(L,i)
+# define lmt_optinteger(L,i,j) (int) luaL_optinteger(L,i,j)
+
+# define lmt_tosizet(L,i) (size_t) lua_tointeger(L,i)
+# define lmt_checksizet(L,i) (size_t) luaL_checkinteger(L,i)
+# define lmt_optsizet(L,i,j) (size_t) luaL_optinteger(L,i,j)
+
+# define lmt_tohalfword(L,i) (halfword) lua_tointeger(L,i)
+# define lmt_checkhalfword(L,i) (halfword) luaL_checkinteger(L,i)
+# define lmt_opthalfword(L,i,j) (halfword) luaL_optinteger(L,i,j)
+
+# define lmt_toscaled(L,i) (scaled) lua_tointeger(L,i)
+# define lmt_checkscaled(L,i) (scaled) luaL_checkinteger(L,i)
+# define lmt_optscaled(L,i,j) (scaled) luaL_optinteger(L,i,j)
+
+# define lmt_toquarterword(L,i) (quarterword) lua_tointeger(L,i)
+# define lmt_checkquarterword(L,i) (quarterword) luaL_checkinteger(L,i)
+# define lmt_optquarterword(L,i,j) (quarterword) luaL_optinteger(L,i,j)
+
+# define lmt_tosingleword(L,i) (singleword) lua_tointeger(L,i)
+# define lmt_checksingleword(L,i) (singleword) luaL_checkinteger(L,i)
+# define lmt_optsingleword(L,i,j) (singleword) luaL_optinteger(L,i,j)
+
+# undef lround
+# include <math.h>
+
+inline static int lmt_roundnumber(lua_State *L, int i)
+{
+ double n = lua_tonumber(L, i);
+ return n == 0.0 ? 0 : lround(n);
+}
+
+inline static int lmt_optroundnumber(lua_State *L, int i, int dflt)
+{
+ double n = luaL_optnumber(L, i, dflt);
+ return n == 0.0 ? 0 : lround(n);
+}
+
+inline static unsigned int lmt_uroundnumber(lua_State *L, int i)
+{
+ double n = lua_tonumber(L, i);
+ return n == 0.0 ? 0 : (unsigned int) lround(n);
+}
+
+inline static double lmt_number_from_table(lua_State *L, int i, int j, lua_Number d)
+{
+ double n;
+ lua_rawgeti(L, i, j);
+ n = luaL_optnumber(L, -1, d);
+ lua_pop(L, 1);
+ return n;
+}
+
+extern void lmt_initialize_interface(void);
+
+# define lmt_toroundnumber lmt_roundnumber
+# define lmt_touroundnumber lmt_uroundnumber
+
+/*
+# define lua_set_string_by_key(L,a,b) \
+ lua_pushstring(L, b ? b : ""); \
+ lua_setfield(L, -2, a);
+
+# define lua_set_string_by_index(L,a,b) \
+ lua_pushstring(L, b ? b : ""); \
+ lua_rawseti(L, -2, a);
+
+# define lua_set_integer_by_key(L,a,b) \
+ lua_pushinteger(L, b); \
+ lua_setfield(L, -2, a);
+
+# define lua_set_integer_by_index(L,a,b) \
+ lua_pushinteger(L, b); \
+ lua_rawseti(L, -2, a);
+
+# define lua_set_boolean_by_key(L,a,b) \
+ lua_pushboolean(L, b); \
+ lua_setfield(L, -2, a);
+
+# define lua_set_boolean_by_index(L,a,b) \
+ lua_pushboolean(L, b); \
+ lua_rawseti(L, -2, a);
+*/
+
+inline static void lua_set_string_by_key(lua_State *L, const char *a, const char *b)
+{
+ lua_pushstring(L, b ? b : "");
+ lua_setfield(L, -2, a);
+}
+
+inline static void lua_set_string_by_index(lua_State *L, int a, const char *b)
+{
+ lua_pushstring(L, b ? b : "");
+ lua_rawseti(L, -2, a);
+}
+
+inline static void lua_set_integer_by_key(lua_State *L, const char *a, int b)
+{
+ lua_pushinteger(L, b);
+ lua_setfield(L, -2, a);
+}
+
+inline static void lua_set_integer_by_index(lua_State *L, int a, int b)
+{
+ lua_pushinteger(L, b);
+ lua_rawseti(L, -2, a);
+}
+
+inline static void lua_set_cardinal_by_key(lua_State *L, const char *a, unsigned b)
+{
+ lua_pushinteger(L, b);
+ lua_setfield(L, -2, a);
+}
+
+inline static void lua_set_cardinal_by_index(lua_State *L, int a, unsigned b)
+{
+ lua_pushinteger(L, b);
+ lua_rawseti(L, -2, a);
+}
+
+inline static void lua_set_boolean_by_key(lua_State *L, const char *a, int b)
+{
+ lua_pushboolean(L, b);
+ lua_setfield(L, -2, a);
+}
+
+inline static void lua_set_boolean_by_index(lua_State *L, int a, int b)
+{
+ lua_pushboolean(L, b);
+ lua_rawseti(L, -2, a);
+}
+
+inline void lmt_string_to_buffer(const char *str)
+{
+ luaL_addstring(lmt_lua_state.used_buffer, str);
+}
+
+inline void lmt_char_to_buffer(char c)
+{
+ luaL_addchar(lmt_lua_state.used_buffer, c);
+}
+
+inline void lmt_newline_to_buffer(void)
+{
+ luaL_addchar(lmt_lua_state.used_buffer, '\n');
+}
+
+# endif
diff --git a/source/luametatex/source/lua/lmtlanguagelib.c b/source/luametatex/source/lua/lmtlanguagelib.c
new file mode 100644
index 000000000..55646e3ef
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlanguagelib.c
@@ -0,0 +1,439 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This is the interface to everything that relates to hyphenation in the frontend: defining
+ a new language, setting properties for hyphenation, loading patterns and exceptions.
+
+*/
+
+# include "luametatex.h"
+
+# define LANGUAGE_METATABLE "luatex.language"
+# define LANGUAGE_FUNCTIONS "luatex.language.wordhandlers"
+
+/* todo: get rid of top */
+
+typedef struct languagelib_language {
+ tex_language *lang;
+} languagelib_language;
+
+static int languagelib_new(lua_State *L)
+{
+ languagelib_language *ulang = lua_newuserdatauv(L, sizeof(tex_language *), 0);
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ halfword lualang = lmt_tohalfword(L, 1);
+ ulang->lang = tex_get_language(lualang);
+ if (! ulang->lang) {
+ return luaL_error(L, "undefined language %d", lualang);
+ }
+ } else {
+ ulang->lang = tex_new_language(-1);
+ if (! ulang->lang) {
+ return luaL_error(L, "no room for a new language");
+ }
+ }
+ luaL_getmetatable(L, LANGUAGE_METATABLE);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+static tex_language *languagelib_object(lua_State* L)
+{
+ tex_language *lang = NULL;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ lang = tex_get_language(lmt_tohalfword(L, 1));
+ break;
+ case LUA_TUSERDATA:
+ {
+ languagelib_language *ulang = lua_touserdata(L, 1);
+ if (ulang && lua_getmetatable(L, 1)) {
+ luaL_getmetatable(L, LANGUAGE_METATABLE);
+ if (lua_rawequal(L, -1, -2)) {
+ lang = ulang->lang;
+ }
+ lua_pop(L, 2);
+ }
+ break;
+ }
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 1)) {
+ lang = tex_get_language(language_par);
+ }
+ break;
+ }
+ if (! lang) {
+ luaL_error(L, "argument should be a valid language id, language object, or true");
+ }
+ return lang;
+}
+
+static int languagelib_id(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ lua_pushinteger(L, lang->id);
+ return 1;
+}
+
+static int languagelib_patterns(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ if (lang->patterns) {
+ lua_pushstring(L, (char *) hnj_dictionary_tostring(lang->patterns));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TSTRING) {
+ tex_load_patterns(lang, (const unsigned char *) lua_tostring(L, 2));
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a string");
+ }
+}
+
+static int languagelib_clear_patterns(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ tex_clear_patterns(lang);
+ return 0;
+}
+
+static int languagelib_hyphenation(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ if (lang->exceptions) {
+ luaL_Buffer b;
+ int done = 0;
+ luaL_buffinit(L, &b);
+ if (lua_rawgeti(L, LUA_REGISTRYINDEX, lang->exceptions) == LUA_TTABLE) {
+ lua_pushnil(L);
+ while (lua_next(L, -2)) {
+ if (done) {
+ luaL_addlstring(&b, " ", 1);
+ } else {
+ done = 1;
+ }
+ luaL_addvalue(&b);
+ }
+ }
+ luaL_pushresult(&b);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TSTRING) {
+ tex_load_hyphenation(lang, (const unsigned char *) lua_tostring(L, 2));
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a string");
+ }
+}
+
+static int languagelib_pre_hyphen_char(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ lua_pushinteger(L, lang->pre_hyphen_char);
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TNUMBER) {
+ lang->pre_hyphen_char = lmt_tohalfword(L, 2);
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+ return 0;
+}
+
+static int languagelib_post_hyphen_char(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ lua_pushinteger(L, lang->post_hyphen_char);
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TNUMBER) {
+ lang->post_hyphen_char = lmt_tohalfword(L, 2);
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+ return 0;
+}
+
+static int languagelib_pre_exhyphen_char(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ lua_pushinteger(L, lang->pre_exhyphen_char);
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TNUMBER) {
+ lang->pre_exhyphen_char = lmt_tohalfword(L, 2);
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+}
+
+/* We push nuts! */
+
+int lmt_handle_word(tex_language *lang, const char *original, const char *word, int length, halfword first, halfword last, char **replacement)
+{
+ if (lang->wordhandler && word && first && last) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int stacktop = lua_gettop(L);
+ int result = 0;
+ int res;
+ *replacement = NULL;
+ lua_pushcfunction(L, lmt_traceback); /* goes before function */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_language_state.handler_table_id);
+ lua_rawgeti(L, -1, lang->id);
+ lua_pushinteger(L, lang->id);
+ lua_pushstring(L, original);
+ lua_pushstring(L, word);
+ lua_pushinteger(L, length);
+ lua_pushinteger(L, first);
+ lua_pushinteger(L, last);
+ res = lua_pcall(L, 6, 1, 0);
+ if (res) {
+ lua_remove(L, stacktop + 1);
+ lmt_error(L, "function call", -1, res == LUA_ERRRUN ? 0 : 1);
+ }
+ ++lmt_language_state.handler_count;
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ *replacement = (char *) lmt_memory_strdup(lua_tostring(L, -1));
+ break;
+ case LUA_TNUMBER:
+ result = lmt_tointeger(L, -1);
+ break;
+ default:
+ break;
+ }
+ lua_settop(L, stacktop);
+ return result;
+ }
+ return 0;
+}
+
+void lmt_initialize_languages(void)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ lua_newtable(L);
+ lmt_language_state.handler_table_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_pushstring(L, LANGUAGE_FUNCTIONS);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_language_state.handler_table_id);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+static int languagelib_setwordhandler(lua_State* L)
+{
+ tex_language *lang = languagelib_object(L);
+ switch (lua_type(L, 2)) {
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ goto DEFAULT;
+ } else {
+ // fall-through
+ }
+ case LUA_TNIL:
+ {
+ if (lang->wordhandler) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_language_state.handler_table_id);
+ lua_pushnil(L);
+ lua_rawseti(L, -2, lang->id);
+ lang->wordhandler = 0;
+ }
+ break;
+ }
+ case LUA_TFUNCTION:
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_language_state.handler_table_id);
+ lua_pushvalue(L, 2);
+ lua_rawseti(L, -2, lang->id);
+ lang->wordhandler = 1;
+ break;
+ }
+ default:
+ DEFAULT:
+ return luaL_error(L, "argument should be a function, false or nil");
+ }
+ return 0;
+}
+
+static int languagelib_sethjcode(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ halfword i = lmt_tohalfword(L, 2) ;
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ tex_set_hj_code(lang->id, i, lmt_tohalfword(L, 3), -1);
+ } else {
+ tex_set_hj_code(lang->id, i, i, -1);
+ }
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+}
+
+static int languagelib_gethjcode(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_pushinteger(L, tex_get_hj_code(lang->id, lmt_tohalfword(L, 2)));
+ return 1;
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+}
+
+static int languagelib_post_exhyphen_char(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ lua_pushinteger(L, lang->post_exhyphen_char);
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TNUMBER) {
+ lang->post_exhyphen_char = lmt_tohalfword(L, 2);
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a character number");
+ }
+}
+
+static int languagelib_hyphenation_min(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ if (lua_gettop(L) == 1) {
+ lua_pushinteger(L, lang->hyphenation_min);
+ return 1;
+ } else if (lua_type(L, 2) == LUA_TNUMBER) {
+ lang->hyphenation_min = lmt_tohalfword(L, 2);
+ return 0;
+ } else {
+ return luaL_error(L, "argument should be a number");
+ }
+}
+
+static int languagelib_clear_hyphenation(lua_State *L)
+{
+ tex_language *lang = languagelib_object(L);
+ tex_clear_hyphenation(lang);
+ return 0;
+}
+
+static int languagelib_clean(lua_State *L)
+{
+ char *cleaned = NULL;
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ tex_clean_hyphenation(cur_lang_par, lua_tostring(L, 1), &cleaned);
+ } else {
+ tex_language *lang = languagelib_object(L);
+ if (lang) {
+ if (lua_type(L, 2) == LUA_TSTRING) {
+ tex_clean_hyphenation(lang->id, lua_tostring(L, 2), &cleaned);
+ } else {
+ return luaL_error(L, "second argument should be a string");
+ }
+ } else {
+ return luaL_error(L, "first argument should be a string or language");
+ }
+ }
+ lua_pushstring(L, cleaned);
+ lmt_memory_free(cleaned);
+ return 1;
+}
+
+static int languagelib_hyphenate(lua_State *L)
+{
+ halfword h = lmt_check_isnode(L, 1);
+ halfword t = null;
+ if (lua_isuserdata(L, 2)) {
+ t = lmt_check_isnode(L, 2);
+ }
+ if (! t) {
+ t = h;
+ while (node_next(t)) {
+ t = node_next(t);
+ }
+ }
+ tex_hyphenate_list(h, t);
+ lmt_push_node_fast(L, h);
+ lmt_push_node_fast(L, t);
+ lua_pushboolean(L, 1);
+ return 3;
+}
+
+static int languagelib_current(lua_State *L)
+{
+ lua_pushinteger(L, language_par);
+ return 1;
+}
+
+static int languagelib_has_language(lua_State *L)
+{
+ halfword h = lmt_check_isnode(L, 1);
+ while (h) {
+ if (node_type(h) == glyph_node && get_glyph_language(h) > 0) {
+ lua_pushboolean(L, 1);
+ return 1;
+ } else {
+ h = node_next(h);
+ }
+ }
+ lua_pushboolean(L,0);
+ return 1;
+}
+
+static const struct luaL_Reg langlib_metatable[] = {
+ { "clearpatterns", languagelib_clear_patterns },
+ { "clearhyphenation", languagelib_clear_hyphenation },
+ { "patterns", languagelib_patterns },
+ { "hyphenation", languagelib_hyphenation },
+ { "prehyphenchar", languagelib_pre_hyphen_char },
+ { "posthyphenchar", languagelib_post_hyphen_char },
+ { "preexhyphenchar", languagelib_pre_exhyphen_char },
+ { "postexhyphenchar", languagelib_post_exhyphen_char },
+ { "hyphenationmin", languagelib_hyphenation_min },
+ { "sethjcode", languagelib_sethjcode },
+ { "gethjcode", languagelib_gethjcode },
+ { "setwordhandler", languagelib_setwordhandler },
+ { "id", languagelib_id },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg langlib_function_list[] = {
+ { "clearpatterns", languagelib_clear_patterns },
+ { "clearhyphenation", languagelib_clear_hyphenation },
+ { "patterns", languagelib_patterns },
+ { "hyphenation", languagelib_hyphenation },
+ { "prehyphenchar", languagelib_pre_hyphen_char },
+ { "posthyphenchar", languagelib_post_hyphen_char },
+ { "preexhyphenchar", languagelib_pre_exhyphen_char },
+ { "postexhyphenchar", languagelib_post_exhyphen_char },
+ { "hyphenationmin", languagelib_hyphenation_min },
+ { "sethjcode", languagelib_sethjcode },
+ { "gethjcode", languagelib_gethjcode },
+ { "setwordhandler", languagelib_setwordhandler },
+ { "id", languagelib_id },
+ { "clean", languagelib_clean }, /* maybe obsolete */
+ { "has_language", languagelib_has_language },
+ { "hyphenate", languagelib_hyphenate },
+ { "current", languagelib_current },
+ { "new", languagelib_new },
+ { NULL, NULL },
+};
+
+int luaopen_language(lua_State *L)
+{
+ luaL_newmetatable(L, LANGUAGE_METATABLE);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, langlib_metatable, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, langlib_function_list, 0);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtlanguagelib.h b/source/luametatex/source/lua/lmtlanguagelib.h
new file mode 100644
index 000000000..970ea6af5
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlanguagelib.h
@@ -0,0 +1,20 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LLANGUAGELIB_H
+# define LLANGUAGELIB_H
+
+extern void lmt_initialize_languages (void);
+
+extern int lmt_handle_word (
+ tex_language *lang,
+ const char *original,
+ const char *word,
+ int length,
+ halfword first,
+ halfword last,
+ char **replacement
+);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtlibrary.c b/source/luametatex/source/lua/lmtlibrary.c
new file mode 100644
index 000000000..ff6822a02
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlibrary.c
@@ -0,0 +1,106 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+
+/*tex
+
+ There is not much here. We only implement a mechanism for storing optional libraries. The
+ engine is self contained and doesn't depend on large and complex libraries. One can (try to)
+ load libraries at runtime. The optional ones that come with the engine end up in the
+ |optional| namespace.
+
+*/
+
+# include "luametatex.h"
+
+void lmt_library_initialize(lua_State *L)
+{
+ lua_getglobal(L,"optional");
+ if (! lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_setglobal(L, "optional");
+ } else {
+ lua_pop(L, 1);
+ }
+}
+
+void lmt_library_register(lua_State *L, const char *name, luaL_Reg functions[])
+{
+ lmt_library_initialize(L);
+ lua_getglobal(L, "optional");
+ lua_pushstring(L, name);
+ lua_newtable(L);
+ luaL_setfuncs(L, functions, 0);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+}
+
+lmt_library lmt_library_load(const char *filename)
+{
+ lmt_library lib = { .lib = NULL };
+ if (filename && strlen(filename)) {
+ lib.lib = lmt_library_open_indeed(filename);
+ lib.okay = lib.lib != NULL;
+ if (! lib.okay) {
+ tex_formatted_error("lmt library", "unable to load '%s', quitting\n", filename);
+ }
+ }
+ return lib;
+}
+
+lmt_library_function lmt_library_find(lmt_library lib, const char *source)
+{
+ if (lib.lib && lib.okay) {
+ lmt_library_function target = lmt_library_find_indeed(lib.lib, source);
+ if (target) {
+ return target;
+ } else {
+ lib.okay = 0;
+ tex_formatted_error("lmt library", "unable to locate '%s', quitting\n", source);
+ }
+ }
+ return NULL;
+}
+
+int lmt_library_okay(lmt_library lib)
+{
+ return lib.lib && lib.okay;
+};
+
+/* experiment */
+
+int librarylib_load(lua_State *L)
+{
+ /* So we permit it in mtxrun (for now, when we test). */
+ if (lmt_engine_state.lua_only || lmt_engine_state.permit_loadlib) {
+ const char *filename = lua_tostring(L, 1);
+ const char *openname = lua_tostring(L, 2);
+ if (filename && openname) {
+ lmt_library lib = lmt_library_load(filename);
+ if (lmt_library_okay(lib)) {
+ lua_CFunction target = lmt_library_find_indeed(lib.lib, openname);
+ if (target) {
+ lua_pushcfunction(L, target);
+ lua_pushstring(L, filename);
+ return 2;
+ }
+ }
+ }
+ } else {
+ tex_formatted_error("lmt library", "loading is not permitted, quitting\n");
+ }
+ return 0;
+};
+
+static struct luaL_Reg librarylib_function_list[] = {
+ { "load", librarylib_load },
+ { NULL, NULL },
+};
+
+int luaopen_library(lua_State * L)
+{
+ lmt_library_register(L, "library", librarylib_function_list);
+ return 0;
+}
diff --git a/source/luametatex/source/lua/lmtlibrary.h b/source/luametatex/source/lua/lmtlibrary.h
new file mode 100644
index 000000000..40f5f47f4
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlibrary.h
@@ -0,0 +1,60 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LLIBRARY_H
+# define LMT_LLIBRARY_H
+
+/*tex
+
+ The normal \LUA\ library loader uses the same calls as below. After loading the initializer is
+ looked up and called but here we use that method for locating more functions.
+
+ -- anonymous cast: void(*)(void)
+
+*/
+
+/* Do we need LoadLibraryW here or are we never utf/wide? */
+
+/* void : dlclose(lib) | string: dlerror() */
+
+typedef void (*lmt_library_function);
+
+# ifdef _WIN32
+
+ # include <windows.h>
+
+ typedef struct lmt_library {
+ HMODULE lib;
+ int okay;
+ int padding;
+ } lmt_library;
+
+ # define lmt_library_open_indeed(filename) LoadLibraryExA(filename, NULL, 0)
+ # define lmt_library_close_indeed(lib) FreeLibrary((HMODULE) lib)
+ # define lmt_library_find_indeed(lib,source) (void *) GetProcAddress((HMODULE) lib, source)
+
+# else
+
+ # include <dlfcn.h>
+
+ typedef struct lmt_library {
+ void *lib;
+ int okay;
+ int padding;
+ } lmt_library;
+
+ # define lmt_library_open_indeed(filename) dlopen(filename, RTLD_NOW | RTLD_LOCAL)
+ # define lmt_library_close_indeed(lib) dlclose(lib)
+ # define lmt_library_find_indeed(lib,source) (void *) dlsym(lib, source)
+
+# endif
+
+extern void lmt_library_register (lua_State *L, const char *name, luaL_Reg functions[]);
+extern void lmt_library_initialize (lua_State *L);
+
+extern lmt_library lmt_library_load (const char *filename);
+extern lmt_library_function lmt_library_find (lmt_library lib, const char *source);
+extern int lmt_library_okay (lmt_library lib);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtluaclib.c b/source/luametatex/source/lua/lmtluaclib.c
new file mode 100644
index 000000000..797585aba
--- /dev/null
+++ b/source/luametatex/source/lua/lmtluaclib.c
@@ -0,0 +1,660 @@
+/*
+ See license.txt in the root of this project. Most code here is from the luac program by the
+ official Lua project developers.
+*/
+
+# include "luametatex.h"
+
+/*
+
+ This is a slightly adapted version of luac which is not in the library but a separate program.
+ We keep a copy around in order to check changes. The version below doesn't load files nor saves
+ one. It is derived from:
+
+ $Id: luac.c $
+ Lua compiler (saves bytecodes to files; also lists bytecodes)
+ See Copyright Notice in lua.h
+
+ I added this helper because I wanted to look to what extend constants were resolved beforehand
+ but in the end that was seldom the case because we get them from tables and that bit of code is
+ not resolved at bytecode compile time (so in the end macros made more sense, although the gain
+ is very little).
+
+ I considered replacing the print with writing to a buffer so that we can deal with it later but
+ it is not worth the effort.
+
+*/
+
+# include "ldebug.h"
+# include "lopcodes.h"
+# include "lopnames.h"
+
+static TString **tmname = NULL;
+
+# define toproto(L,i) getproto(s2v(L->top+(i)))
+# define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
+# define LUACVOID(p) ((const void*)(p))
+# define eventname(i) (getstr(tmname[i]))
+
+static void luaclib_aux_print_string(const TString* ts)
+{
+ const char* s = getstr(ts);
+ size_t n = tsslen(ts);
+ printf("\"");
+ for (size_t i = 0; i < n; i++) {
+ int c = (int) (unsigned char) s[i];
+ switch (c) {
+ case '"':
+ printf("\\\"");
+ break;
+ case '\\':
+ printf("\\\\");
+ break;
+ case '\a':
+ printf("\\a");
+ break;
+ case '\b':
+ printf("\\b");
+ break;
+ case '\f':
+ printf("\\f");
+ break;
+ case '\n':
+ printf("\\n");
+ break;
+ case '\r':
+ printf("\\r");
+ break;
+ case '\t':
+ printf("\\t");
+ break;
+ case '\v':
+ printf("\\v");
+ break;
+ default:
+ printf(isprint(c) ? "%c" : "\\%03d", c);
+ break;
+ }
+ }
+ printf("\"");
+}
+
+static void PrintType(const Proto* f, int i)
+{
+ const TValue* o = &f->k[i];
+ switch (ttypetag(o)) {
+ case LUA_VNIL:
+ printf("N");
+ break;
+ case LUA_VFALSE:
+ case LUA_VTRUE:
+ printf("B");
+ break;
+ case LUA_VNUMFLT:
+ printf("F");
+ break;
+ case LUA_VNUMINT:
+ printf("I");
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ printf("S");
+ break;
+ default:
+ /* cannot happen */
+ printf("?%d", ttypetag(o));
+ break;
+ }
+ printf("\t");
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o = &f->k[i];
+ switch (ttypetag(o)) {
+ case LUA_VNIL:
+ printf("nil");
+ break;
+ case LUA_VFALSE:
+ printf("false");
+ break;
+ case LUA_VTRUE:
+ printf("true");
+ break;
+ case LUA_VNUMFLT:
+ {
+ char buff[100];
+ sprintf(buff,"%.14g", fltvalue(o)); /* LUA_NUMBER_FMT */
+ printf("%s", buff);
+ if (buff[strspn(buff, "-0123456789")] == '\0') {
+ printf(".0");
+ }
+ break;
+ }
+ case LUA_VNUMINT:
+# if defined(__MINGW64__) || defined(__MINGW32__)
+ printf("%I64i", ivalue(o)); /* LUA_INTEGER_FMT */
+# else
+ printf("%lli", ivalue(o)); /* LUA_INTEGER_FMT */
+# endif
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ luaclib_aux_print_string(tsvalue(o));
+ break;
+ default:
+ /* cannot happen */
+ printf("?%d", ttypetag(o));
+ break;
+ }
+}
+
+#define COMMENT "\t; "
+#define EXTRAARG GETARG_Ax(code[pc+1])
+#define EXTRAARGC (EXTRAARG*(MAXARG_C+1))
+#define ISK (isk ? "k" : "")
+
+static void luaclib_aux_print_code(const Proto* f)
+{
+ const Instruction* code = f->code;
+ int n = f->sizecode;
+ for (int pc = 0; pc < n; pc++) {
+ Instruction i = code[pc];
+ OpCode o = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int b = GETARG_B(i);
+ int c = GETARG_C(i);
+ int ax = GETARG_Ax(i);
+ int bx = GETARG_Bx(i);
+ int sb = GETARG_sB(i);
+ int sc = GETARG_sC(i);
+ int sbx = GETARG_sBx(i);
+ int isk = GETARG_k(i);
+ int line = luaG_getfuncline(f, pc);
+ printf("\t%d\t", pc + 1);
+ if (line > 0) {
+ printf("[%d]\t", line);
+ } else {
+ printf("[-]\t");
+ }
+ printf("%-9s\t", opnames[o]);
+ switch (o) {
+ case OP_MOVE:
+ printf("%d %d", a, b);
+ break;
+ case OP_LOADI:
+ printf("%d %d", a, sbx);
+ break;
+ case OP_LOADF:
+ printf("%d %d", a, sbx);
+ break;
+ case OP_LOADK:
+ printf("%d %d", a, bx);
+ printf(COMMENT);
+ PrintConstant(f, bx);
+ break;
+ case OP_LOADKX:
+ printf("%d", a);
+ printf(COMMENT);
+ PrintConstant(f, EXTRAARG);
+ break;
+ case OP_LOADFALSE:
+ printf("%d", a);
+ break;
+ case OP_LFALSESKIP:
+ printf("%d", a);
+ break;
+ case OP_LOADTRUE:
+ printf("%d", a);
+ break;
+ case OP_LOADNIL:
+ printf("%d %d", a, b);
+ printf(COMMENT "%d out", b + 1);
+ break;
+ case OP_GETUPVAL:
+ printf("%d %d", a, b);
+ printf(COMMENT "%s", UPVALNAME(b));
+ break;
+ case OP_SETUPVAL:
+ printf("%d %d", a, b);
+ printf(COMMENT "%s", UPVALNAME(b));
+ break;
+ case OP_GETTABUP:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT "%s", UPVALNAME(b));
+ printf(" ");
+ PrintConstant(f, c);
+ break;
+ case OP_GETTABLE:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_GETI:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_GETFIELD:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_SETTABUP:
+ printf("%d %d %d%s", a, b, c, ISK);
+ printf(COMMENT "%s", UPVALNAME(a));
+ printf(" ");
+ PrintConstant(f, b);
+ if (isk) {
+ printf(" ");
+ PrintConstant(f, c);
+ }
+ break;
+ case OP_SETTABLE:
+ printf("%d %d %d%s", a, b, c, ISK);
+ if (isk) {
+ printf(COMMENT);
+ PrintConstant(f, c);
+ }
+ break;
+ case OP_SETI:
+ printf("%d %d %d%s", a, b, c, ISK);
+ if (isk) {
+ printf(COMMENT);
+ PrintConstant(f, c);
+ }
+ break;
+ case OP_SETFIELD:
+ printf("%d %d %d%s", a, b, c, ISK);
+ printf(COMMENT);
+ PrintConstant(f, b);
+ if (isk) {
+ printf(" ");
+ PrintConstant(f, c);
+ }
+ break;
+ case OP_NEWTABLE:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT "%d", c + EXTRAARGC);
+ break;
+ case OP_SELF:
+ printf("%d %d %d%s", a, b, c, ISK);
+ if (isk) {
+ printf(COMMENT);
+ PrintConstant(f, c);
+ }
+ break;
+ case OP_ADDI:
+ printf("%d %d %d", a, b, sc);
+ break;
+ case OP_ADDK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_SUBK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_MULK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_MODK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_POWK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_DIVK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_IDIVK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_BANDK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_BORK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_BXORK:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ PrintConstant(f, c);
+ break;
+ case OP_SHRI:
+ printf("%d %d %d", a, b, sc);
+ break;
+ case OP_SHLI:
+ printf("%d %d %d", a, b, sc);
+ break;
+ case OP_ADD:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_SUB:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_MUL:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_MOD:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_POW:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_DIV:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_IDIV:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_BAND:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_BOR:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_BXOR:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_SHL:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_SHR:
+ printf("%d %d %d", a, b, c);
+ break;
+ case OP_MMBIN:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT "%s", eventname(c));
+ break;
+ case OP_MMBINI:
+ printf("%d %d %d %d", a, sb, c, isk);
+ printf(COMMENT "%s", eventname(c));
+ if (isk) {
+ printf(" flip");
+ }
+ break;
+ case OP_MMBINK:
+ printf("%d %d %d %d", a, b, c, isk);
+ printf(COMMENT "%s ", eventname(c));
+ PrintConstant(f, b);
+ if (isk) {
+ printf(" flip");
+ }
+ break;
+ case OP_UNM:
+ printf("%d %d", a, b);
+ break;
+ case OP_BNOT:
+ printf("%d %d", a, b);
+ break;
+ case OP_NOT:
+ printf("%d %d", a, b);
+ break;
+ case OP_LEN:
+ printf("%d %d", a, b);
+ break;
+ case OP_CONCAT:
+ printf("%d %d", a, b);
+ break;
+ case OP_CLOSE:
+ printf("%d", a);
+ break;
+ case OP_TBC:
+ printf("%d", a);
+ break;
+ case OP_JMP:
+ printf("%d", GETARG_sJ(i));
+ printf(COMMENT "to %d", GETARG_sJ(i) + pc + 2);
+ break;
+ case OP_EQ:
+ printf("%d %d %d", a, b, isk);
+ break;
+ case OP_LT:
+ printf("%d %d %d", a, b, isk);
+ break;
+ case OP_LE:
+ printf("%d %d %d", a, b, isk);
+ break;
+ case OP_EQK:
+ printf("%d %d %d", a, b, isk);
+ printf(COMMENT);
+ PrintConstant(f, b);
+ break;
+ case OP_EQI:
+ printf("%d %d %d", a, sb, isk);
+ break;
+ case OP_LTI:
+ printf("%d %d %d", a, sb, isk);
+ break;
+ case OP_LEI:
+ printf("%d %d %d", a, sb, isk);
+ break;
+ case OP_GTI:
+ printf("%d %d %d", a, sb, isk);
+ break;
+ case OP_GEI:
+ printf("%d %d %d", a, sb, isk);
+ break;
+ case OP_TEST:
+ printf("%d %d", a, isk);
+ break;
+ case OP_TESTSET:
+ printf("%d %d %d", a, b, isk);
+ break;
+ case OP_CALL:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ if (b==0) {
+ printf("all in ");
+ } else {
+ printf("%d in ", b - 1);
+ }
+ if (c==0) {
+ printf("all out");
+ } else {
+ printf("%d out", c- 1 );
+ }
+ break;
+ case OP_TAILCALL:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT "%d in", b - 1);
+ break;
+ case OP_RETURN:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT);
+ if (b == 0) {
+ printf("all out");
+ } else {
+ printf("%d out", b - 1);
+ }
+ break;
+ case OP_RETURN0:
+ break;
+ case OP_RETURN1:
+ printf("%d", a);
+ break;
+ case OP_FORLOOP:
+ printf("%d %d", a, bx);
+ printf(COMMENT "to %d", pc - bx + 2);
+ break;
+ case OP_FORPREP:
+ printf("%d %d", a, bx);
+ printf(COMMENT "to %d", pc + bx + 2);
+ break;
+ case OP_TFORPREP:
+ printf("%d %d", a, bx);
+ printf(COMMENT "to %d", pc + bx + 2);
+ break;
+ case OP_TFORCALL:
+ printf("%d %d", a, c);
+ break;
+ case OP_TFORLOOP:
+ printf("%d %d", a, bx);
+ printf(COMMENT "to %d", pc - bx + 2);
+ break;
+ case OP_SETLIST:
+ printf("%d %d %d", a, b, c);
+ if (isk) {
+ printf(COMMENT "%d", c + EXTRAARGC);
+ }
+ break;
+ case OP_CLOSURE:
+ printf("%d %d",a,bx);
+ printf(COMMENT "%p", LUACVOID(f->p[bx]));
+ break;
+ case OP_VARARG:
+ printf("%d %d", a, c);
+ printf(COMMENT);
+ if (c == 0) {
+ printf("all out");
+ } else {
+ printf("%d out", c-1);
+ }
+ break;
+ case OP_VARARGPREP:
+ printf("%d",a);
+ break;
+ case OP_EXTRAARG:
+ printf("%d", ax);
+ break;
+ default:
+ printf("%d %d %d", a, b, c);
+ printf(COMMENT "not handled");
+ break;
+ }
+ printf("\n");
+ }
+}
+
+# define SS(x) ((x == 1) ? "" : "s")
+# define S(x) (int)(x),SS(x)
+
+static void luaclib_aux_print_header(const Proto* f)
+{
+ const char* s = f->source ? getstr(f->source) : "=?";
+ if (*s == '@' || *s == '=') {
+ s++;
+ } else if (*s == LUA_SIGNATURE[0]) {
+ s = "(bstring)";
+ } else {
+ s = "(string)";
+ }
+ printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
+ (f->linedefined == 0) ? "main" : "function",
+ s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),LUACVOID(f)
+ );
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ (int)(f->numparams),
+ f->is_vararg?"+":"",
+ SS(f->numparams),
+ S(f->maxstacksize),
+ S(f->sizeupvalues)
+ );
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),
+ S(f->sizek),
+ S(f->sizep)
+ );
+}
+
+static void luaclib_aux_print_debug(const Proto* f)
+{
+ {
+ int n = f->sizek;
+ printf("constants (%d) for %p:\n", n, LUACVOID(f));
+ for (int i = 0; i < n; i++) {
+ printf("\t%d\t", i);
+ PrintType(f, i);
+ PrintConstant(f, i);
+ printf("\n");
+ }
+ }
+ {
+ int n = f->sizelocvars;
+ printf("locals (%d) for %p:\n", n, LUACVOID(f));
+ for (int i = 0; i < n; i++) {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,
+ getstr(f->locvars[i].varname),
+ f->locvars[i].startpc+1,
+ f->locvars[i].endpc+1
+ );
+ }
+ }
+ {
+ int n = f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n", n, LUACVOID(f));
+ for (int i = 0; i < n; i++) {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,
+ UPVALNAME(i),
+ f->upvalues[i].instack,
+ f->upvalues[i].idx
+ );
+ }
+ }
+}
+
+/* We only have one (needs checking). */
+
+static void luaclib_aux_print_function(const Proto* f, int full)
+{
+ int n = f->sizep;
+ luaclib_aux_print_header(f);
+ luaclib_aux_print_code(f);
+ if (full) {
+ luaclib_aux_print_debug(f);
+ }
+ for (int i = 0; i < n; i++) {
+ luaclib_aux_print_function(f->p[i], full);
+ }
+}
+
+static int luaclib_print(lua_State *L)
+{
+ int full = lua_toboolean(L, 2);
+ size_t len = 0;
+ const char *str = lua_tolstring(L, 1, &len);
+ if (len > 0 && luaL_loadbuffer(L, str, len, str) == LUA_OK) {
+ const Proto *f = toproto(L, -1);
+ if (f) {
+ tmname = G(L)->tmname;
+ luaclib_aux_print_function(f, full);
+ }
+ }
+ return 0;
+}
+
+/* So far for the adapted rip-off. */
+
+void lmt_luaclib_initialize(void)
+{
+ /* not used yet */
+}
+
+static const struct luaL_Reg luaclib_function_list[] = {
+ { "print", luaclib_print },
+ { NULL, NULL },
+};
+
+int luaopen_luac(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, luaclib_function_list, 0);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtluaclib.h b/source/luametatex/source/lua/lmtluaclib.h
new file mode 100644
index 000000000..28b5998bb
--- /dev/null
+++ b/source/luametatex/source/lua/lmtluaclib.h
@@ -0,0 +1,10 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LUALUACLIB_H
+# define LUALUACLIB_H
+
+extern void lmt_luaclib_initialize (void);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtlualib.c b/source/luametatex/source/lua/lmtlualib.c
new file mode 100644
index 000000000..b82ddc649
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlualib.c
@@ -0,0 +1,627 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ Some code here originates from the beginning of \LUATEX\ developmentm like the bytecode
+ registers. They provide a way to store (compiled) \LUA\ code in the format file. In the
+ meantime there are plenty of ways to use \LUA\ code in the frontend so an interface at the
+ \TEX\ end makes no longer much sense.
+
+ This module also provides some statistics and control options. Keep in mind that the engine
+ also can act as a \LUA\ engine, so some of that property is reflected in the code.
+
+*/
+
+# define LOAD_BUF_SIZE 64*1024
+# define UINT_MAX32 0xFFFFFFFF
+
+# define LUA_FUNCTIONS "lua.functions"
+# define LUA_BYTECODES "lua.bytecodes"
+# define LUA_BYTECODES_INDIRECT "lua.bytecodes.indirect"
+
+typedef struct bytecode {
+ unsigned char *buf;
+ int size;
+ int alloc;
+} bytecode;
+
+static bytecode *lmt_bytecode_registers = NULL;
+
+void lmt_dump_registers(dumpstream f)
+{
+ dump_int(f, lmt_lua_state.version_number);
+ dump_int(f, lmt_lua_state.release_number);
+ dump_int(f, lmt_lua_state.integer_size);
+ dump_int(f, lmt_lua_state.bytecode_max);
+ if (lmt_bytecode_registers) {
+ int n = 0;
+ for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
+ if (lmt_bytecode_registers[k].size != 0) {
+ n++;
+ }
+ }
+ dump_int(f, n);
+ for (int k = 0; k <= lmt_lua_state.bytecode_max; k++) {
+ bytecode b = lmt_bytecode_registers[k];
+ if (b.size != 0) {
+ dump_int(f, k);
+ dump_int(f, b.size);
+ dump_items(f, (char *) b.buf, 1, b.size);
+ }
+ }
+ }
+}
+
+void lmt_undump_registers(dumpstream f)
+{
+ int version_number = 0;
+ int release_number = 0;
+ int integer_size = 0;
+ undump_int(f, version_number);
+ if (version_number != lmt_lua_state.version_number) {
+ tex_fatal_undump_error("mismatching Lua version number");
+ }
+ undump_int(f, release_number);
+ if (release_number != lmt_lua_state.release_number) {
+ tex_fatal_undump_error("mismatching Lua release number");
+ }
+ undump_int(f, integer_size);
+ if (integer_size != lmt_lua_state.integer_size) {
+ tex_fatal_undump_error("different integer size");
+ }
+ undump_int(f, lmt_lua_state.bytecode_max);
+ if (lmt_lua_state.bytecode_max < 0) {
+ tex_fatal_undump_error("not enough memory for undumping bytecodes"); /* old */
+ } else {
+ size_t s = (lmt_lua_state.bytecode_max + 1) * sizeof(bytecode);
+ int n = (int) s;
+ lmt_bytecode_registers = (bytecode *) lmt_memory_malloc(s);
+ if (lmt_bytecode_registers) {
+ lmt_lua_state.bytecode_bytes = n;
+ for (int j = 0; j <= lmt_lua_state.bytecode_max; j++) {
+ lmt_bytecode_registers[j].buf = NULL;
+ lmt_bytecode_registers[j].size = 0;
+ lmt_bytecode_registers[j].alloc = 0;
+ }
+ undump_int(f, n);
+ for (int j = 0; j < n; j++) {
+ unsigned char *buffer;
+ int slot, size;
+ undump_int(f, slot);
+ undump_int(f, size);
+ buffer = (unsigned char *) lmt_memory_malloc((unsigned) size);
+ if (buffer) {
+ memset(buffer, 0, (size_t) size);
+ undump_items(f, buffer, 1, size);
+ lmt_bytecode_registers[slot].buf = buffer;
+ lmt_bytecode_registers[slot].size = size;
+ lmt_bytecode_registers[slot].alloc = size;
+ lmt_lua_state.bytecode_bytes += size;
+ } else {
+ tex_fatal_undump_error("not enough memory for undumping bytecodes");
+ }
+ }
+ }
+ }
+}
+
+static void lualib_aux_bytecode_register_shadow_set(lua_State *L, int k)
+{
+ /*tex the stack holds the value to be set */
+ luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
+ if (lua_istable(L, -1)) {
+ lua_pushvalue(L, -2);
+ lua_rawseti(L, -2, k);
+ }
+ lua_pop(L, 2); /*tex pop table or nil and value */
+}
+
+static int lualib_aux_bytecode_register_shadow_get(lua_State *L, int k)
+{
+ /*tex the stack holds the value to be set */
+ int ret = 0;
+ luaL_getmetatable(L, LUA_BYTECODES_INDIRECT);
+ if (lua_istable(L, -1)) {
+ if (lua_rawgeti(L, -1, k) != LUA_TNIL) {
+ ret = 1;
+ }
+ /*tex store the value or nil, deeper down */
+ lua_insert(L, -3);
+ /*tex pop the value or nil at top */
+ lua_pop(L, 1);
+ }
+ /*tex pop table or nil */
+ lua_pop(L, 1);
+ return ret;
+}
+
+static int lualib_aux_writer(lua_State *L, const void *b, size_t size, void *B)
+{
+ bytecode *buf = (bytecode *) B;
+ (void) L;
+ if ((int) (buf->size + (int) size) > buf->alloc) {
+ unsigned newalloc = (unsigned) (buf->alloc + (int) size + LOAD_BUF_SIZE);
+ unsigned char *bb = lmt_memory_realloc(buf->buf, newalloc);
+ if (bb) {
+ buf->buf = bb;
+ buf->alloc = newalloc;
+ } else {
+ return luaL_error(L, "something went wrong with handling bytecodes");
+ }
+ }
+ memcpy(buf->buf + buf->size, b, size);
+ buf->size += (int) size;
+ lmt_lua_state.bytecode_bytes += (unsigned) size;
+ return 0;
+}
+
+static const char *lualib_aux_reader(lua_State *L, void *ud, size_t *size)
+{
+ bytecode *buf = (bytecode *) ud;
+ (void) L;
+ *size = (size_t) buf->size;
+ return (const char *) buf->buf;
+}
+
+static int lualib_valid_bytecode(lua_State *L, int slot)
+{
+ if (slot < 0 || slot > lmt_lua_state.bytecode_max) {
+ return luaL_error(L, "bytecode register out of range");
+ } else if (lualib_aux_bytecode_register_shadow_get(L, slot) || ! lmt_bytecode_registers[slot].buf) {
+ return luaL_error(L, "undefined bytecode register");
+ } else if (lua_load(L, lualib_aux_reader, (void *) (lmt_bytecode_registers + slot), "bytecode", NULL)) {
+ return luaL_error(L, "bytecode register doesn't load well");
+ } else {
+ return 1;
+ }
+}
+
+static int lualib_get_bytecode(lua_State *L)
+{
+ int slot = lmt_checkinteger(L, 1);
+ if (lualib_valid_bytecode(L, slot)) {
+ lua_pushvalue(L, -1);
+ lualib_aux_bytecode_register_shadow_set(L, slot);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lmt_handle_bytecode_call(lua_State *L, int slot)
+{
+ int stacktop = lua_gettop(L);
+ int error = 1;
+ if (lualib_valid_bytecode(L, slot)) {
+ /*tex function index */
+ lua_pushinteger(L, slot);
+ /*tex push traceback function */
+ lua_pushcfunction(L, lmt_traceback);
+ /*tex put it under chunk */
+ lua_insert(L, stacktop);
+ ++lmt_lua_state.bytecode_callback_count;
+ error = lua_pcall(L, 1, 0, stacktop);
+ /*tex remove traceback function */
+ lua_remove(L, stacktop);
+ if (error) {
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ lmt_error(L, "bytecode call", slot, (error == LUA_ERRRUN ? 0 : 1));
+ }
+ }
+ lua_settop(L, stacktop);
+ return ! error;
+}
+
+void lmt_bytecode_call(int slot)
+{
+ lmt_handle_bytecode_call(lmt_lua_state.lua_instance, slot);
+}
+
+/*tex
+ We don't report an error so this this permits a loop over the bytecode array.
+*/
+
+static int lualib_call_bytecode(lua_State *L)
+{
+ int k = lmt_checkinteger(L, -1);
+ if (k >= 0 && ! lualib_aux_bytecode_register_shadow_get(L, k)) {
+ if (k <= lmt_lua_state.bytecode_max && lmt_bytecode_registers[k].buf) {
+ lmt_handle_bytecode_call(L, k);
+ /* We can have a function pushed! */
+ } else {
+ k = -1;
+ }
+ } else {
+ k = -1;
+ }
+ lua_pushboolean(L, k != -1);
+ /*tex At most 1. */
+ return 1;
+}
+
+static int lualib_set_bytecode(lua_State *L)
+{
+ int k = lmt_checkinteger(L, 1);
+ int i = k + 1;
+ if ((k < 0) || (k > max_bytecode_index)) {
+ return luaL_error(L, "bytecode register out of range");
+ } else {
+ int ltype = lua_type(L, 2);
+ int strip = lua_toboolean(L, 3);
+ if (ltype != LUA_TFUNCTION && ltype != LUA_TNIL) {
+ return luaL_error(L, "bytecode register should be a function or nil");
+ } else {
+ /*tex Later calls expect the function at the top of the stack. */
+ lua_settop(L, 2);
+ if (k > lmt_lua_state.bytecode_max) {
+ bytecode *r = lmt_memory_realloc(lmt_bytecode_registers, (size_t) i * sizeof(bytecode));
+ if (r) {
+ lmt_bytecode_registers = r;
+ lmt_lua_state.bytecode_bytes += ((int) sizeof(bytecode) * (k + 1 - (lmt_lua_state.bytecode_max > 0 ? lmt_lua_state.bytecode_max : 0)));
+ for (unsigned j = (unsigned) (lmt_lua_state.bytecode_max + 1); j <= (unsigned) k; j++) {
+ lmt_bytecode_registers[j].buf = NULL;
+ lmt_bytecode_registers[j].size = 0;
+ lmt_bytecode_registers[j].alloc = 0;
+ }
+ lmt_lua_state.bytecode_max = k;
+ } else {
+ return luaL_error(L, "bytecode register exceeded memory");
+ }
+ }
+ if (lmt_bytecode_registers[k].buf) {
+ lmt_memory_free(lmt_bytecode_registers[k].buf);
+ lmt_lua_state.bytecode_bytes -= lmt_bytecode_registers[k].size;
+ lmt_bytecode_registers[k].size = 0;
+ lmt_bytecode_registers[k].buf = NULL;
+ lua_pushnil(L);
+ lualib_aux_bytecode_register_shadow_set(L, k);
+ }
+ if (ltype == LUA_TFUNCTION) {
+ lmt_bytecode_registers[k].buf = lmt_memory_calloc(1, LOAD_BUF_SIZE);
+ if (lmt_bytecode_registers[k].buf) {
+ lmt_bytecode_registers[k].alloc = LOAD_BUF_SIZE;
+ // memset(lua_bytecode_registers[k].buf, 0, LOAD_BUF_SIZE);
+ lua_dump(L, lualib_aux_writer, (void *) (lmt_bytecode_registers + k), strip);
+ } else {
+ return luaL_error(L, "bytecode register exceeded memory");
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ return 0;
+}
+
+void lmt_initialize_functions(int set_size)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (set_size) {
+ tex_engine_get_config_number("functionsize", &lmt_lua_state.function_table_size);
+ if (lmt_lua_state.function_table_size < 0) {
+ lmt_lua_state.function_table_size = 0;
+ }
+ lua_createtable(L, lmt_lua_state.function_table_size, 0);
+ } else {
+ lua_newtable(L);
+ }
+ lmt_lua_state.function_table_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ /* not needed, so unofficial */
+ lua_pushstring(L, LUA_FUNCTIONS);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+static int lualib_get_functions_table(lua_State *L)
+{
+ if (lua_toboolean(L, lua_gettop(L))) {
+ /*tex Beware: this can have side effects when used without care. */
+ lmt_initialize_functions(1);
+ }
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_lua_state.function_table_id);
+ return 1;
+}
+
+static int lualib_new_table(lua_State *L)
+{
+ int i = lmt_checkinteger(L, 1);
+ int h = lmt_checkinteger(L, 2);
+ lua_createtable(L, i < 0 ? 0 : i, h < 0 ? 0 : h);
+ return 1;
+}
+
+static int lualib_new_index(lua_State *L)
+{
+ int n = lmt_checkinteger(L, 1);
+ int t = lua_gettop(L);
+ lua_createtable(L, n < 0 ? 0 : n, 0);
+ if (t == 2) {
+ for (lua_Integer i = 1; i <= n; i++) {
+ lua_pushvalue(L, 2);
+ lua_rawseti(L, -2, i);
+ }
+ }
+ return 1;
+}
+
+static int lualib_get_stack_top(lua_State *L)
+{
+ lua_pushinteger(L, lua_gettop(L));
+ return 1;
+}
+
+static int lualib_get_runtime(lua_State *L)
+{
+ lua_pushnumber(L, aux_get_run_time());
+ return 1;
+}
+
+static int lualib_get_currenttime(lua_State *L)
+{
+ lua_pushnumber(L, aux_get_current_time());
+ return 1;
+}
+
+static int lualib_set_exitcode(lua_State *L)
+{
+ lmt_error_state.default_exit_code = lmt_checkinteger(L, 1);
+ return 0;
+}
+
+static int lualib_get_exitcode(lua_State *L)
+{
+ lua_pushinteger(L, lmt_error_state.default_exit_code);
+ return 1;
+}
+
+/*tex
+
+ The |getpreciseticks()| call returns a number. This number has no meaning in itself but
+ successive calls can be used to calculate a delta with a previous call. When the number is fed
+ into |getpreciseseconds(n)| a number is returned representing seconds.
+
+*/
+
+# ifdef _WIN32
+
+# define clock_inittime()
+
+ static int lualib_get_preciseticks(lua_State *L)
+ {
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ lua_pushnumber(L, (double) t.QuadPart);
+ return 1;
+ }
+
+ static int lualib_get_preciseseconds(lua_State *L)
+ {
+ LARGE_INTEGER t;
+ QueryPerformanceFrequency(&t);
+ lua_pushnumber(L, luaL_optnumber(L, 1, 0) / (double) t.QuadPart);
+ return 1;
+ }
+
+# else
+
+# if (defined(__MACH__) && ! defined(CLOCK_PROCESS_CPUTIME_ID))
+
+ /* https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x */
+
+# include <mach/mach_time.h>
+# define CLOCK_PROCESS_CPUTIME_ID 1
+
+ static double conversion_factor;
+
+ static void clock_inittime()
+ {
+ mach_timebase_info_data_t timebase;
+ mach_timebase_info(&timebase);
+ conversion_factor = (double)timebase.numer / (double)timebase.denom;
+ }
+
+ static int clock_gettime(int clk_id, struct timespec *t)
+ {
+ uint64_t time;
+ double nseconds, seconds;
+ (void) clk_id; /* please the compiler */
+ time = mach_absolute_time();
+ nseconds = ((double)time * conversion_factor);
+ seconds = ((double)time * conversion_factor / 1e9);
+ t->tv_sec = seconds;
+ t->tv_nsec = nseconds;
+ return 0;
+ }
+
+# else
+
+# define clock_inittime()
+
+# endif
+
+ static int lualib_get_preciseticks(lua_State *L)
+ {
+ struct timespec t;
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&t);
+ lua_pushnumber(L, t.tv_sec*1000000000.0 + t.tv_nsec);
+ return 1;
+ }
+
+ static int lualib_get_preciseseconds(lua_State *L)
+ {
+ lua_pushnumber(L, ((double) luaL_optnumber(L, 1, 0)) / 1000000000.0);
+ return 1;
+ }
+
+# endif
+
+static int lualib_get_startupfile(lua_State *L)
+{
+ lua_pushstring(L, lmt_engine_state.startup_filename);
+ return 1;
+}
+
+static int lualib_get_version(lua_State *L)
+{
+ lua_pushstring(L, LUA_VERSION);
+ return 1;
+}
+
+/* obsolete:
+static int lualib_get_hashchars(lua_State *L)
+{
+ lua_pushinteger(L, 1 << LUAI_HASHLIMIT);
+ return 1;
+}
+*/
+
+/*
+static int lualib_get_doing_the(lua_State *L)
+{
+ lua_pushboolean(L, lua_state.doing_the);
+ return 1;
+}
+*/
+
+/* This makes the (already old and rusty) profiler 2.5 times faster. */
+
+/*
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ } else {
+ *arg = 0;
+ return L;
+ }
+}
+
+static int lualib_get_debug_info(lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_getstack(L1, 2, &ar) && lua_getinfo(L1, "nS", &ar)) {
+ ....
+ }
+ return 0;
+}
+*/
+
+/*
+static int lualib_get_debug_info(lua_State *L) {
+ if (! lua_isthread(L, 1)) {
+ lua_Debug ar;
+ if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
+ lua_pushstring(L, ar.short_src);
+ lua_pushinteger(L, ar.linedefined);
+ if (ar.name) {
+ lua_pushstring(L, ar.name);
+ } else if (! strcmp(ar.what, "C")) {
+ lua_pushliteral(L, "<anonymous>");
+ } else if (ar.namewhat) {
+ lua_pushstring(L, ar.namewhat);
+ } else if (ar.what) {
+ lua_pushstring(L, ar.what);
+ } else {
+ lua_pushliteral(L, "<unknown>");
+ }
+ return 3;
+ }
+ }
+ return 0;
+}
+*/
+
+/*tex
+ I can make it faster if needed but then I need to patch the two lua modules (add some simple
+ helpers) which for now doesn't make much sense. This is an undocumented feature.
+*/
+
+static int lualib_get_debug_info(lua_State *L) {
+ if (! lua_isthread(L, 1)) {
+ lua_Debug ar;
+ if (lua_getstack(L, 2, &ar) && lua_getinfo(L, "nS", &ar)) {
+ lua_pushstring(L, ar.name ? ar.name : (ar.namewhat ? ar.namewhat : (ar.what ? ar.what : "<unknown>")));
+ lua_pushstring(L, ar.short_src);
+ lua_pushinteger(L, ar.linedefined);
+ return 3;
+ }
+ }
+ return 0;
+}
+
+/* */
+
+static const struct luaL_Reg lualib_function_list[] = {
+ { "newtable", lualib_new_table },
+ { "newindex", lualib_new_index },
+ { "getstacktop", lualib_get_stack_top },
+ { "getruntime", lualib_get_runtime },
+ { "getcurrenttime", lualib_get_currenttime },
+ { "getpreciseticks", lualib_get_preciseticks },
+ { "getpreciseseconds", lualib_get_preciseseconds },
+ { "getbytecode", lualib_get_bytecode },
+ { "setbytecode", lualib_set_bytecode },
+ { "callbytecode", lualib_call_bytecode },
+ { "getfunctionstable", lualib_get_functions_table },
+ { "getstartupfile", lualib_get_startupfile },
+ { "getversion", lualib_get_version },
+ /* { "gethashchars", lualib_get_hashchars }, */
+ { "setexitcode", lualib_set_exitcode },
+ { "getexitcode", lualib_get_exitcode },
+ /* { "doingthe", lualib_get_doing_the }, */
+ { "getdebuginfo", lualib_get_debug_info },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg lualib_function_list_only[] = {
+ { "newtable", lualib_new_table },
+ { "newindex", lualib_new_index },
+ { "getstacktop", lualib_get_stack_top },
+ { "getruntime", lualib_get_runtime },
+ { "getcurrenttime", lualib_get_currenttime },
+ { "getpreciseticks", lualib_get_preciseticks },
+ { "getpreciseseconds", lualib_get_preciseseconds },
+ { "getstartupfile", lualib_get_startupfile },
+ { "getversion", lualib_get_version },
+ /* { "gethashchars", lualib_get_hashchars }, */
+ { "setexitcode", lualib_set_exitcode },
+ { "getexitcode", lualib_get_exitcode },
+ { NULL, NULL },
+};
+
+static int lualib_index_bytecode(lua_State *L)
+{
+ lua_remove(L, 1);
+ return lualib_get_bytecode(L);
+}
+
+static int lualib_newindex_bytecode(lua_State *L)
+{
+ lua_remove(L, 1);
+ return lualib_set_bytecode(L);
+}
+
+int luaopen_lua(lua_State *L)
+{
+ lua_newtable(L);
+ if (lmt_engine_state.lua_only) {
+ luaL_setfuncs(L, lualib_function_list_only, 0);
+ } else {
+ luaL_setfuncs(L, lualib_function_list, 0);
+ lmt_make_table(L, "bytecode", LUA_BYTECODES, lualib_index_bytecode, lualib_newindex_bytecode);
+ lua_newtable(L);
+ lua_setfield(L, LUA_REGISTRYINDEX, LUA_BYTECODES_INDIRECT);
+ }
+ lua_pushstring(L, LUA_VERSION);
+ lua_setfield(L, -2, "version");
+ if (lmt_engine_state.startup_filename) {
+ lua_pushstring(L, lmt_engine_state.startup_filename);
+ lua_setfield(L, -2, "startupfile");
+ }
+ clock_inittime();
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtlualib.h b/source/luametatex/source/lua/lmtlualib.h
new file mode 100644
index 000000000..88ae5ae38
--- /dev/null
+++ b/source/luametatex/source/lua/lmtlualib.h
@@ -0,0 +1,25 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LLUALIB_H
+# define LMT_LLUALIB_H
+
+/*tex
+
+ We started with multiple instances but that made no sense as dealing with isolated instances
+ and talking to \TEX\ also means that one then has to have a channel between instances. and it's
+ not worth the trouble. So we went for one instance.
+
+ This also means that the names related to directlua instances have been removed in the follow
+ up.
+
+*/
+
+extern void lmt_dump_registers (dumpstream f);
+extern void lmt_undump_registers (dumpstream f);
+extern void lmt_bytecode_call (int slot);
+
+extern void lmt_initialize_functions (int set_size);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtmplib.c b/source/luametatex/source/lua/lmtmplib.c
new file mode 100644
index 000000000..74d684c3f
--- /dev/null
+++ b/source/luametatex/source/lua/lmtmplib.c
@@ -0,0 +1,3137 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This is an adapted version of \MPLIB\ for ConTeXt LMTX. Interfaces might change as experiments
+ demand or indicate this. It's meant for usage in \LUAMETATEX, which is an engine geared at
+ \CONTEXT, and which, although useable in production, will in principle be experimental and
+ moving for a while, depending on needs, experiments and mood.
+
+ In \LUATEX\, at some point \MPLIB\ got an interface to \LUA\ and that greatly enhanced the
+ posibilities. In \LUAMETATEX\ this interface was further improved and in addition scanners
+ got added. So in the meantime we have a rather complete and tight integration of \TEX, \LUA,
+ and \METAPOST\ and in \CONTEXT\ we use that integration as much as possible.
+
+ An important difference with \LUATEX\ is that we use an upgraded \MPLIB. The \POSTSCRIPT\
+ backend has been dropped and there is no longer font and file related code because that
+ all goes via \LUA\ now. The binary frontend has been dropped to so we now only have scaled,
+ double and decimal.
+
+*/
+
+# include "mpconfig.h"
+
+# include "mp.h"
+
+/*tex
+ We can use common definitions but can only load this file after we load the \METAPOST\ stuff,
+ which defines a whole bunch of macros that can clash. Using the next one is actually a good
+ check for such conflicts.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ Here are some enumeration arrays to map MPlib enums to \LUA\ strings. If needed we can also
+ predefine keys here, as we do with nodes. Some tables relate to the scanners.
+
+*/
+
+# define MPLIB_PATH 0
+# define MPLIB_PEN 1
+# define MPLIB_PATH_SIZE 8
+
+static const char *mplib_math_options[] = {
+ "scaled",
+ "double",
+ "binary", /* not available in luatex */
+ "decimal",
+ NULL
+};
+
+static const char *mplib_interaction_options[] = {
+ "unknown",
+ "batch",
+ "nonstop",
+ "scroll",
+ "errorstop",
+ "silent",
+ NULL
+};
+
+static const char *mplib_filetype_names[] = {
+ "terminal", /* mp_filetype_terminal */
+ "mp", /* mp_filetype_program */
+ "data", /* mp_filetype_text */
+ NULL
+};
+
+/*
+static const char *knot_type_enum[] = {
+ "endpoint", "explicit", "given", "curl", "open", "end_cycle"
+};
+*/
+
+static const char *mplib_fill_fields[] = {
+ "type",
+ "path", "htap",
+ "pen", "color",
+ "linejoin", "miterlimit",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stroked_fields[] = {
+ "type",
+ "path",
+ "pen", "color",
+ "linejoin", "miterlimit", "linecap",
+ "dash",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_clip_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_group_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_bounds_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_clip_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_group_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_bounds_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_no_fields[] = {
+ NULL
+};
+
+static const char *mplib_codes[] = {
+ "undefined",
+ "btex", /* mp_btex_command */ /* btex verbatimtex */
+ "etex", /* mp_etex_command */ /* etex */
+ "if", /* mp_if_test_command */ /* if */
+ "fiorelse", /* mp_fi_or_else_command */ /* elseif else fi */
+ "input", /* mp_input_command */ /* input endinput */
+ "iteration", /* mp_iteration_command */ /* for forsuffixes forever endfor */
+ "repeatloop", /* mp_repeat_loop_command */ /* used in repeat loop (endfor) */
+ "exittest", /* mp_exit_test_command */ /* exitif */
+ "relax", /* mp_relax_command */ /* \\ */
+ "scantokens", /* mp_scan_tokens_command */ /* scantokens */
+ "runscript", /* mp_runscript_command */ /* runscript */
+ "maketext", /* mp_maketext_command */ /* maketext */
+ "expandafter", /* mp_expand_after_command */ /* expandafter */
+ "definedmacro", /* mp_defined_macro_command */ /* */
+ "save", /* mp_save_command */ /* save */
+ "interim", /* mp_interim_command */ /* interim */
+ "let", /* mp_let_command */ /* let */
+ "newinternal", /* mp_new_internal_command */ /* newinternal */
+ "macrodef", /* mp_macro_def_command */ /* def vardef (etc) */
+ "shipout", /* mp_ship_out_command */ /* shipout */
+ "addto", /* mp_add_to_command */ /* addto */
+ "setbounds", /* mp_bounds_command */ /* setbounds clip group */
+ "protection", /* mp_protection_command */
+ "property", /* mp_property_command */
+ "show", /* mp_show_command */ /* show showvariable (etc) */
+ "mode", /* mp_mode_command */ /* batchmode (etc) */
+ "onlyset", /* mp_only_set_command */ /* randomseed, maxknotpool */
+ "message", /* mp_message_command */ /* message errmessage */
+ "everyjob", /* mp_every_job_command */ /* everyjob */
+ "delimiters", /* mp_delimiters_command */ /* delimiters */
+ "write", /* mp_write_command */ /* write */
+ "typename", /* mp_type_name_command */ /* (declare) numeric pair */
+ "leftdelimiter", /* mp_left_delimiter_command */ /* the left delimiter of a matching pair */
+ "begingroup", /* mp_begin_group_command */ /* begingroup */
+ "nullary", /* mp_nullary_command */ /* operator without arguments: normaldeviate (etc) */
+ "unary", /* mp_unary_command */ /* operator with one argument: sqrt (etc) */
+ "str", /* mp_str_command */ /* convert a suffix to a string: str */
+ "void", /* mp_void_command */ /* convert a suffix to a boolean: void */
+ "cycle", /* mp_cycle_command */ /* cycle */
+ "ofbinary", /* mp_of_binary_command */ /* binary operation taking "of", like "point of" */
+ "capsule", /* mp_capsule_command */ /* */
+ "string", /* mp_string_command */ /* */
+ "internal", /* mp_internal_quantity_command */ /* */
+ "tag", /* mp_tag_command */ /* a symbolic token without a primitive meaning */
+ "numeric", /* mp_numeric_command */ /* numeric constant */
+ "plusorminus", /* mp_plus_or_minus_command */ /* + - */
+ "secondarydef", /* mp_secondary_def_command */ /* secondarydef */
+ "tertiarybinary", /* mp_tertiary_binary_command */ /* an operator at the tertiary level: ++ (etc) */
+ "leftbrace", /* mp_left_brace_command */ /* { */
+ "pathjoin", /* mp_path_join_command */ /* .. */
+ "ampersand", /* mp_ampersand_command */ /* & */
+ "tertiarydef", /* mp_tertiary_def_command */ /* tertiarydef */
+ "primarybinary", /* mp_primary_binary_command */ /* < (etc) */
+ "equals", /* mp_equals_command */ /* = */
+ "and", /* mp_and_command */ /* and */
+ "primarydef", /* mp_primary_def_command */ /* primarydef */
+ "slash", /* mp_slash_command */ /* / */
+ "secondarybinary", /* mp_secondary_binary_command */ /* an operator at the binary level: shifted (etc) */
+ "parametertype", /* mp_parameter_commmand */ /* primary expr suffix (etc) */
+ "controls", /* mp_controls_command */ /* controls */
+ "tension", /* mp_tension_command */ /* tension */
+ "atleast", /* mp_at_least_command */ /* atleast */
+ "curl", /* mp_curl_command */ /* curl */
+ "macrospecial", /* mp_macro_special_command */ /* quote, #@ (etc) */
+ "rightdelimiter", /* mp_right_delimiter_command */ /* the right delimiter of a matching pair */
+ "leftbracket", /* mp_left_bracket_command */ /* [ */
+ "rightbracket", /* mp_right_bracket_command */ /* ] */
+ "rightbrace", /* mp_right_brace_command */ /* } */
+ "with", /* mp_with_option_command */ /* withpen (etc) */
+ "thingstoadd", /* mp_thing_to_add_command */ /* addto contour doublepath also */
+ "of", /* mp_of_command */ /* of */
+ "to", /* mp_to_command */ /* to */
+ "step", /* mp_step_command */ /* step */
+ "until", /* mp_until_command */ /* until */
+ "within", /* mp_within_command */ /* within */
+ "assignment", /* mp_assignment_command */ /* := */
+ "colon", /* mp_colon_command */ /* : */
+ "comma", /* mp_comma_command */ /* , */
+ "semicolon", /* mp_semicolon_command */ /* ; */
+ "endgroup", /* mp_end_group_command */ /* endgroup */
+ "stop", /* mp_stop_command */ /* end dump */
+ // "outertag", /* mp_outer_tag_command */ /* protection code added to command code */
+ "undefinedcs", /* mp_undefined_cs_command */ /* protection code added to command code */
+ NULL
+};
+
+static const char *mplib_states[] = {
+ "normal",
+ "skipping",
+ "flushing",
+ "absorbing",
+ "var_defining",
+ "op_defining",
+ "loop_defining",
+ NULL
+};
+
+static const char *mplib_types[] = {
+ "undefined", /* mp_undefined_type */
+ "vacuous", /* mp_vacuous_type */
+ "boolean", /* mp_boolean_type */
+ "unknownboolean", /* mp_unknown_boolean_type */
+ "string", /* mp_string_type */
+ "unknownstring", /* mp_unknown_string_type */
+ "pen", /* mp_pen_type */
+ "unknownpen", /* mp_unknown_pen_type */
+ "nep", /* mp_nep_type */
+ "unknownnep", /* mp_unknown_nep_type */
+ "path", /* mp_path_type */
+ "unknownpath", /* mp_unknown_path_type */
+ "picture", /* mp_picture_type */
+ "unknownpicture", /* mp_unknown_picture_type */
+ "transform", /* mp_transform_type */
+ "color", /* mp_color_type */
+ "cmykcolor", /* mp_cmykcolor_type */
+ "pair", /* mp_pair_type */
+ // "script", /* */
+ "numeric", /* mp_numeric_type */
+ "known", /* mp_known_type */
+ "dependent", /* mp_dependent_type */
+ "protodependent", /* mp_proto_dependent_type */
+ "independent", /* mp_independent_type */
+ "tokenlist", /* mp_token_list_type */
+ "structured", /* mp_structured_type */
+ "unsuffixedmacro", /* mp_unsuffixed_macro_type */
+ "suffixedmacro", /* mp_suffixed_macro_type */
+ NULL
+};
+
+static const char *mplib_colormodels[] = {
+ "no",
+ "grey",
+ "rgb",
+ "cmyk",
+ NULL
+};
+
+/*tex Some statistics. */
+
+typedef struct mplib_state_info {
+ int file_callbacks;
+ int text_callbacks;
+ int script_callbacks;
+ int log_callbacks;
+ int overload_callbacks;
+ int error_callbacks;
+ int warning_callbacks;
+} mplib_state_info;
+
+static mplib_state_info mplib_state = {
+ .file_callbacks = 0,
+ .text_callbacks = 0,
+ .script_callbacks = 0,
+ .log_callbacks = 0,
+ .overload_callbacks = 0,
+ .error_callbacks = 0,
+ .warning_callbacks = 0,
+};
+
+/*tex
+
+ We need a few metatable identifiers in order to access the metatables for the main object and result
+ userdata. The following code is now replaced by the method that uses keys.
+
+*/
+
+/*
+# define MP_METATABLE_INSTANCE "mp.instance"
+# define MP_METATABLE_FIGURE "mp.figure"
+# define MP_METATABLE_OBJECT "mp.object"
+*/
+
+/*tex
+ This is used for heuristics wrt curves or lines. The default value is rather small
+ and often leads to curved rectangles.
+
+ \starttabulate[||||]
+ \NC 131/65536.0 \NC 0.0019989013671875 \NC default \NC \NR
+ \NC 0.001 * 0x7FFF/0x4000 \NC 0.0019999389648438 \NC kind of the default \NC \NR
+ \NC 32/16000.0 \NC 0.002 \NC somewhat cleaner \NC \NR
+ \NC 10/ 2000.0 \NC 0.005 \NC often good enough \NC \NR
+ \stoptabulate
+
+*/
+
+# define default_bend_tolerance 131/65536.0
+# define default_move_tolerance 131/65536.0
+
+typedef enum mp_variables {
+ mp_bend_tolerance = 1,
+ mp_move_tolerance = 2,
+} mp_variables;
+
+static lua_Number mplib_aux_get_bend_tolerance(lua_State *L, int slot)
+{
+ lua_Number tolerance;
+ lua_getiuservalue(L, slot, mp_bend_tolerance);
+ tolerance = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return tolerance;
+}
+
+static lua_Number mplib_aux_get_move_tolerance(lua_State *L, int slot)
+{
+ lua_Number tolerance;
+ lua_getiuservalue(L, slot, mp_move_tolerance);
+ tolerance = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return tolerance;
+}
+
+static void mplib_aux_set_bend_tolerance(lua_State *L, lua_Number tolerance)
+{
+ lua_pushnumber(L, tolerance);
+ lua_setiuservalue(L, -2, mp_bend_tolerance);
+}
+
+static void mplib_aux_set_move_tolerance(lua_State *L, lua_Number tolerance)
+{
+ lua_pushnumber(L, tolerance);
+ lua_setiuservalue(L, -2, mp_move_tolerance);
+}
+
+inline static char *lmt_string_from_index(lua_State *L, int n)
+{
+ size_t l;
+ const char *x = lua_tolstring(L, n, &l);
+ // return (x && l > 0) ? lmt_generic_strdup(x) : NULL;
+ return (x && l > 0) ? lmt_memory_strdup(x) : NULL;
+}
+
+inline static char *lmt_lstring_from_index(lua_State *L, int n, size_t *l)
+{
+ const char *x = lua_tolstring(L, n, l);
+ // return (x && l > 0) ? lmt_generic_strdup(x) : NULL;
+ return (x && l > 0) ? lmt_memory_strdup(x) : NULL;
+}
+
+static void mplib_aux_invalid_object_warning(const char * detail)
+{
+ tex_formatted_warning("mp lib","lua <mp %s> expected", detail);
+}
+
+static void mplib_aux_invalid_object_error(const char * detail)
+{
+ tex_formatted_error("mp lib","lua <mp %s> expected", detail);
+}
+
+inline static MP *mplib_aux_is_mpud(lua_State *L, int n)
+{
+ MP *p = (MP *) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_instance);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_error("instance");
+ return NULL;
+}
+
+inline static MP mplib_aux_is_mp(lua_State *L, int n)
+{
+ MP *p = (MP *) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_instance);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return *p;
+ }
+ }
+ mplib_aux_invalid_object_error("instance");
+ return NULL;
+}
+
+inline static mp_edge_object **mplib_aux_is_figure(lua_State *L, int n)
+{
+ mp_edge_object **p = (mp_edge_object **) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_figure);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_warning("figure");
+ return NULL;
+}
+
+inline static mp_graphic_object **mplib_aux_is_gr_object(lua_State *L, int n)
+{
+ mp_graphic_object **p = (mp_graphic_object **) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_object);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_warning("object");
+ return NULL;
+}
+
+/*tex In the next array entry 0 is not used */
+
+static int mplib_values_type[mp_stop_bounds_code + 1] = { 0 };
+static int mplib_values_knot[6] = { 0 };
+
+static void mplib_aux_initialize_lua(lua_State *L)
+{
+ (void) L;
+ mplib_values_type[mp_fill_code] = lua_key_index(fill);
+ mplib_values_type[mp_stroked_code] = lua_key_index(outline);
+ mplib_values_type[mp_start_clip_code] = lua_key_index(start_clip);
+ mplib_values_type[mp_start_group_code] = lua_key_index(start_group);
+ mplib_values_type[mp_start_bounds_code] = lua_key_index(start_bounds);
+ mplib_values_type[mp_stop_clip_code] = lua_key_index(stop_clip);
+ mplib_values_type[mp_stop_group_code] = lua_key_index(stop_group);
+ mplib_values_type[mp_stop_bounds_code] = lua_key_index(stop_bounds);
+
+ mplib_values_knot[mp_endpoint_knot] = lua_key_index(endpoint);
+ mplib_values_knot[mp_explicit_knot] = lua_key_index(explicit);
+ mplib_values_knot[mp_given_knot] = lua_key_index(given);
+ mplib_values_knot[mp_curl_knot] = lua_key_index(curl);
+ mplib_values_knot[mp_open_knot] = lua_key_index(open);
+ mplib_values_knot[mp_end_cycle_knot] = lua_key_index(end_cycle);
+}
+
+static void mplib_aux_push_pentype(lua_State *L, mp_gr_knot h)
+{
+ if (h && h == h->next) {
+ lua_push_value_at_key(L, type, elliptical);
+ }
+}
+
+static int mplib_set_tolerance(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mplib_aux_set_bend_tolerance(L, luaL_optnumber(L, 2, default_bend_tolerance));
+ mplib_aux_set_move_tolerance(L, luaL_optnumber(L, 3, default_move_tolerance));
+ }
+ return 0;
+}
+
+static int mplib_get_tolerance(lua_State *L)
+{
+
+ if (lua_type(L, 1) == LUA_TUSERDATA) {
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
+ lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
+ return 2;
+ } else {
+ return 0;
+ }
+ } else {
+ lua_pushnumber(L, default_bend_tolerance);
+ lua_pushnumber(L, default_move_tolerance);
+ return 2;
+ }
+}
+
+/*tex
+
+ We start by defining the needed callback routines for the library. We could store them per
+ instance but it has no advantages so that will be done when we feel the need.
+
+*/
+
+static int mplib_aux_register_function(lua_State *L, int old_id)
+{
+ if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) {
+ return 0;
+ } else {
+ lua_pushvalue(L, -1);
+ if (old_id) {
+ luaL_unref(L, LUA_REGISTRYINDEX, old_id);
+ }
+ return luaL_ref(L, LUA_REGISTRYINDEX); /*tex |new_id| */
+ }
+}
+
+static int mplib_aux_find_file_function(lua_State *L, MP_options *options)
+{
+ options->find_file_id = mplib_aux_register_function(L, options->find_file_id);
+ return (! options->find_file_id);
+}
+
+static int mplib_aux_run_script_function(lua_State *L, MP_options *options)
+{
+ options->run_script_id = mplib_aux_register_function(L, options->run_script_id);
+ return (! options->run_script_id);
+}
+
+static int mplib_aux_run_internal_function(lua_State *L, MP_options *options)
+{
+ options->run_internal_id = mplib_aux_register_function(L, options->run_internal_id);
+ return (! options->run_internal_id);
+}
+
+static int mplib_aux_make_text_function(lua_State *L, MP_options *options)
+{
+ options->make_text_id = mplib_aux_register_function(L, options->make_text_id);
+ return (! options->make_text_id);
+}
+
+static int mplib_aux_run_logger_function(lua_State *L, MP_options *options)
+{
+ options->run_logger_id = mplib_aux_register_function(L, options->run_logger_id);
+ return (! options->run_logger_id);
+}
+
+static int mplib_aux_run_overload_function(lua_State *L, MP_options *options)
+{
+ options->run_overload_id = mplib_aux_register_function(L, options->run_overload_id);
+ return (! options->run_overload_id);
+}
+
+static int mplib_aux_run_error_function(lua_State *L, MP_options *options)
+{
+ options->run_error_id = mplib_aux_register_function(L, options->run_error_id);
+ return (! options->run_error_id);
+}
+
+static int mplib_aux_open_file_function(lua_State *L, MP_options *options)
+{
+ options->open_file_id = mplib_aux_register_function(L, options->open_file_id);
+ return (! options->open_file_id);
+}
+
+static char *mplib_aux_find_file(MP mp, const char *fname, const char *fmode, int ftype)
+{
+ if (mp->find_file_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ char *s = NULL;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->find_file_id);
+ lua_pushstring(L, fname);
+ lua_pushstring(L, fmode);
+ if (ftype > mp_filetype_text) {
+ lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
+ } else {
+ lua_pushstring(L, mplib_filetype_names[ftype]);
+ }
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ tex_formatted_warning("mplib", "find file: %s", lua_tostring(L, -1));
+ } else {
+ s = lmt_string_from_index(L, -1);
+ }
+ lua_settop(L, stacktop);
+ return s;
+ } else if (fmode[0] != 'r' || (! access(fname, R_OK)) || ftype) {
+ // return lmt_generic_strdup(fname);
+ return lmt_memory_strdup(fname);
+ }
+ return NULL;
+}
+
+/*
+
+ In retrospect we could have has a more granular approach: a flag that tells that the result is
+ a string to be scanned. Maybe I'll that anyway but it definitely means an adaption of the low
+ level interface we have in MetaFun. Also, the interface would be a bit ugly because then we
+ would like to handle values as in the 'whatever' function which in turn means a flag indicating
+ that a string is a string and not something to be scanned as tokens, and being compatible then
+ means that this flag comes after the string which kind of conflicts with multiple (number)
+ arguments ... in the end we gain too little to accept such an ugly mess as option. So, we stick
+ to:
+
+ -- nil : nothing is injected and no scanning will happen
+ -- string : passed on in order to be scanned
+ -- table : passed on concatenated in order to be scanned
+ -- number : injected as numeric (so no scanning later on)
+ -- boolean : injected as boolean (so no scanning later on)
+
+ and dealing with other datatypes is delegated to the injectors.
+
+*/
+
+static int mplib_aux_with_path(lua_State *L, MP mp, int index, int what, int multiple);
+
+static void mplib_aux_inject_whatever(lua_State *L, MP mp, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TBOOLEAN:
+ mp_push_boolean_value(mp, lua_toboolean(L, index));
+ break;
+ case LUA_TNUMBER:
+ mp_push_numeric_value(mp, lua_tonumber(L, index));
+ break;
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *s = lua_tolstring(L, index, &l);
+ mp_push_string_value(mp, s, (int) l);
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
+ /* table of tables */
+ lua_pop(L, 1);
+ mplib_aux_with_path(L, mp, index, 1, 0);
+ } else {
+ lua_pop(L, 1);
+ switch (lua_rawlen(L, index)) {
+ case 2 :
+ mp_push_pair_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0)
+ );
+ break;
+ case 3 :
+ mp_push_color_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0)
+ );
+ break;
+ case 4 :
+ mp_push_cmykcolor_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0),
+ lmt_number_from_table(L, index, 4, 0.0)
+ );
+ break;
+ case 6 :
+ mp_push_transform_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0),
+ lmt_number_from_table(L, index, 4, 0.0),
+ lmt_number_from_table(L, index, 5, 0.0),
+ lmt_number_from_table(L, index, 6, 0.0)
+ );
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static char *mplib_aux_return_whatever(lua_State *L, MP mp, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TBOOLEAN:
+ mp_push_boolean_value(mp, lua_toboolean(L, index));
+ break;
+ case LUA_TNUMBER:
+ mp_push_numeric_value(mp, lua_tonumber(L, index));
+ break;
+ /* A string is passed to scantokens. */
+ case LUA_TSTRING:
+ return lmt_string_from_index(L, index);
+ /*tex A table is concatenated and passed to scantokens. */
+ case LUA_TTABLE:
+ {
+ luaL_Buffer b;
+ lua_Integer n = (lua_Integer) lua_rawlen(L, index);
+ luaL_buffinit(L, &b);
+ for (lua_Integer i = 1; i <= n; i++) {
+ lua_rawgeti(L, index, i);
+ luaL_addvalue(&b);
+ lua_pop(L, 1);
+ }
+ luaL_pushresult(&b);
+ return lmt_string_from_index(L, -1);
+ }
+ }
+ return NULL;
+}
+
+static char *mplib_aux_run_script(MP mp, const char *str, size_t len, int n)
+{
+ if (mp->run_script_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_script_id);
+ if (str) {
+ lua_pushlstring(L, str, len);
+ } else if (n > 0) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ ++mplib_state.script_callbacks;
+ if (lua_pcall(L, 1, 2, 0)) {
+ tex_formatted_warning("mplib", "run script: %s", lua_tostring(L, -1));
+ } else if (lua_toboolean(L, -1)) {
+ /* value boolean */
+ mplib_aux_inject_whatever(L, mp, -2);
+ lua_settop(L, stacktop);
+ return NULL;
+ } else {
+ /* value nil */
+ char *s = mplib_aux_return_whatever(L, mp, -2);
+ lua_settop(L, stacktop);
+ return s;
+ }
+ }
+ return NULL;
+}
+
+void mplib_aux_run_internal(MP mp, int action, int n, int type, const char *name)
+{
+ if (mp->run_internal_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ ++mplib_state.script_callbacks; /* maybe a special counter */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_internal_id);
+ lua_pushinteger(L, action); /* 0=initialize, 1=save, 2=restore */
+ lua_pushinteger(L, n);
+ if (name) {
+ lua_pushinteger(L, type);
+ lua_pushstring(L, name);
+ if (! lua_pcall(L, 4, 0, 0)) {
+ return;
+ }
+ } else {
+ if (lua_pcall(L, 2, 0, 0)) {
+ return;
+ }
+ }
+ tex_formatted_warning("mplib", "run internal: %s", lua_tostring(L, -1));
+ }
+}
+
+static char *mplib_aux_make_text(MP mp, const char *str, size_t len, int mode)
+{
+ if (mp->make_text_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ char *s = NULL;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->make_text_id);
+ lua_pushlstring(L, str, len);
+ lua_pushinteger(L, mode);
+ ++mplib_state.text_callbacks;
+ if (lua_pcall(L, 2, 1, 0)) {
+ tex_formatted_warning("mplib", "make text: %s", lua_tostring(L, -1));
+ } else {
+ s = lmt_string_from_index(L, -1);
+ }
+ lua_settop(L, stacktop);
+ return s;
+ }
+ return NULL;
+}
+
+static void mplib_aux_run_logger(MP mp, int target, const char *str, size_t len)
+{
+ if (mp->run_logger_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_logger_id);
+ lua_pushinteger(L, target);
+ lua_pushlstring(L, str, len);
+ ++mplib_state.log_callbacks;
+ if (lua_pcall(L, 2, 0, 0)) {
+ tex_formatted_warning("mplib", "run logger: %s", lua_tostring(L, -1));
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+static int mplib_aux_run_overload(MP mp, int property, const char *str, int mode)
+{
+ int quit = 0;
+ if (mp->run_overload_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_overload_id);
+ lua_pushinteger(L, property);
+ lua_pushstring(L, str);
+ lua_pushinteger(L, mode);
+ ++mplib_state.overload_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ tex_formatted_warning("mplib", "run overload: %s", lua_tostring(L, -1));
+ quit = 1;
+ } else {
+ quit = lua_toboolean(L, -1);
+ }
+ lua_settop(L, stacktop);
+ }
+ return quit;
+}
+
+static void mplib_aux_run_error(MP mp, const char *str, const char *help, int interaction)
+{
+ if (mp->run_error_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_error_id);
+ lua_pushstring(L, str);
+ lua_pushstring(L, help);
+ lua_pushinteger(L, interaction);
+ ++mplib_state.error_callbacks;
+ if (lua_pcall(L, 3, 0, 0)) {
+ tex_formatted_warning("mplib", "run error: %s", lua_tostring(L, -1));
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+/*
+
+ We keep all management in Lua, so for now we don't create an object. Files are normally closed
+ anyway. We can always make it nicer. The opener has to return an integer. A value zero indicates
+ that no file is opened.
+
+*/
+
+/* index needs checking, no need for pointer (was old) */
+
+static void *mplib_aux_open_file(MP mp, const char *fname, const char *fmode, int ftype)
+{
+ if (mp->open_file_id) {
+ int *index = mp_memory_allocate(sizeof(int));
+ if (index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->open_file_id);
+ lua_pushstring(L, fname);
+ lua_pushstring(L, fmode);
+ if (ftype > mp_filetype_text) {
+ lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
+ } else {
+ lua_pushstring(L, mplib_filetype_names[ftype]);
+ }
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ *((int*) index) = 0;
+ } else if (lua_istable(L, -1)) {
+ lua_pushvalue(L, -1);
+ *((int*) index) = luaL_ref(L, LUA_REGISTRYINDEX);
+ } else {
+ tex_normal_warning("mplib", "open file: table expected");
+ *((int*) index) = 0;
+ }
+ lua_settop(L, stacktop);
+ return index;
+ }
+ }
+ return NULL;
+}
+
+# define mplib_pop_function(idx,tag) \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, idx); \
+ lua_push_key(tag); \
+ lua_rawget(L, -2);
+
+static void mplib_aux_close_file(MP mp, void *index)
+{
+ if (mp->open_file_id && index) {
+ int idx = *((int*) index);
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ mplib_pop_function(idx, close)
+ if (lua_isfunction(L, -1)) {
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 0, 0, 0)) {
+ /* no message */
+ } else {
+ /* nothing to be done here */
+ }
+ }
+ /*
+ if (index) {
+ luaL_unref(L, idx));
+ }
+ */
+ lua_settop(L, stacktop);
+ mp_memory_free(index);
+ }
+}
+
+static char *mplib_aux_read_file(MP mp, void *index, size_t *size)
+{
+ if (mp->open_file_id && index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ int idx = *((int*) index);
+ char *s = NULL;
+ mplib_pop_function(idx, reader)
+ if (lua_isfunction(L, -1)) {
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 0, 1, 0)) {
+ *size = 0;
+ } else if (lua_type(L, -1) == LUA_TSTRING) {
+ s = lmt_lstring_from_index(L, -1, size);
+ }
+ }
+ lua_settop(L, stacktop);
+ return s;
+ }
+ return NULL;
+}
+
+static void mplib_aux_write_file(MP mp, void *index, const char *s)
+{
+ if (mp->open_file_id && index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ int idx = *((int*) index);
+ mplib_pop_function(idx, writer)
+ if (lua_isfunction(L, -1)) {
+ lua_pushstring(L, s);
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 1, 0, 0)) {
+ /* no message */
+ } else {
+ /* nothing to be done here */
+ }
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+static int mplib_scan_next(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int token = 0;
+ int mode = 0;
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_next_value(mp, keep, &token, &mode, &kind);
+ }
+ lua_pushinteger(L, token);
+ lua_pushinteger(L, mode);
+ lua_pushinteger(L, kind);
+ return 3;
+}
+
+static int mplib_scan_expression(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_expr_value(mp, keep, &kind);
+ }
+ lua_pushinteger(L, kind);
+ return 1;
+}
+
+static int mplib_scan_token(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int token = 0;
+ int mode = 0;
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_token_value(mp, keep, &token, &mode, &kind);
+ }
+ lua_pushinteger(L, token);
+ lua_pushinteger(L, mode);
+ lua_pushinteger(L, kind);
+ return 3;
+}
+
+static int mplib_skip_token(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ lua_pushboolean(L, mp ? mp_skip_token_value(mp, lmt_tointeger(L, 2)) : 0);
+ return 1;
+}
+
+static int mplib_scan_symbol(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int keep = 0 ;
+ int expand = 1 ;
+ int top = lua_gettop(L) ;
+ char *s = NULL;
+ if (top > 2) { // no need to check
+ expand = lua_toboolean(L, 3);
+ }
+ if (top > 1) { // no need to check
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_symbol_value(mp, keep, &s, expand) ;
+ if (s) {
+ /* we could do without the copy */
+ lua_pushstring(L, s);
+ mp_memory_free(s);
+ return 1;
+ }
+ }
+ lua_pushliteral(L,"");
+ return 1;
+}
+
+static int mplib_scan_property(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int keep = 0 ;
+ int top = lua_gettop(L) ;
+ int type = 0 ;
+ int property = 0 ;
+ int detail = 0 ;
+ char *s = NULL;
+ if (top > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_property_value(mp, keep, &type, &s, &property, &detail);
+ if (s) {
+ lua_pushinteger(L, type);
+ lua_pushstring(L, s);
+ lua_pushinteger(L, property);
+ lua_pushinteger(L, detail);
+ mp_memory_free(s);
+ return 4;
+ }
+ }
+ return 0;
+}
+
+/*tex
+ A circle has 8 points and a square 4 so let's just start with 8 slots in the table.
+*/
+
+static int aux_is_curved_gr(mp_gr_knot ith, mp_gr_knot pth, lua_Number tolerance)
+{
+ lua_Number d = pth->left_x - ith->right_x;
+ if (fabs(ith->right_x - ith->x_coord - d) <= tolerance && fabs(pth->x_coord - pth->left_x - d) <= tolerance) {
+ d = pth->left_y - ith->right_y;
+ if (fabs(ith->right_y - ith->y_coord - d) <= tolerance && fabs(pth->y_coord - pth->left_y - d) <= tolerance) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int aux_is_duplicate_gr(mp_gr_knot pth, mp_gr_knot nxt, lua_Number tolerance)
+{
+ return (fabs(pth->x_coord - nxt->x_coord) <= tolerance && fabs(pth->y_coord - nxt->y_coord) <= tolerance);
+}
+
+# define valid_knot_type(t) (t >= mp_endpoint_knot && t <= mp_end_cycle_knot) /* pens can act weird here */
+
+// -68.031485 2.83464 l
+// -68.031485 2.83464 -68.031485 -2.83464 -68.031485 -2.83464 c
+
+static void mplib_aux_push_path(lua_State *L, mp_gr_knot h, int ispen, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (h) {
+ int i = 0;
+ mp_gr_knot p = h;
+ mp_gr_knot q = h;
+ int iscycle = 1;
+ lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
+ do {
+ mp_gr_knot n = p->next;
+ int lt = p->left_type;
+ int rt = p->right_type;
+ if (ispen) {
+ lua_createtable(L, 0, 6);
+ // } else if (i > 0 && p != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) {
+ } else if (i > 0 && p != h && n != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) {
+ n->left_x = p->left_x;
+ n->left_y = p->left_y;
+ goto NEXTONE;
+ } else {
+ int ln = lt != mp_explicit_knot;
+ int rn = rt != mp_explicit_knot;
+ int ic = i > 0 && aux_is_curved_gr(q, p, bendtolerance);
+ int st = p->state;
+ lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0) + (st ? 1: 0));
+ if (ln && valid_knot_type(lt)) {
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
+ }
+ if (rn && valid_knot_type(rt)) {
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
+ }
+ if (ic) {
+ lua_push_boolean_at_key(L, curved, 1);
+ }
+ if (st) {
+ lua_push_integer_at_key(L, state, st);
+ }
+ lua_push_number_at_key(L, x_coord, p->x_coord);
+ lua_push_number_at_key(L, y_coord, p->y_coord);
+ lua_push_number_at_key(L, left_x, p->left_x);
+ lua_push_number_at_key(L, left_y, p->left_y);
+ lua_push_number_at_key(L, right_x, p->right_x);
+ lua_push_number_at_key(L, right_y, p->right_y);
+ lua_rawseti(L, -2, ++i);
+ if (rt == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ }
+ }
+ NEXTONE:
+ q = p;
+ p = n;
+ } while (p && p != h);
+ if (iscycle && i > 1 && aux_is_curved_gr(q, h, bendtolerance)) {
+ lua_rawgeti(L, -1, 1);
+ lua_push_boolean_at_key(L, curved, 1);
+ lua_pop(L, 1);
+ }
+ if (ispen) {
+ lua_push_boolean_at_key(L, pen, 1);
+ }
+ lua_push_boolean_at_key(L, cycle, iscycle);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static int aux_is_curved(MP mp, mp_knot ith, mp_knot pth, lua_Number tolerance)
+{
+ lua_Number d = mp_number_as_double(mp, pth->left_x) - mp_number_as_double(mp, ith->right_x);
+ if (fabs(mp_number_as_double(mp, ith->right_x) - mp_number_as_double(mp, ith->x_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->x_coord) - mp_number_as_double(mp, pth->left_x) - d) <= tolerance) {
+ d = mp_number_as_double(mp, pth->left_y) - mp_number_as_double(mp, ith->right_y);
+ if (fabs(mp_number_as_double(mp, ith->right_y) - mp_number_as_double(mp, ith->y_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->y_coord) - mp_number_as_double(mp, pth->left_y) - d) <= tolerance) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void aux_mplib_knot_to_path(lua_State *L, MP mp, mp_knot h, int ispen, int compact, int check, lua_Number bendtolerance) // maybe also movetolerance
+{
+ int i = 1;
+ mp_knot p = h;
+ mp_knot q = h;
+ int iscycle = 1;
+ lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
+ if (compact) {
+ do {
+ lua_createtable(L, 2, 0);
+ lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
+ lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
+ lua_rawseti(L, -2, i++);
+ if (p->right_type == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ } else {
+ p = p->next;
+ }
+ } while (p && p != h);
+ } else {
+ do {
+ int lt = p->left_type;
+ int rt = p->right_type;
+ int ln = lt != mp_explicit_knot;
+ int rn = rt != mp_explicit_knot;
+ int ic = check && (i > 1) && aux_is_curved(mp, q, p, bendtolerance);
+ lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0));
+ if (ln && valid_knot_type(lt)) {
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
+ } else {
+ /* a pen */
+ }
+ if (rn && valid_knot_type(rt)) {
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
+ } else {
+ /* a pen */
+ }
+ if (ic) {
+ lua_push_boolean_at_key(L, curved, 1);
+ }
+ lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
+ lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
+ lua_push_number_at_index(L, 3, mp_number_as_double(mp, p->left_x));
+ lua_push_number_at_index(L, 4, mp_number_as_double(mp, p->left_y));
+ lua_push_number_at_index(L, 5, mp_number_as_double(mp, p->right_x));
+ lua_push_number_at_index(L, 6, mp_number_as_double(mp, p->right_y));
+ lua_rawseti(L, -2, i++);
+ if (rt == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ } else {
+ q = p;
+ p = p->next;
+ }
+ } while (p && p != h);
+ if (check && iscycle && i > 1 && aux_is_curved(mp, q, h, bendtolerance)) {
+ lua_rawgeti(L, -1, 1);
+ lua_push_boolean_at_key(L, curved, 1);
+ lua_pop(L, 1);
+ }
+ }
+ if (ispen) {
+ lua_push_boolean_at_key(L, pen, 1);
+ }
+ lua_push_boolean_at_key(L, cycle, iscycle);
+}
+
+/*tex
+
+ A couple of scanners. I know what I want to change but not now. First some longer term
+ experiments.
+
+*/
+
+# define push_number_in_slot(L,i,n) \
+ lua_pushnumber(L, n); \
+ lua_rawseti(L, -2, i);
+
+# define kind_of_expression(n) \
+ lmt_optinteger(L, n, 0)
+
+static int mplib_scan_string(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ char *s = NULL;
+ size_t l = 0;
+ mp_scan_string_value(mp, kind_of_expression(2), &s, &l) ;
+ if (s) {
+ lua_pushlstring(L, s, l);
+ return 1;
+ }
+ }
+ lua_pushliteral(L,"");
+ return 1;
+}
+
+static int mplib_scan_boolean(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int b = 0;
+ if (mp) {
+ mp_scan_boolean_value(mp, kind_of_expression(2), &b);
+ }
+ lua_pushboolean(L, b);
+ return 1;
+}
+
+static int mplib_scan_numeric(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double d = 0.0;
+ if (mp) {
+ mp_scan_numeric_value(mp, kind_of_expression(2), &d);
+ }
+ lua_pushnumber(L, d);
+ return 1;
+}
+
+static int mplib_scan_integer(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double d = 0.0;
+ if (mp) {
+ mp_scan_numeric_value(mp, kind_of_expression(2), &d);
+ }
+ lua_pushinteger(L, (int) d); /* floored */
+ return 1;
+}
+
+static int mplib_scan_pair(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double x = 0.0;
+ double y = 0.0;
+ if (mp) {
+ mp_scan_pair_value(mp, kind_of_expression(3), &x, &y);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 2, 0);
+ push_number_in_slot(L, 1, x);
+ push_number_in_slot(L, 2, y);
+ return 1;
+ } else {
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ return 2;
+ }
+}
+
+static int mplib_scan_color(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double r = 0.0;
+ double g = 0.0;
+ double b = 0.0;
+ if (mp) {
+ mp_scan_color_value(mp, kind_of_expression(3), &r, &g, &b);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 3, 0);
+ push_number_in_slot(L, 1, r);
+ push_number_in_slot(L, 2, g);
+ push_number_in_slot(L, 3, b);
+ return 1;
+ } else {
+ lua_pushnumber(L, r);
+ lua_pushnumber(L, g);
+ lua_pushnumber(L, b);
+ return 3;
+ }
+}
+
+static int mplib_scan_cmykcolor(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double c = 0.0;
+ double m = 0.0;
+ double y = 0.0;
+ double k = 0.0;
+ if (mp) {
+ mp_scan_cmykcolor_value(mp, kind_of_expression(3), &c, &m, &y, &k);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 4, 0);
+ push_number_in_slot(L, 1, c);
+ push_number_in_slot(L, 2, m);
+ push_number_in_slot(L, 3, y);
+ push_number_in_slot(L, 4, k);
+ return 1;
+ } else {
+ lua_pushnumber(L, c);
+ lua_pushnumber(L, m);
+ lua_pushnumber(L, y);
+ lua_pushnumber(L, k);
+ return 4;
+ }
+}
+
+static int mplib_scan_transform(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double x = 0.0;
+ double y = 0.0;
+ double xx = 0.0;
+ double xy = 0.0;
+ double yx = 0.0;
+ double yy = 0.0;
+ if (mp) {
+ mp_scan_transform_value(mp, kind_of_expression(3), &x, &y, &xx, &xy, &yx, &yy);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 6, 0);
+ push_number_in_slot(L, 1, x);
+ push_number_in_slot(L, 2, y);
+ push_number_in_slot(L, 3, xx);
+ push_number_in_slot(L, 4, xy);
+ push_number_in_slot(L, 5, yx);
+ push_number_in_slot(L, 6, yy);
+ return 1;
+ } else {
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ lua_pushnumber(L, xx);
+ lua_pushnumber(L, xy);
+ lua_pushnumber(L, yx);
+ lua_pushnumber(L, yy);
+ return 6;
+ }
+}
+
+static int mplib_scan_path(lua_State *L) /* 1=mp 2=compact 3=kind(prim) 4=check */
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mp_knot p = NULL;
+ lua_Number t = mplib_aux_get_bend_tolerance(L, 1); /* iuservalue */
+ mp_scan_path_value(mp, kind_of_expression(3), &p);
+ if (p) {
+ aux_mplib_knot_to_path(L, mp, p, 0, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_scan_pen(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mp_knot p = NULL ;
+ lua_Number t = mplib_aux_get_bend_tolerance(L, 1);
+ mp_scan_path_value(mp, kind_of_expression(3), &p) ;
+ if (p) {
+ aux_mplib_knot_to_path(L, mp, p, 1, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_string(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ size_t l = 0;
+ const char *s = lua_tolstring(L, 2, &l);
+ mp_push_string_value(mp, s, (int) l);
+ }
+ return 0;
+}
+
+static int mplib_inject_boolean(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int b = lua_toboolean(L, 2);
+ mp_push_boolean_value(mp, b);
+ }
+ return 0;
+}
+
+static int mplib_inject_numeric(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ double d = lua_tonumber(L, 2);
+ mp_push_numeric_value(mp, d);
+ }
+ return 0;
+}
+
+static int mplib_inject_integer(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int i = lmt_tointeger(L, 2);
+ mp_push_integer_value(mp, i);
+ }
+ return 0;
+}
+
+static int mplib_inject_pair(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_pair_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_pair_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_color(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_color_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_color_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_cmykcolor(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_cmykcolor_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0),
+ luaL_optnumber(L, 5, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_cmykcolor_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0),
+ lmt_number_from_table(L, 2, 4, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_transform(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_transform_value(mp,
+ luaL_optnumber(L, 2, 0), // 1
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0),
+ luaL_optnumber(L, 5, 0), // 1
+ luaL_optnumber(L, 6, 0),
+ luaL_optnumber(L, 7, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_transform_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0), // 1.0
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0),
+ lmt_number_from_table(L, 2, 4, 0.0), // 1.0
+ lmt_number_from_table(L, 2, 5, 0.0),
+ lmt_number_from_table(L, 2, 6, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_new(lua_State *L)
+{
+ MP *mpud = lua_newuserdatauv(L, sizeof(MP *), 2);
+ if (mpud) {
+ MP mp = NULL;
+ struct MP_options *options = mp_options();
+ lua_Number bendtolerance = default_bend_tolerance;
+ lua_Number movetolerance = default_move_tolerance;
+ options->userdata = (void *) L;
+ options->job_name = NULL;
+ options->extensions = 0 ;
+ options->utf8_mode = 0;
+ options->text_mode = 0;
+ options->show_mode = 0;
+ options->halt_on_error = 0;
+ options->find_file = mplib_aux_find_file;
+ options->run_script = mplib_aux_run_script;
+ options->run_internal = mplib_aux_run_internal;
+ options->run_logger = mplib_aux_run_logger;
+ options->run_overload = mplib_aux_run_overload;
+ options->run_error = mplib_aux_run_error;
+ options->make_text = mplib_aux_make_text;
+ options->open_file = mplib_aux_open_file;
+ options->close_file = mplib_aux_close_file;
+ options->read_file = mplib_aux_read_file;
+ options->write_file = mplib_aux_write_file;
+ options->shipout_backend = mplib_shipout_backend;
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ const char *s = lua_tostring(L, -2);
+ if (lua_key_eq(s, random_seed)) {
+ options->random_seed = (int) lua_tointeger(L, -1);
+ } else if (lua_key_eq(s, interaction)) {
+ options->interaction = luaL_checkoption(L, -1, "silent", mplib_interaction_options);
+ } else if (lua_key_eq(s, job_name)) {
+ // options->job_name = lmt_generic_strdup(lua_tostring(L, -1));
+ options->job_name = lmt_memory_strdup(lua_tostring(L, -1));
+ } else if (lua_key_eq(s, find_file)) {
+ if (mplib_aux_find_file_function(L, options)) {
+ tex_normal_warning("mplib", "find file: function expected");
+ }
+ } else if (lua_key_eq(s, run_script)) {
+ if (mplib_aux_run_script_function(L, options)) {
+ tex_normal_warning("mplib", "run script: function expected");
+ }
+ } else if (lua_key_eq(s, run_internal)) {
+ if (mplib_aux_run_internal_function(L, options)) {
+ tex_normal_warning("mplib", "run internal: function expected");
+ }
+ } else if (lua_key_eq(s, make_text)) {
+ if (mplib_aux_make_text_function(L, options)) {
+ tex_normal_warning("mplib", "make text: function expected");
+ }
+ } else if (lua_key_eq(s, extensions)) {
+ options->extensions = (int) lua_tointeger(L, -1);
+ } else if (lua_key_eq(s, math_mode)) {
+ options->math_mode = luaL_checkoption(L, -1, "scaled", mplib_math_options);
+ } else if (lua_key_eq(s, utf8_mode)) {
+ options->utf8_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, text_mode)) {
+ options->text_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, show_mode)) {
+ options->show_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, halt_on_error)) {
+ options->halt_on_error = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, run_logger)) {
+ if (mplib_aux_run_logger_function(L, options)) {
+ tex_normal_warning("mplib", "run logger: function expected");
+ }
+ } else if (lua_key_eq(s, run_overload)) {
+ if (mplib_aux_run_overload_function(L, options)) {
+ tex_normal_warning("mplib", "run overload: function expected");
+ }
+ } else if (lua_key_eq(s, run_error)) {
+ if (mplib_aux_run_error_function(L, options)) {
+ tex_normal_warning("mplib", "run error: function expected");
+ }
+ } else if (lua_key_eq(s, open_file)) {
+ if (mplib_aux_open_file_function(L, options)) {
+ tex_normal_warning("mplib", "open file: function expected");
+ }
+ } else if (lua_key_eq(s, bend_tolerance)) {
+ bendtolerance = lua_tonumber(L, -1);
+ } else if (lua_key_eq(s, move_tolerance)) {
+ movetolerance = lua_tonumber(L, -1);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ if (! options->job_name || ! *(options->job_name)) {
+ mp_memory_free(options); /* leaks */
+ tex_normal_warning("mplib", "job_name is not set");
+ goto BAD;
+ }
+ mp = mp_initialize(options);
+ mp_memory_free(options); /* leaks */
+ if (mp) {
+ *mpud = mp;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_INSTANCE);
+ lua_setmetatable(L, -2);
+ return 1;
+ }
+ }
+ BAD:
+ lua_pushnil(L);
+ return 1;
+}
+
+# define mplib_collect_id(id) do { \
+ if (id) { \
+ luaL_unref(L, LUA_REGISTRYINDEX, id); \
+ } \
+} while(0)
+
+static int mplib_instance_collect(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ int run_logger_id = (mp)->run_logger_id;
+ mplib_collect_id((mp)->find_file_id);
+ mplib_collect_id((mp)->run_script_id);
+ mplib_collect_id((mp)->run_internal_id);
+ mplib_collect_id((mp)->run_overload_id);
+ mplib_collect_id((mp)->run_error_id);
+ mplib_collect_id((mp)->make_text_id);
+ mplib_collect_id((mp)->open_file_id);
+ mp_finish(mp);
+ *mpud = NULL;
+ mplib_collect_id(run_logger_id);
+ }
+ return 0;
+}
+
+static int mplib_instance_tostring(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushfstring(L, "<mp.instance %p>", mp);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_aux_wrapresults(lua_State *L, mp_run_data *res, int status, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ lua_newtable(L);
+ if (res->edges) {
+ struct mp_edge_object *p = res->edges;
+ int i = 1;
+ lua_push_key(fig);
+ lua_newtable(L);
+ while (p) {
+ struct mp_edge_object **v = lua_newuserdatauv(L, sizeof(struct mp_edge_object *), 2);
+ *v = p;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_FIGURE);
+ lua_setmetatable(L, -2);
+ lua_rawseti(L, -2, i);
+ i++;
+ p = p->next;
+ }
+ lua_rawset(L,-3);
+ res->edges = NULL;
+ }
+ lua_push_integer_at_key(L, status, status);
+ return 1;
+}
+
+static int mplib_execute(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ /* no string in slot 2 or an empty string means that we already filled the terminal */
+ size_t l = 0;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ const char *s = lua_isstring(L, 2) ? lua_tolstring(L, 2, &l) : NULL;
+ int h = mp_execute(mp, s, l);
+ mp_run_data *res = mp_rundata(mp);
+ return mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int mplib_finish(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ int h = mp_execute(mp, NULL, 0);
+ mp_run_data *res = mp_rundata(mp);
+ int i = mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
+ mp_finish(mp);
+ *mpud = NULL;
+ return i;
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_showcontext(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ mp_show_context(mp);
+ }
+ return 0;
+}
+
+static int mplib_gethashentry(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ char *name = (char *) lua_tostring(L, 2);
+ if (name) {
+ mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol(mp, name);
+ if (s) {
+ mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
+ lua_pushinteger(L, s->type);
+ lua_pushinteger(L, s->property);
+ if (q) {
+ lua_pushinteger(L, q->type);
+ return 3;
+ } else {
+ return 2;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int mplib_gethashentries(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ int full = lua_toboolean(L, 2);
+ if (mp_initialize_symbol_traverse(mp)) {
+ size_t n = 0;
+ lua_newtable(L);
+ while (1) {
+ mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol_traverse(mp);
+ if (s) {
+ if (full) {
+ mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
+ lua_createtable(L, (q || s->property == 0x1) ? 4 : 3, 0);
+ lua_pushinteger(L, s->type);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, s->property);
+ lua_rawseti(L, -2, 2);
+ lua_pushlstring(L, (const char *) s->text->str, s->text->len);
+ lua_rawseti(L, -2, 3);
+ if (q) {
+ lua_pushinteger(L, q->type);
+ lua_rawseti(L, -2, 4);
+ } else if (s->property == 0x1) {
+ lua_pushinteger(L, s->v.data.indep.serial);
+ lua_rawseti(L, -2, 4);
+ }
+ } else {
+ lua_pushlstring(L, (const char *) s->text->str, s->text->len);
+ }
+ lua_rawseti(L, -2, ++n);
+ } else {
+ break;
+ }
+ }
+ mp_kill_symbol_traverse(mp);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_version(lua_State *L)
+{
+ char *s = mp_metapost_version();
+ lua_pushstring(L, s);
+ mp_memory_free(s);
+ return 1;
+}
+
+static int mplib_getstatistics(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_createtable(L, 0, 9);
+ lua_push_integer_at_key(L, memory, mp->var_used); /* bytes of node memory */
+ lua_push_integer_at_key(L, hash, mp->st_count);
+ lua_push_integer_at_key(L, parameters, mp->max_param_stack); /* allocated: mp->param_size */
+ lua_push_integer_at_key(L, input, mp->max_in_stack); /* allocated: mp->stack_size */
+ lua_push_integer_at_key(L, tokens, mp->num_token_nodes);
+ lua_push_integer_at_key(L, pairs, mp->num_pair_nodes);
+ lua_push_integer_at_key(L, knots, mp->num_knot_nodes);
+ lua_push_integer_at_key(L, nodes, mp->num_value_nodes);
+ lua_push_integer_at_key(L, symbols, mp->num_symbolic_nodes);
+ lua_push_integer_at_key(L, characters, mp->max_pl_used);
+ lua_push_integer_at_key(L, strings, mp->max_strs_used);
+ lua_push_integer_at_key(L, internals, mp->int_ptr); /* allocates: mp->max_internal */
+ /* lua_push_integer_at_key(L, buffer, mp->max_buf_stack + 1); */ /* allocated: mp->buf_size */
+ /* lua_push_integer_at_key(L, open, mp->in_open_max - file_bottom); */ /* allocated: mp->max_in_open - file_bottom */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_getstatus(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushinteger(L, mp->scanner_status);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_direction(lua_State *L, MP mp, mp_knot p) {
+ double direction_x = (double) lua_tonumber(L, -1);
+ double direction_y = 0;
+ lua_pop(L, 1);
+ lua_push_key(direction_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ direction_y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_direction(mp, p, direction_x, direction_y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_left_curl(lua_State *L, MP mp, mp_knot p) {
+ double curl = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_curl(mp, p, curl) ? 1 : 0;
+}
+
+static int mplib_aux_set_left_tension(lua_State *L, MP mp, mp_knot p) {
+ double tension = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_tension(mp, p, tension) ? 1 : 0;
+}
+
+static int mplib_aux_set_left_control(lua_State *L, MP mp, mp_knot p) {
+ double x = (double) lua_tonumber(L, -1);
+ double y = 0;
+ lua_pop(L, 1);
+ lua_push_key(left_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_control(mp, p, x, y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_right_curl(lua_State *L, MP mp, mp_knot p) {
+ double curl = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_curl(mp, p, curl) ? 1 : 0;
+}
+
+static int mplib_aux_set_right_tension(lua_State *L, MP mp, mp_knot p) {
+ double tension = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_tension(mp, p, tension) ? 1 : 0;
+}
+
+static int mplib_aux_set_right_control(lua_State *L, MP mp, mp_knot p) {
+ double x = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_push_key(right_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ double y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_control(mp, p, x, y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_with_path(lua_State *L, MP mp, int index, int inject, int multiple)
+{
+ // setbuf(stdout, NULL);
+ if (! mp) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, "valid instance expected");
+ return 2;
+ } else if (! lua_istable(L, index) || lua_rawlen(L, index) <= 0) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, "non empty table expected");
+ return 2;
+ } else {
+ mp_knot p = NULL;
+ mp_knot q = NULL;
+ mp_knot first = NULL;
+ const char *errormsg = NULL;
+ int cyclic = 0;
+ int curled = 0;
+ int solve = 0;
+ int numpoints = (int) lua_rawlen(L, index);
+ /*tex
+ As a bonus we check for two keys. When an index is negative we come from the
+ callback in which case we definitely cannot check the rest of the arguments.
+ */
+ if (multiple && lua_type(L, index + 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, index + 1);
+ } else {
+ lua_push_key(close);
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ lua_push_key(cycle); /* wins */
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ if (multiple && lua_type(L, index + 2) == LUA_TBOOLEAN) {
+ curled = lua_toboolean(L, index + 2);
+ } else {
+ lua_push_key(curled);
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ curled = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ /*tex We build up the path. */
+ if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
+ lua_Unsigned len = lua_rawlen(L, -1);
+ lua_pop(L, 1);
+ if (len >= 2) {
+ /* .. : p1 .. controls a and b .. p2 : { p1 a b } */
+ /* -- : p1 .. { curl 1 } .. { curl 1 } .. p2 : { p1 nil nil } */
+ for (int i = 1; i <= numpoints; i++) {
+ if (lua_rawgeti(L, index, i) == LUA_TTABLE) {
+ double x0, y0;
+ lua_rawgeti(L, -1, 1);
+ lua_rawgeti(L, -2, 2);
+ x0 = lua_tonumber(L, -2);
+ y0 = lua_tonumber(L, -1);
+ q = p;
+ p = mp_append_knot_xy(mp, p, x0, y0); /* makes end point */
+ lua_pop(L, 2);
+ if (p) {
+ double x1, y1, x2, y2;
+ if (curled) {
+ x1 = x0;
+ y1 = y0;
+ x2 = x0;
+ y2 = y0;
+ } else {
+ lua_rawgeti(L, -1, 3);
+ lua_rawgeti(L, -2, 4);
+ lua_rawgeti(L, -3, 5);
+ lua_rawgeti(L, -4, 6);
+ x1 = luaL_optnumber(L, -4, x0);
+ y1 = luaL_optnumber(L, -3, y0);
+ x2 = luaL_optnumber(L, -2, x0);
+ y2 = luaL_optnumber(L, -1, y0);
+ lua_pop(L, 4);
+ }
+ mp_set_knot_left_control(mp, p, x1, y1);
+ mp_set_knot_right_control(mp, p, x2, y2);
+ if (! first) {
+ first = p;
+ }
+ } else {
+ errormsg = "knot creation failure";
+ goto BAD;
+ }
+ }
+ /*tex Up the next item */
+ lua_pop(L, 1);
+ }
+ } else if (len > 0) {
+ errormsg = "messy table";
+ goto BAD;
+ } else {
+ for (int i = 1; i <= numpoints; i++) {
+ if (lua_rawgeti(L, index, i) == LUA_TTABLE) {
+ /* We can probably also use the _xy here. */
+ int left_set = 0;
+ int right_set = 0;
+ double x_coord, y_coord;
+ if (! lua_istable(L, -1)) {
+ errormsg = "wrong argument types";
+ goto BAD;
+ }
+ lua_push_key(x_coord);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ errormsg = "missing x coordinate";
+ goto BAD;
+ }
+ x_coord = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_push_key(y_coord);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ errormsg = "missing y coordinate";
+ goto BAD;
+ }
+ y_coord = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ /* */
+ q = p;
+ if (q) {
+ /*tex
+ We have to save the right_tension because |mp_append_knot| trashes it,
+ believing that it is as yet uninitialized .. I need to check this.
+ */
+ double saved_tension = mp_number_as_double(mp, p->right_tension);
+ p = mp_append_knot(mp, p, x_coord, y_coord);
+ if (p) {
+ mp_set_knot_right_tension(mp, q, saved_tension);
+ }
+ } else {
+ p = mp_append_knot(mp, p, x_coord, y_coord);
+ }
+ if (p) {
+ errormsg = "knot creation failure";
+ goto BAD;
+ }
+ /* */
+ if (! first) {
+ first = p;
+ }
+ lua_push_key(left_curl);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_left_curl(L, mp, p)) {
+ errormsg = "failed to set left curl";
+ goto BAD;
+ } else {
+ left_set = 1;
+ solve = 1;
+ }
+ lua_push_key(left_tension);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (left_set) {
+ errormsg = "left side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_left_tension(L, mp, p)) {
+ errormsg = "failed to set left tension";
+ goto BAD;
+ } else {
+ left_set = 1;
+ solve = 1;
+ }
+ lua_push_key(left_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (left_set) {
+ errormsg = "left side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_left_control(L, mp, p)) {
+ errormsg = "failed to set left control";
+ goto BAD;
+ }
+ lua_push_key(right_curl);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_right_curl(L, mp, p)) {
+ errormsg = "failed to set right curl";
+ goto BAD;
+ } else {
+ right_set = 1;
+ solve = 1;
+ }
+ lua_push_key(right_tension);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L,1);
+ } else if (right_set) {
+ errormsg = "right side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_right_tension(L, mp, p)) {
+ errormsg = "failed to set right tension";
+ goto BAD;
+ } else {
+ right_set = 1;
+ solve = 1;
+ }
+ lua_push_key(right_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (right_set) {
+ errormsg = "right side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_right_control(L, mp, p)) {
+ errormsg = "failed to set right control";
+ goto BAD;
+ }
+ lua_push_key(direction_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_direction(L, mp, p)) {
+ errormsg = "failed to set direction";
+ goto BAD;
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ }
+ if (first && p) {
+ /* not: mp_close_path(mp, p, first); */
+ if (cyclic) {
+ p->right_type = mp_explicit_knot;
+ first->left_type = mp_explicit_knot;
+ } else {
+ /* check this on shapes-001.tex and arrows-001.tex */
+ p->right_type = mp_endpoint_knot;
+ first->left_type = mp_endpoint_knot;
+ }
+ p->next = first;
+ if (inject) {
+ if (solve && ! mp_solve_path(mp, first)) {
+ tex_normal_warning("lua", "failed to solve the path");
+ }
+ mp_push_path_value(mp, first);
+ return 0;
+ } else {
+ /*tex We're finished reading arguments so we squeeze the new values back into the table. */
+ if (! mp_solve_path(mp, first)) {
+ errormsg = "failed to solve the path";
+ } else {
+ /* We replace in the original table .. maybe not a good idea at all. */
+ p = first;
+ for (int i = 1; i <= numpoints; i++) {
+ lua_rawgeti(L, -1, i);
+ lua_push_number_at_key(L, left_x, mp_number_as_double(mp, p->left_x));
+ lua_push_number_at_key(L, left_y, mp_number_as_double(mp, p->left_y));
+ lua_push_number_at_key(L, right_x, mp_number_as_double(mp, p->right_x));
+ lua_push_number_at_key(L, right_y, mp_number_as_double(mp, p->right_y));
+ /*tex This is a bit overkill, wiping \unknown */
+ lua_push_nil_at_key(L, left_tension);
+ lua_push_nil_at_key(L, right_tension);
+ lua_push_nil_at_key(L, left_curl);
+ lua_push_nil_at_key(L, right_curl);
+ lua_push_nil_at_key(L, direction_x);
+ lua_push_nil_at_key(L, direction_y);
+ /*tex \unknown\ till here. */
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[p->left_type]);
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[p->right_type]);
+ lua_pop(L, 1);
+ p = p->next;
+ }
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ }
+ } else {
+ errormsg = "invalid path";
+ }
+ BAD:
+ if (p) {
+ mp_free_path(mp, p);
+ }
+ lua_pushboolean(L, 0);
+ if (errormsg) {
+ lua_pushstring(L, errormsg);
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static int mplib_solvepath(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ return mplib_aux_with_path(L, mp, 2, 0, 1);
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_inject_path(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ return mplib_aux_with_path(L, mp, 2, 1, 1);
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_inject_whatever(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mplib_aux_inject_whatever(L, mp, 2);
+ }
+ return 0;
+}
+
+/*tex The next methods are for collecting the results from |fig|. */
+
+static int mplib_figure_collect(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ mp_gr_toss_objects(*hh);
+ *hh = NULL;
+ }
+ return 0;
+}
+
+static int mplib_figure_objects(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ int i = 1;
+ struct mp_graphic_object *p = (*hh)->body;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ lua_newtable(L);
+ while (p) {
+ struct mp_graphic_object **v = lua_newuserdatauv(L, sizeof(struct mp_graphic_object *), 2);
+ *v = p;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_OBJECT);
+ lua_setmetatable(L, -2);
+ lua_rawseti(L, -2, i);
+ i++;
+ p = p->next;
+ }
+ /*tex Prevent a double free: */
+ (*hh)->body = NULL;
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_stacking(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ int stacking = 0; /* This only works when before fetching objects! */
+ if (*hh) {
+ struct mp_graphic_object *p = (*hh)->body;
+ while (p) {
+ if (((mp_shape_object *) p)->stacking) {
+ stacking = 1;
+ break;
+ } else {
+ p = p->next;
+ }
+ }
+ }
+ lua_pushboolean(L, stacking);
+ return 1;
+}
+
+static int mplib_figure_tostring(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushfstring(L, "<mp.figure %p>", *hh);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_width(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->width);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_height(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->height);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_depth(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->depth);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_italic(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->italic);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_charcode(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushinteger(L, (lua_Integer) (*hh)->charcode);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_tolerance(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
+ lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+static int mplib_figure_bounds(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ lua_createtable(L, 4, 0);
+ lua_push_number_at_index(L, 1, (*hh)->minx);
+ lua_push_number_at_index(L, 2, (*hh)->miny);
+ lua_push_number_at_index(L, 3, (*hh)->maxx);
+ lua_push_number_at_index(L, 4, (*hh)->maxy);
+ return 1;
+}
+
+/*tex The methods for the figure objects plus a few helpers. */
+
+static int mplib_object_collect(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ mp_gr_toss_object(*hh);
+ *hh = NULL;
+ }
+ return 0;
+}
+
+static int mplib_object_tostring(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ lua_pushfstring(L, "<mp.object %p>", *hh);
+ return 1;
+}
+
+# define pyth(a,b) (sqrt((a)*(a) + (b)*(b)))
+# define aspect_bound (10.0/65536.0)
+# define aspect_default 1.0
+# define eps 0.0001
+
+static double mplib_aux_coord_range_x(mp_gr_knot h, double dz)
+{
+ double zlo = 0.0;
+ double zhi = 0.0;
+ mp_gr_knot f = h;
+ while (h) {
+ double z = h->x_coord;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->right_x;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->left_x;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ h = h->next;
+ if (h == f) {
+ break;
+ }
+ }
+ return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
+}
+
+static double mplib_aux_coord_range_y(mp_gr_knot h, double dz)
+{
+ double zlo = 0.0;
+ double zhi = 0.0;
+ mp_gr_knot f = h;
+ while (h) {
+ double z = h->y_coord;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->right_y;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->left_y;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ h = h->next;
+ if (h == f) {
+ break;
+ }
+ }
+ return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
+}
+
+static int mplib_object_peninfo(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, -1);
+ if (! *hh) {
+ lua_pushnil(L);
+ return 1;
+ } else {
+ mp_gr_knot p = NULL;
+ mp_gr_knot path = NULL;
+ switch ((*hh)->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ p = ((mp_shape_object *) (*hh))->pen;
+ path = ((mp_shape_object *) (*hh))->path;
+ break;
+ }
+ if (! p || ! path) {
+ lua_pushnil(L);
+ return 1;
+ } else {
+ double wx, wy;
+ double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
+ double width = 1.0;
+ double x_coord = p->x_coord;
+ double y_coord = p->y_coord;
+ double left_x = p->left_x;
+ double left_y = p->left_y;
+ double right_x = p->right_x;
+ double right_y = p->right_y;
+ if ((right_x == x_coord) && (left_y == y_coord)) {
+ wx = fabs(left_x - x_coord);
+ wy = fabs(right_y - y_coord);
+ } else {
+ wx = pyth(left_x - x_coord, right_x - x_coord);
+ wy = pyth(left_y - y_coord, right_y - y_coord);
+ }
+ if ((wy/mplib_aux_coord_range_x(path, wx)) >= (wx/mplib_aux_coord_range_y(path, wy))) {
+ width = wy;
+ } else {
+ width = wx;
+ }
+ tx = x_coord;
+ ty = y_coord;
+ sx = left_x - tx;
+ rx = left_y - ty;
+ ry = right_x - tx;
+ sy = right_y - ty;
+ if (width != 1.0) {
+ if (width == 0.0) {
+ sx = 1.0;
+ sy = 1.0;
+ } else {
+ rx /= width;
+ ry /= width;
+ sx /= width;
+ sy /= width;
+ }
+ }
+ if (fabs(sx) < eps) {
+ sx = eps;
+ }
+ if (fabs(sy) < eps) {
+ sy = eps;
+ }
+ lua_createtable(L,0,7);
+ lua_push_number_at_key(L, width, width);
+ lua_push_number_at_key(L, rx, rx);
+ lua_push_number_at_key(L, sx, sx);
+ lua_push_number_at_key(L, sy, sy);
+ lua_push_number_at_key(L, ry, ry);
+ lua_push_number_at_key(L, tx, tx);
+ lua_push_number_at_key(L, ty, ty);
+ return 1;
+ }
+ }
+}
+
+/*tex Here is a helper that reports the valid field names of the possible objects. */
+
+static void mplib_aux_mplib_push_fields(lua_State* L, const char **fields)
+{
+ lua_newtable(L);
+ for (lua_Integer i = 0; fields[i]; i++) {
+ lua_pushstring(L, fields[i]); /* not yet an index */
+ lua_rawseti(L, -2, i + 1);
+ }
+}
+
+static int mplib_gettype(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ lua_pushinteger(L, (*hh)->type);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_getobjecttypes(lua_State* L)
+{
+ lua_createtable(L, 7, 1);
+ lua_push_key_at_index(L, fill, mp_fill_code);
+ lua_push_key_at_index(L, outline, mp_stroked_code);
+ lua_push_key_at_index(L, start_clip, mp_start_clip_code);
+ lua_push_key_at_index(L, start_group, mp_start_group_code);
+ lua_push_key_at_index(L, start_bounds, mp_start_bounds_code);
+ lua_push_key_at_index(L, stop_clip, mp_stop_clip_code);
+ lua_push_key_at_index(L, stop_group, mp_stop_group_code);
+ lua_push_key_at_index(L, stop_bounds, mp_stop_bounds_code);
+ return 1;
+}
+
+static int mplib_getfields(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TUSERDATA) {
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ const char **fields;
+ switch ((*hh)->type) {
+ case mp_fill_code : fields = mplib_fill_fields; break;
+ case mp_stroked_code : fields = mplib_stroked_fields; break;
+ case mp_start_clip_code : fields = mplib_start_clip_fields; break;
+ case mp_start_group_code : fields = mplib_start_group_fields; break;
+ case mp_start_bounds_code: fields = mplib_start_bounds_fields; break;
+ case mp_stop_clip_code : fields = mplib_stop_clip_fields; break;
+ case mp_stop_group_code : fields = mplib_stop_group_fields; break;
+ case mp_stop_bounds_code : fields = mplib_stop_bounds_fields; break;
+ default : fields = mplib_no_fields; break;
+ }
+ mplib_aux_mplib_push_fields(L, fields);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_createtable(L, 8, 0);
+ mplib_aux_mplib_push_fields(L, mplib_fill_fields); lua_rawseti(L, -2, mp_fill_code);
+ mplib_aux_mplib_push_fields(L, mplib_stroked_fields); lua_rawseti(L, -2, mp_stroked_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_clip_fields); lua_rawseti(L, -2, mp_start_clip_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_group_fields); lua_rawseti(L, -2, mp_start_group_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_bounds_fields); lua_rawseti(L, -2, mp_start_bounds_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_clip_fields); lua_rawseti(L, -2, mp_stop_clip_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_group_fields); lua_rawseti(L, -2, mp_stop_group_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_bounds_fields); lua_rawseti(L, -2, mp_stop_bounds_code);
+ }
+ return 1;
+}
+
+static int mplib_push_values(lua_State *L, const char *list[])
+{
+ lua_newtable(L);
+ for (lua_Integer i = 0; list[i]; i++) {
+ lua_pushstring(L, list[i]);
+ lua_rawseti(L, -2, i);
+ }
+ return 1;
+}
+
+static int mplib_getcodes(lua_State *L)
+{
+ return mplib_push_values(L, mplib_codes);
+}
+
+static int mplib_gettypes(lua_State *L)
+{
+ return mplib_push_values(L, mplib_types);
+}
+
+static int mplib_getcolormodels(lua_State *L)
+{
+ return mplib_push_values(L, mplib_colormodels);
+}
+
+static int mplib_getstates(lua_State *L)
+{
+ return mplib_push_values(L, mplib_states);
+}
+
+static int mplib_getcallbackstate(lua_State *L)
+{
+ lua_createtable(L, 0, 5);
+ lua_push_integer_at_key(L, file, mplib_state.file_callbacks);
+ lua_push_integer_at_key(L, text, mplib_state.text_callbacks);
+ lua_push_integer_at_key(L, script, mplib_state.script_callbacks);
+ lua_push_integer_at_key(L, log, mplib_state.log_callbacks);
+ lua_push_integer_at_key(L, overloaded, mplib_state.overload_callbacks);
+ lua_push_integer_at_key(L, error, mplib_state.error_callbacks);
+ lua_push_integer_at_key(L, warning, mplib_state.warning_callbacks);
+ lua_push_integer_at_key(L, count,
+ mplib_state.file_callbacks + mplib_state.text_callbacks
+ + mplib_state.script_callbacks + mplib_state.log_callbacks
+ + mplib_state.overload_callbacks + mplib_state.error_callbacks
+ + mplib_state.warning_callbacks
+ );
+ return 1;
+}
+
+/*tex
+
+ This assumes that the top of the stack is a table or nil already in the case.
+*/
+
+# define mplib_set_color_objects(pq) \
+object_color_model = pq->color_model; \
+object_color_a = pq->color.a_val; \
+object_color_b = pq->color.b_val; \
+object_color_c = pq->color.c_val; \
+object_color_d = pq->color.d_val;
+
+static void mplib_aux_push_color(lua_State *L, struct mp_graphic_object *p)
+{
+ if (p) {
+ int object_color_model;
+ double object_color_a, object_color_b, object_color_c, object_color_d;
+ switch (p->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ {
+ mp_shape_object *h = (mp_shape_object *) p;
+ mplib_set_color_objects(h);
+ }
+ break;
+ default:
+ object_color_model = mp_no_model;
+ break;
+ }
+ switch (object_color_model) {
+ case mp_grey_model:
+ lua_createtable(L, 1, 0);
+ lua_push_number_at_index(L, 1, object_color_d);
+ break;
+ case mp_rgb_model:
+ lua_createtable(L, 3, 0);
+ lua_push_number_at_index(L, 1, object_color_a);
+ lua_push_number_at_index(L, 2, object_color_b);
+ lua_push_number_at_index(L, 3, object_color_c);
+ break;
+ case mp_cmyk_model:
+ lua_createtable(L, 4, 0);
+ lua_push_number_at_index(L, 1, object_color_a);
+ lua_push_number_at_index(L, 2, object_color_b);
+ lua_push_number_at_index(L, 3, object_color_c);
+ lua_push_number_at_index(L, 4, object_color_d);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+/*tex The dash scale is not exported, the field has no external value. */
+
+static void mplib_aux_push_dash(lua_State *L, struct mp_shape_object *h)
+{
+ if (h && h->dash) {
+ mp_dash_object *d = h->dash;
+ lua_newtable(L); /* we could start at size 2 or so */
+ lua_push_number_at_key(L, offset, d->offset);
+ if (d->array) {
+ int i = 0;
+ lua_push_key(dashes);
+ lua_newtable(L);
+ while (*(d->array + i) != -1) {
+ double ds = *(d->array + i);
+ lua_pushnumber(L, ds);
+ i++;
+ lua_rawseti(L, -2, i);
+ }
+ lua_rawset(L, -3);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void mplib_aux_shape(lua_State *L, const char *s, struct mp_shape_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (lua_key_eq(s, path)) {
+ mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, htap)) {
+ mplib_aux_push_path(L, h->htap, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, pen)) {
+ mplib_aux_push_path(L, h->pen, MPLIB_PEN, bendtolerance, movetolerance);
+ /* pushed in the table at the top */
+ mplib_aux_push_pentype(L, h->pen);
+ } else if (lua_key_eq(s, color)) {
+ mplib_aux_push_color(L, (mp_graphic_object *) h);
+ } else if (lua_key_eq(s, linejoin)) {
+ lua_pushnumber(L, (lua_Number) h->linejoin);
+ } else if (lua_key_eq(s, linecap)) {
+ lua_pushnumber(L, (lua_Number) h->linecap);
+ // } else if (lua_key_eq(s, stacking)) {
+ // lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else if (lua_key_eq(s, miterlimit)) {
+ lua_pushnumber(L, h->miterlimit);
+ } else if (lua_key_eq(s, prescript)) {
+ lua_pushlstring(L, h->pre_script, h->pre_length);
+ } else if (lua_key_eq(s, postscript)) {
+ lua_pushlstring(L, h->post_script, h->post_length);
+ } else if (lua_key_eq(s, dash)) {
+ mplib_aux_push_dash(L, h);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void mplib_aux_start(lua_State *L, const char *s, struct mp_start_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (lua_key_eq(s, path)) {
+ mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, prescript)) {
+ lua_pushlstring(L, h->pre_script, h->pre_length);
+ } else if (lua_key_eq(s, postscript)) {
+ lua_pushlstring(L, h->post_script, h->post_length);
+ // } else if (lua_key_eq(s, stacking)) {
+ // lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+// static void mplib_aux_stop(lua_State *L, const char *s, struct mp_stop_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+// {
+// if (lua_key_eq(s, stacking)) {
+// lua_pushinteger(L, (lua_Integer) h->stacking);
+// } else {
+// lua_pushnil(L);
+// }
+// }
+
+static int mplib_object_index(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); /* no need for test */
+ if (*hh) {
+ struct mp_graphic_object *h = *hh;
+ const char *s = lua_tostring(L, 2);
+ /* todo: remove stacking from specific aux */
+ if (lua_key_eq(s, type)) {
+ lua_push_key_by_index(mplib_values_type[h->type]);
+ } else if (lua_key_eq(s, stacking)) {
+ lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else {
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ /* todo: we can use generic casts */
+ switch (h->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ mplib_aux_shape(L, s, (mp_shape_object *) h, bendtolerance, movetolerance);
+ break;
+ case mp_start_clip_code:
+ case mp_start_group_code:
+ case mp_start_bounds_code:
+ mplib_aux_start(L, s, (mp_start_object *) h, bendtolerance, movetolerance);
+ break;
+ // case mp_stop_clip_code:
+ // case mp_stop_group_code:
+ // case mp_stop_bounds_code:
+ // mplib_aux_stop_clip(L, s, (mp_stop_object *) h, bendtolerance, movetolerance);
+ // break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* Experiment: mpx, kind, macro, arguments */
+
+static int mplib_expand_tex(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int kind = lmt_tointeger(L, 2);
+ halfword tail = null;
+ halfword head = lmt_macro_to_tok(L, 3, &tail);
+ if (head) {
+ switch (kind) {
+ case lua_value_none_code:
+ case lua_value_dimension_code:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_dimen(0, 0, 0, 0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (! lmt_error_state.last_intercept) {
+ mp_push_numeric_value(mp, (double) value * (7200.0/7227.0) / 65536.0);
+ break;
+ } else if (kind == lua_value_none_code) {
+ head = lmt_macro_to_tok(L, 3, &tail);
+ goto TRYAGAIN;
+ } else {
+ // head = lmt_macro_to_tok(L, 3, &tail);
+ // goto JUSTINCASE;
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ }
+ case lua_value_integer_code:
+ case lua_value_cardinal_code:
+ case lua_value_boolean_code:
+ TRYAGAIN:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_int(0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (lmt_error_state.last_intercept) {
+ // head = lmt_macro_to_tok(L, 3, &tail);
+ // goto JUSTINCASE;
+ lua_pushboolean(L, 0);
+ return 1;
+ } else if (kind == lua_value_boolean_code) {
+ mp_push_boolean_value(mp, value);
+ break;
+ } else {
+ mp_push_numeric_value(mp, value);
+ break;
+ }
+ }
+ default:
+ // JUSTINCASE:
+ {
+ int len = 0;
+ const char *str = (const char *) lmt_get_expansion(head, &len);
+ mp_push_string_value(mp, str, str ? len : 0); /* len includes \0 */
+ break;
+ }
+ }
+ }
+ }
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+/* */
+
+static const struct luaL_Reg mplib_instance_metatable[] = {
+ { "__gc", mplib_instance_collect },
+ { "__tostring", mplib_instance_tostring },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_figure_metatable[] = {
+ { "__gc", mplib_figure_collect },
+ { "__tostring", mplib_figure_tostring },
+ { "objects", mplib_figure_objects },
+ { "boundingbox", mplib_figure_bounds },
+ { "width", mplib_figure_width },
+ { "height", mplib_figure_height },
+ { "depth", mplib_figure_depth },
+ { "italic", mplib_figure_italic },
+ { "charcode", mplib_figure_charcode },
+ { "tolerance", mplib_figure_tolerance },
+ { "stacking", mplib_figure_stacking },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_object_metatable[] = {
+ { "__gc", mplib_object_collect },
+ { "__tostring", mplib_object_tostring },
+ { "__index", mplib_object_index },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_instance_functions_list[] = {
+ { "execute", mplib_execute },
+ { "finish", mplib_finish },
+ { "getstatistics", mplib_getstatistics },
+ { "getstatus", mplib_getstatus },
+ { "solvepath", mplib_solvepath },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_functions_list[] = {
+ { "new", mplib_new },
+ { "version", mplib_version },
+ /* */
+ { "getfields", mplib_getfields },
+ { "gettype", mplib_gettype },
+ { "gettypes", mplib_gettypes },
+ { "getcolormodels", mplib_getcolormodels },
+ { "getcodes", mplib_getcodes },
+ { "getstates", mplib_getstates },
+ { "getobjecttypes", mplib_getobjecttypes },
+ { "getcallbackstate", mplib_getcallbackstate },
+ /* */
+ { "settolerance", mplib_set_tolerance },
+ { "gettolerance", mplib_get_tolerance },
+ /* indirect */
+ { "execute", mplib_execute },
+ { "finish", mplib_finish },
+ { "showcontext", mplib_showcontext },
+ { "gethashentries", mplib_gethashentries },
+ { "gethashentry", mplib_gethashentry },
+ { "getstatistics", mplib_getstatistics },
+ { "getstatus", mplib_getstatus },
+ { "solvepath", mplib_solvepath },
+ /* helpers */
+ { "peninfo", mplib_object_peninfo },
+ /* scanners */
+ { "scannext", mplib_scan_next },
+ { "scanexpression", mplib_scan_expression },
+ { "scantoken", mplib_scan_token },
+ { "scansymbol", mplib_scan_symbol },
+ { "scanproperty", mplib_scan_property },
+ { "scannumeric", mplib_scan_numeric },
+ { "scannumber", mplib_scan_numeric }, /* bonus */
+ { "scaninteger", mplib_scan_integer },
+ { "scanboolean", mplib_scan_boolean },
+ { "scanstring", mplib_scan_string },
+ { "scanpair", mplib_scan_pair },
+ { "scancolor", mplib_scan_color },
+ { "scancmykcolor", mplib_scan_cmykcolor },
+ { "scantransform", mplib_scan_transform },
+ { "scanpath", mplib_scan_path },
+ { "scanpen", mplib_scan_pen },
+ /* skippers */
+ { "skiptoken", mplib_skip_token },
+ /* injectors */
+ { "injectnumeric", mplib_inject_numeric },
+ { "injectnumber", mplib_inject_numeric }, /* bonus */
+ { "injectinteger", mplib_inject_integer },
+ { "injectboolean", mplib_inject_boolean },
+ { "injectstring", mplib_inject_string },
+ { "injectpair", mplib_inject_pair },
+ { "injectcolor", mplib_inject_color },
+ { "injectcmykcolor", mplib_inject_cmykcolor },
+ { "injecttransform", mplib_inject_transform },
+ { "injectpath", mplib_inject_path },
+ { "injectwhatever", mplib_inject_whatever },
+ /* */
+ { "expandtex", mplib_expand_tex },
+ /* */
+ { NULL, NULL },
+};
+
+int luaopen_mplib(lua_State *L)
+{
+ mplib_aux_initialize_lua(L);
+
+ luaL_newmetatable(L, MP_METATABLE_OBJECT);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_object_metatable, 0);
+ luaL_newmetatable(L, MP_METATABLE_FIGURE);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_figure_metatable, 0);
+ luaL_newmetatable(L, MP_METATABLE_INSTANCE);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_instance_metatable, 0);
+ luaL_setfuncs(L, mplib_instance_functions_list, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, mplib_functions_list, 0);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmtnodelib.c b/source/luametatex/source/lua/lmtnodelib.c
new file mode 100644
index 000000000..ff98a7064
--- /dev/null
+++ b/source/luametatex/source/lua/lmtnodelib.c
@@ -0,0 +1,10324 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This module is one of the backbones on \LUAMETATEX. It has gradually been extended based on
+ experiences in \CONTEXT\ \MKIV\ and later \LMTX. There are many helpers here and the main
+ reason is that the more callbacks one enables and the more one does in them, the larger the
+ impact on performance.
+
+ After doing lots of tests with \LUATEX\ and \LUAJITTEX, with and without jit, and with and
+ without ffi, I came to the conclusion that userdata prevents a speedup. I also found that the
+ checking of metatables as well as assignment comes with overhead that can't be neglected. This
+ is normally not really a problem but when processing fonts for more complex scripts it's quite
+ some overhead. So \unknown\ direct nodes were introduced (we call them nuts in \CONTEXT).
+
+ Because the userdata approach has some benefits, we keep that interface too. We did some
+ experiments with fast access (assuming nodes), but eventually settled for the direct approach.
+ For code that is proven to be okay, one can use the direct variants and operate on nodes more
+ directly. Currently these are numbers but don't rely on that property; treat them aslhmin
+
+ abstractions. An important aspect is that one cannot mix both methods, although with |tonode|
+ and |todirect| one can cast representations.
+
+ So the advice is: use the indexed (userdata) approach when possible and investigate the direct
+ one when speed might be an issue. For that reason we also provide some get* and set* functions
+ in the top level node namespace. There is a limited set of getters for nodes and a generic
+ getfield to complement them. The direct namespace has a few more.
+
+ Keep in mind that such speed considerations only make sense when we're accessing nodes millions
+ of times (which happens in font processing for instance). Setters are less important as
+ documents have not that many content related nodes and setting many thousands of properties is
+ hardly a burden contrary to millions of consultations. And with millions, we're talking of tens
+ of millions which is not that common.
+
+ Another change is that |__index| and |__newindex| are (as expected) exposed to users but do no
+ checking. The getfield and setfield functions do check. In fact, a fast mode can be simulated
+ by fast_getfield = __index but the (measured) benefit on average runs is not that large (some
+ 5\% when we also use the other fast ones) which is easily nilled by inefficient coding. The
+ direct variants on the other hand can be significantly faster but with the drawback of lack of
+ userdata features. With respect to speed: keep in mind that measuring a speedup on these
+ functions is not representative for a normal run, where much more happens.
+
+ A user should beware of the fact that messing around with |prev|, |next| and other links can
+ lead to crashes. Don't complain about this: you get what you ask for. Examples are bad loops
+ in nodes lists that make the program run out of stack space.
+
+ The code below differs from the \LUATEX\ code in that it drops some userdata related
+ accessors. These can easily be emulates in \LUA, which is what we do in \CONTEXT\ \LMTX. Also,
+ some optimizations, like using macros and dedicated |getfield| and |setfield| functions for
+ userdata and direct nodes were removed because on a regular run there is not much impact and
+ the less code we have, the better. In the early days of \LUATEX\ it really did improve the
+ overall performance but computers (as well as compilers) have become better. But still, it
+ could be that \LUATEX\ has a better performance here; so be it. A performance hit can also be
+ one of the side effects of the some more rigourous testing of direct node validity introduced
+ here.
+
+ Attribute nodes are special as their prev and subtype fields are used for other purposes.
+ Setting them can confuse the checkers but we don't check each case for performance reasons.
+ Messing a list up is harmless and only affects functionality which is the users responsibility
+ anyway.
+
+ In \LUAMETATEX\ nodes can have different names and properties as in \LUATEX. Some might be
+ backported but that is kind of dangerous as macro packages other than \CONTEXT\ depend on
+ stability of \LUATEX. (It's one of the reasons for \LUAMETATEX\ being around: it permits us
+ to move on).
+
+ Todo: getters/setters for leftovers.
+
+*/
+
+/*
+ direct_prev_id(n) => returns prev and id
+ direct_next_id(n) => returns next and id
+*/
+
+# include "luametatex.h"
+
+/* # define NODE_METATABLE_INSTANCE "node.instance" */
+/* # define NODE_PROPERTIES_DIRECT "node.properties" */
+/* # define NODE_PROPERTIES_INDIRECT "node.properties.indirect" */
+/* # define NODE_PROPERTIES_INSTANCE "node.properties.instance" */
+
+/*tex
+
+ There is a bit of checking for validity of direct nodes but of course one can still create
+ havoc by using flushed nodes, setting bad links, etc.
+
+ Although we could gain a little by moving the body of the valid checker into the caller (that
+ way the field variables might be shared) there is no real measurable gain in that on a regular
+ run. So, in the end I settled for function calls.
+
+*/
+
+halfword lmt_check_isdirect(lua_State *L, int i)
+{
+ halfword n = lmt_tohalfword(L, i);
+ return n && _valid_node_(n) ? n : null;
+}
+
+inline static halfword nodelib_valid_direct_from_index(lua_State *L, int i)
+{
+ halfword n = lmt_tohalfword(L, i);
+ return n && _valid_node_(n) ? n : null;
+}
+
+inline static void nodelib_push_direct_or_nil(lua_State *L, halfword n)
+{
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+inline static void nodelib_push_direct_or_nil_node_prev(lua_State *L, halfword n)
+{
+ if (n) {
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+inline static void nodelib_push_node_on_top(lua_State *L, halfword n)
+{
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, -2);
+ lua_setmetatable(L, -2);
+}
+
+/*tex
+ Many of these small functions used to be macros but that no longer pays off because compilers
+ became better (for instance at deciding when to inline small functions). We could have explicit
+ inline variants of these too but normally the compiler will inline small functions anyway.
+
+*/
+
+static halfword lmt_maybe_isnode(lua_State *L, int i)
+{
+ halfword *p = lua_touserdata(L, i);
+ halfword n = null;
+ if (p && lua_getmetatable(L, i)) {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ n = *p;
+ }
+ lua_pop(L, 2);
+ }
+ return n;
+}
+
+halfword lmt_check_isnode(lua_State *L, int i)
+{
+ halfword n = lmt_maybe_isnode(L, i);
+ if (! n) {
+ // formatted_error("node lib", "lua <node> expected, not an object with type %s", luaL_typename(L, i));
+ luaL_error(L, "invalid node");
+ }
+ return n;
+}
+
+/* helpers */
+
+static void nodelib_push_direct_or_node(lua_State *L, int direct, halfword n)
+{
+ if (n) {
+ if (direct) {
+ lua_pushinteger(L, n);
+ } else {
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, 1);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void nodelib_push_direct_or_node_node_prev(lua_State *L, int direct, halfword n)
+{
+ if (n) {
+ node_prev(n) = null;
+ if (direct) {
+ lua_pushinteger(L, n);
+ } else {
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, 1);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static halfword nodelib_direct_or_node_from_index(lua_State *L, int direct, int i)
+{
+ if (direct) {
+ return nodelib_valid_direct_from_index(L, i);
+ } else if (lua_isuserdata(L, i)) {
+ return lmt_check_isnode(L, i);
+ } else {
+ return null;
+ }
+}
+
+halfword lmt_check_isdirectornode(lua_State *L, int i, int *isdirect)
+{
+ *isdirect = ! lua_isuserdata(L, i);
+ return *isdirect ? nodelib_valid_direct_from_index(L, i) : lmt_check_isnode(L, i);
+}
+
+static void nodelib_push_attribute_data(lua_State* L, halfword n)
+{
+ if (node_type(n) == attribute_list_subtype) {
+ lua_newtable(L);
+ n = node_next(n);
+ while (n) {
+ lua_pushinteger(L, attribute_value(n));
+ lua_rawseti(L, -2, attribute_index(n));
+ n = node_next(n);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+/*tex Another shortcut: */
+
+inline static singleword nodelib_getdirection(lua_State *L, int i)
+{
+ return ((lua_type(L, i) == LUA_TNUMBER) ? checked_direction_value(lmt_tohalfword(L, i)) : direction_def_value);
+}
+
+/*tex
+
+ This routine finds the numerical value of a string (or number) at \LUA\ stack index |n|. If it
+ is not a valid node type |-1| is returned.
+
+*/
+
+static quarterword nodelib_aux_get_node_type_id_from_name(lua_State *L, int n, node_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (int j = 0; data[j].id != -1; j++) {
+ if (s == data[j].name) {
+ if (data[j].visible) {
+ return (quarterword) j;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return unknown_node;
+}
+
+static quarterword nodelib_aux_get_node_subtype_id_from_name(lua_State *L, int n, value_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (quarterword j = 0; data[j].id != -1; j++) {
+ if (s == data[j].name) {
+ return j;
+ }
+ }
+ }
+ return unknown_subtype;
+}
+
+static quarterword nodelib_aux_get_field_index_from_name(lua_State *L, int n, value_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (quarterword j = 0; data[j].name; j++) {
+ if (s == data[j].name) {
+ return j;
+ }
+ }
+ }
+ return unknown_field;
+}
+
+static quarterword nodelib_aux_get_valid_node_type_id(lua_State *L, int n)
+{
+ quarterword i = unknown_node;
+ switch (lua_type(L, n)) {
+ case LUA_TSTRING:
+ i = nodelib_aux_get_node_type_id_from_name(L, n, lmt_interface.node_data);
+ if (i == unknown_node) {
+ luaL_error(L, "invalid node type id: %s", lua_tostring(L, n));
+ }
+ break;
+ case LUA_TNUMBER:
+ i = lmt_toquarterword(L, n);
+ if (! tex_nodetype_is_visible(i)) {
+ luaL_error(L, "invalid node type id: %d", i);
+ }
+ break;
+ default:
+ luaL_error(L, "invalid node type id");
+ }
+ return i;
+}
+
+int lmt_get_math_style(lua_State *L, int n, int dflt)
+{
+ int i = -1;
+ switch (lua_type(L, n)) {
+ case LUA_TNUMBER:
+ i = lmt_tointeger(L, n);
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_style_values);
+ break;
+ }
+ if (i >= 0 && i <= cramped_script_script_style) {
+ return i;
+ } else {
+ return dflt;
+ }
+}
+
+int lmt_get_math_parameter(lua_State *L, int n, int dflt)
+{
+ int i;
+ switch (lua_type(L, n)) {
+ case LUA_TNUMBER:
+ i = lmt_tointeger(L, n);
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_parameter_values);
+ break;
+ default:
+ i = -1;
+ break;
+ }
+ if (i >= 0 && i < math_parameter_last) {
+ return i;
+ } else {
+ return dflt;
+ }
+}
+
+/*tex
+
+ Creates a userdata object for a number found at the stack top, if it is representing a node
+ (i.e. an pointer into |varmem|). It replaces the stack entry with the new userdata, or pushes
+ |nil| if the number is |null|, or if the index is definately out of range. This test could be
+ improved.
+
+*/
+
+void lmt_push_node(lua_State *L)
+{
+ halfword n = null;
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ n = lmt_tohalfword(L, -1);
+ }
+ lua_pop(L, 1);
+ if ((! n) || (n > lmt_node_memory_state.nodes_data.allocated)) {
+ lua_pushnil(L);
+ } else {
+ halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ }
+ return;
+}
+
+void lmt_push_node_fast(lua_State *L, halfword n)
+{
+ if (n) {
+ halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+void lmt_push_directornode(lua_State *L, halfword n, int isdirect)
+{
+ if (! n) {
+ lua_pushnil(L);
+ } else if (isdirect) {
+ lua_push_integer(L, n);
+ } else {
+ lmt_push_node_fast(L, n);
+ }
+}
+
+/*tex getting and setting fields (helpers) */
+
+static int nodelib_getlist(lua_State *L, int n)
+{
+ if (lua_isuserdata(L, n)) {
+ return lmt_check_isnode(L, n);
+ } else {
+ return null;
+ }
+}
+
+/*tex converts type strings to type ids */
+
+static int nodelib_shared_id(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ int i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data);
+ if (i >= 0) {
+ lua_pushinteger(L, i);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getid */
+
+static int nodelib_direct_getid(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_pushinteger(L, node_type(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getsubtype */
+/* node.direct.setsubtype */
+
+static int nodelib_direct_getsubtype(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_pushinteger(L, node_subtype(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setsubtype(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ node_subtype(n) = lmt_toquarterword(L, 2);
+ }
+ return 0;
+}
+
+/* node.direct.getexpansion */
+/* node.direct.setexpansion */
+
+static int nodelib_direct_getexpansion(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_expansion(n));
+ break;
+ case kern_node:
+ lua_pushinteger(L, kern_expansion(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setexpansion(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword e = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ e = (halfword) lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_expansion(n) = e;
+ break;
+ case kern_node:
+ kern_expansion(n) = e;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getfont */
+/* node.direct.setfont */
+
+static int nodelib_direct_getfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_font(n));
+ break;
+ case glue_node:
+ lua_pushinteger(L, glue_font(n));
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ break;
+ case delimiter_node:
+ lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_character(n) = lmt_tohalfword(L, 3);
+ }
+ break;
+ case rule_node:
+ tex_set_rule_font(n, lmt_tohalfword(L, 2));
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_character(n) = lmt_tohalfword(L, 3);
+ }
+ break;
+ case glue_node:
+ glue_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getchardict(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_properties(n));
+ lua_pushinteger(L, glyph_group(n));
+ lua_pushinteger(L, glyph_index(n));
+ lua_pushinteger(L, glyph_font(n));
+ lua_pushinteger(L, glyph_character(n));
+ return 5;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_properties(n));
+ lua_pushinteger(L, kernel_math_group(n));
+ lua_pushinteger(L, kernel_math_index(n));
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n),0));
+ lua_pushinteger(L, kernel_math_character(n));
+ return 5;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setchardict(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_properties(n) = lmt_optquarterword(L, 2, 0);
+ glyph_group(n) = lmt_optquarterword(L, 3, 0);
+ glyph_index(n) = lmt_opthalfword(L, 4, 0);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_properties(n) = lmt_optquarterword(L, 2, 0);
+ kernel_math_group(n) = lmt_optquarterword(L, 3, 0);
+ kernel_math_index(n) = lmt_opthalfword(L, 4, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getchar */
+/* node.direct.setchar */
+
+static int nodelib_direct_getchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch(node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_character(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_character(n));
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_character(n));
+ break;
+ case delimiter_node:
+ /* used in wide fonts */
+ lua_pushinteger(L, delimiter_small_character(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case rule_node:
+ rule_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case delimiter_node:
+ /* used in wide fonts */
+ delimiter_small_character(n) = lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* bonus */
+
+static int nodelib_direct_getcharspec(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ return 2;
+ case rule_node:
+ lua_pushinteger(L, rule_character(n));
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ break;
+ case simple_noad:
+ n = noad_nucleus(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_character(n));
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ lua_pushinteger(L, kernel_math_family(n));
+ return 3;
+ case delimiter_node:
+ lua_pushinteger(L, delimiter_small_character(n));
+ lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
+ lua_pushinteger(L, delimiter_small_family(n));
+ return 3;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getfam */
+/* node.direct.setfam */
+
+static int nodelib_direct_getfam(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch(node_type(n)) {
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_family(n));
+ break;
+ case delimiter_node:
+ lua_pushinteger(L, delimiter_small_family(n));
+ break;
+ case fraction_noad:
+ case simple_noad:
+ lua_pushinteger(L, noad_family(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, tex_get_rule_family(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setfam(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ switch (node_type(n)) {
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_family(n) = lmt_tohalfword(L, 2);
+ break;
+ case delimiter_node:
+ delimiter_small_family(n) = lmt_tohalfword(L, 2);
+ break;
+ case fraction_noad:
+ case simple_noad:
+ set_noad_family(n, lmt_tohalfword(L, 2));
+ break;
+ case rule_node:
+ tex_set_rule_family(n, lmt_tohalfword(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getstate(n) */
+/* node.direct.setstate(n) */
+
+/*tex
+ A zero state is considered to be false or basically the same as \quote {unset}. That way we
+ can are compatible with an unset property. This is cheaper on testing too. But I might
+ reconsider this at some point. (In which case I need to adapt the context source but by then
+ we have a lua/lmt split.)
+*/
+
+static int nodelib_direct_getstate(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int state = 0;
+ switch (node_type(n)) {
+ case glyph_node:
+ state = get_glyph_state(n);
+ break;
+ case hlist_node:
+ case vlist_node:
+ state = box_package_state(n);
+ break;
+ default:
+ goto NOPPES;
+ }
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_pushboolean(L, lua_tointeger(L, 2) == state);
+ return 1;
+ } else if (state) {
+ lua_pushinteger(L, state);
+ return 1;
+ } else {
+ /*tex Indeed, |nil|. */
+ }
+ }
+ NOPPES:
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setstate(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ set_glyph_state(n, lmt_opthalfword(L, 2, 0));
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_package_state(n) = (singleword) lmt_opthalfword(L, 2, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getclass(n,main,left,right) */
+/* node.direct.setclass(n,main,left,right) */
+
+static int nodelib_direct_getclass(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ lua_push_integer(L, get_noad_main_class(n));
+ lua_push_integer(L, get_noad_left_class(n));
+ lua_push_integer(L, get_noad_right_class(n));
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setclass(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ set_noad_main_class(n, lmt_tohalfword(L, 2));
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ set_noad_left_class(n, lmt_tohalfword(L, 3));
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ set_noad_right_class(n, lmt_tohalfword(L, 4));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getscript(n) */
+/* node.direct.setscript(n) */
+
+static int nodelib_direct_getscript(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node && get_glyph_script(n)) {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_pushboolean(L, lua_tointeger(L, 2) == get_glyph_script(n));
+ } else {
+ lua_pushinteger(L, get_glyph_script(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setscript(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_script(n, lmt_opthalfword(L, 2, 0));
+ }
+ return 0;
+}
+
+/* node.direct.getlang */
+/* node.direct.setlang */
+
+static int nodelib_direct_getlanguage(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, get_glyph_language(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setlanguage(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_language(n, lmt_opthalfword(L, 2, 0));
+ }
+ return 0;
+}
+
+/* node.direct.getattributelist */
+/* node.direct.setattributelist */
+
+static int nodelib_direct_getattributelist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n)) && node_attr(n)) {
+ if (lua_toboolean(L, 2)) {
+ nodelib_push_attribute_data(L, n);
+ } else {
+ lua_pushinteger(L, node_attr(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static void nodelib_aux_setattributelist(lua_State *L, halfword n, int index)
+{
+ if (n && tex_nodetype_has_attributes(node_type(n))) {
+ halfword a = null;
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ {
+ halfword m = nodelib_valid_direct_from_index(L, index);
+ if (m) {
+ quarterword t = node_type(m);
+ if (t == attribute_node) {
+ if (node_subtype(m) == attribute_list_subtype) {
+ a = m;
+ } else {
+ /* invalid list, we could make a proper one if needed */
+ }
+ } else if (tex_nodetype_has_attributes(t)) {
+ a = node_attr(m);
+ }
+ }
+ }
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, index)) {
+ a = tex_current_attribute_list();
+ }
+ break;
+ case LUA_TTABLE:
+ {
+ /* kind of slow because we need a sorted inject */
+ lua_pushnil(L); /* push initial key */
+ while (lua_next(L, index)) {
+ halfword key = lmt_tohalfword(L, -2);
+ halfword val = lmt_tohalfword(L, -1);
+ a = tex_patch_attribute_list(a, key, val);
+ lua_pop(L, 1); /* pop value, keep key */
+ }
+ lua_pop(L, 1); /* pop key */
+ }
+ break;
+ }
+ tex_attach_attribute_list_attribute(n, a);
+ }
+}
+
+static int nodelib_direct_setattributelist(lua_State *L)
+{
+ nodelib_aux_setattributelist(L, nodelib_valid_direct_from_index(L, 1), 2);
+ return 0;
+}
+
+/* node.direct.getpenalty */
+/* node.direct.setpenalty */
+
+static int nodelib_direct_getpenalty(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case penalty_node:
+ lua_pushinteger(L, penalty_amount(n));
+ break;
+ case disc_node:
+ lua_pushinteger(L, disc_penalty(n));
+ break;
+ case math_node:
+ lua_pushinteger(L, math_penalty(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setpenalty(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case penalty_node:
+ penalty_amount(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ case disc_node:
+ disc_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ case math_node:
+ math_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getnucleus */
+/* node.direct.getsub */
+/* node.direct.getsup */
+
+static int nodelib_direct_getnucleus(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_nucleus(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setnucleus(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_nucleus(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getsub(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_subscr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getsubpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_subprescr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setsub(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_subscr(n) = nodelib_valid_direct_from_index(L, 2);
+ // if (lua_gettop(L) > 2) {
+ // noad_subprescr(n) = nodelib_valid_direct_from_index(L, 3);
+ // }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setsubpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_subprescr(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getsup(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_supscr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getsuppre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_supprescr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getprime(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_prime(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setsup(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_supscr(n) = nodelib_valid_direct_from_index(L, 2);
+ // if (lua_gettop(L) > 2) {
+ // supprescr(n) = nodelib_valid_direct_from_index(L, 3);
+ // }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setsuppre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_supprescr(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setprime(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_prime(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getkern (overlaps with getwidth) */
+/* node.direct.setkern (overlaps with getwidth) */
+
+static int nodelib_direct_getkern(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case kern_node:
+ lua_pushnumber(L, kern_amount(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, kern_expansion(n));
+ return 2;
+ } else {
+ break;
+ }
+ case math_node:
+ lua_pushinteger(L, math_surround(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setkern(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case kern_node:
+ kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ node_subtype(n) = lmt_toquarterword(L, 3);
+ }
+ break;
+ case math_node:
+ math_surround(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getdirection */
+/* node.direct.setdirection */
+
+static int nodelib_direct_getdirection(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case dir_node:
+ lua_pushinteger(L, dir_direction(n));
+ lua_pushboolean(L, node_subtype(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ break;
+ case par_node:
+ lua_pushinteger(L, par_dir(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setdirection(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case dir_node:
+ dir_direction(n) = nodelib_getdirection(L, 2);
+ if (lua_type(L, 3) == LUA_TBOOLEAN) {
+ if (lua_toboolean(L, 3)) {
+ node_subtype(n) = (quarterword) (lua_toboolean(L, 3) ? cancel_dir_subtype : normal_dir_subtype);
+ }
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_dir(n) = (singleword) nodelib_getdirection(L, 2);
+ break;
+ case par_node:
+ par_dir(n) = nodelib_getdirection(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getanchors */
+/* node.direct.setanchors */
+
+static int nodelib_direct_getanchors(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_anchor(n)) {
+ lua_pushinteger(L, box_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_source_anchor(n)) {
+ lua_pushinteger(L, box_source_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_target_anchor(n)) {
+ lua_pushinteger(L, box_target_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ /* bonus detail: source, target */
+ if (box_source_anchor(n)) {
+ lua_pushinteger(L, box_anchor(n) & 0x0FFF);
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_target_anchor(n)) {
+ lua_pushinteger(L, (box_anchor(n) >> 16) & 0x0FFF);
+ } else {
+ lua_pushnil(L);
+ }
+ return 5;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (noad_source(n)) {
+ lua_pushinteger(L, noad_source(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setanchors(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ box_anchor(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ break;
+ default:
+ box_anchor(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER :
+ box_source_anchor(n) = lmt_tohalfword(L, 3);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 3)) {
+ break;
+ }
+ default:
+ box_source_anchor(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 4)) {
+ case LUA_TNUMBER:
+ box_target_anchor(n) = lmt_tohalfword(L, 4);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 4)) {
+ break;
+ }
+ default:
+ box_target_anchor(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER :
+ noad_source(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ default:
+ noad_source(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getxoffset */
+/* node.direct.getyoffset */
+/* node.direct.getoffsets */
+/* node.direct.setxoffset */
+/* node.direct.setyoffset */
+/* node.direct.setoffsets */
+
+static int nodelib_direct_getoffsets(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_x_offset(n));
+ lua_pushinteger(L, glyph_y_offset(n));
+ lua_pushinteger(L, glyph_left(n));
+ lua_pushinteger(L, glyph_right(n));
+ lua_pushinteger(L, glyph_raise(n));
+ return 5;
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_x_offset(n));
+ lua_pushinteger(L, box_y_offset(n));
+ return 2;
+ case rule_node:
+ lua_pushinteger(L, rule_x_offset(n));
+ lua_pushinteger(L, rule_y_offset(n));
+ lua_pushinteger(L, rule_left(n));
+ lua_pushinteger(L, rule_right(n));
+ return 4;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setoffsets(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_left(n) = (halfword) lmt_roundnumber(L, 4);
+ }
+ if (lua_type(L, 5) == LUA_TNUMBER) {
+ glyph_right(n) = (halfword) lmt_roundnumber(L, 5);
+ }
+ if (lua_type(L, 6) == LUA_TNUMBER) {
+ glyph_raise(n) = (halfword) lmt_roundnumber(L, 6);
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ box_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ rule_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ rule_left(n) = (halfword) lmt_roundnumber(L, 4);
+ }
+ if (lua_type(L, 5) == LUA_TNUMBER) {
+ rule_right(n) = (halfword) lmt_roundnumber(L, 5);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addxoffset(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ rule_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addyoffset(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ rule_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* */
+
+static int nodelib_direct_addmargins(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_left(n) += (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_right(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_raise(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ break;
+ case rule_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ rule_left(n) += (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_right(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addxymargins(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ scaled y = glyph_y_scale(n);
+ double sx, sy;
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ sx = 1;
+ } else {
+ sx = 0.001 * x;
+ }
+ if (y == 0 || y == 1000) {
+ sy = 1;
+ } else {
+ sy = 0.001 * y;
+ }
+ } else {
+ if (x == 0 || x == 1000) {
+ sx = 0.001 * s;
+ } else {
+ sx = 0.000001 * s * x;
+ }
+ if (y == 0 || y == 1000) {
+ sy = 0.001 * s;
+ } else {
+ sy = 0.000001 * s * y;
+ }
+ }
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_left(n) += scaledround(sx * lua_tonumber(L, 2));
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_right(n) += scaledround(sx * lua_tonumber(L, 3));
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_raise(n) += scaledround(sy * lua_tonumber(L, 4));
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getscale */
+/* node.direct.getxscale */
+/* node.direct.getyscale */
+/* node.direct.getxyscale */
+/* node.direct.setscale */
+/* node.direct.setxscale */
+/* node.direct.setyscale */
+/* node.direct.setxyscale */
+
+static int nodelib_direct_getscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, glyph_scale(n));
+ return 1;
+ }
+ return 0;
+}
+
+static int nodelib_direct_getscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, glyph_scale(n));
+ lua_pushinteger(L, glyph_x_scale(n));
+ lua_pushinteger(L, glyph_y_scale(n));
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_setscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_scale(n) = (halfword) lmt_roundnumber(L, 2);
+ if (! glyph_scale(n)) {
+ glyph_scale(n) = 1000;
+ }
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_x_scale(n)) {
+ glyph_x_scale(n) = 1000;
+ }
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 4);
+ if (! glyph_y_scale(n)) {
+ glyph_y_scale(n) = 1000;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getxscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ double d;
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ goto DONE;
+ } else {
+ d = 0.001 * x;
+ }
+ } else if (x == 0 || x == 1000) {
+ d = 0.001 * s;
+ } else {
+ d = 0.000001 * s * x;
+ }
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ DONE:
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+static int nodelib_direct_xscaled(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_Number v = lua_tonumber(L, 2);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ /* okay */
+ } else {
+ v = 0.001 * x * v;
+ }
+ } else if (x == 0 || x == 1000) {
+ v = 0.001 * s * v;
+ } else {
+ v = 0.000001 * s * x * v;
+ }
+ }
+ lua_pushnumber(L, v);
+ return 1;
+}
+
+static int nodelib_direct_getyscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled y = glyph_y_scale(n);
+ double d;
+ if (s == 0 || s == 1000) {
+ if (y == 0 || y == 1000) {
+ goto DONE;
+ } else {
+ d = 0.001 * y;
+ }
+ } else if (y == 0 || y == 1000) {
+ d = 0.001 * s;
+ } else {
+ d = 0.000001 * s * y;
+ }
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ DONE:
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+static int nodelib_direct_yscaled(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_Number v = lua_tonumber(L, 2);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled y = glyph_y_scale(n);
+ if (s == 0 || s == 1000) {
+ if (y == 0 || y == 1000) {
+ /* okay */
+ } else {
+ v = 0.001 * y * v;
+ }
+ } else if (y == 0 || y == 1000) {
+ v = 0.001 * s * v;
+ } else {
+ v = 0.000001 * s * y * v;
+ }
+ }
+ lua_pushnumber(L, v);
+ return 1;
+}
+
+static void nodelib_aux_pushxyscales(lua_State *L, halfword n)
+{
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ scaled y = glyph_y_scale(n);
+ double dx;
+ double dy;
+ if (s && s != 1000) {
+ dx = (x && x != 1000) ? 0.000001 * s * x : 0.001 * s;
+ } else if (x && x != 1000) {
+ dx = 0.001 * x;
+ } else {
+ lua_pushinteger(L, 1);
+ goto DONEX;
+ }
+ lua_pushnumber(L, dx);
+ DONEX:
+ if (s && s != 1000) {
+ dy = (y && y != 1000) ? 0.000001 * s * y : 0.001 * s;
+ } else if (y && y != 1000) {
+ dy = 0.001 * y;
+ } else {
+ lua_pushinteger(L, 1);
+ goto DONEY;
+ }
+ lua_pushnumber(L, dy);
+ DONEY: ;
+}
+
+static int nodelib_direct_getxyscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ nodelib_aux_pushxyscales(L, n);
+ } else {
+ lua_pushinteger(L, 1);
+ lua_pushinteger(L, 1);
+ }
+ return 2;
+}
+
+/* node.direct.getdisc */
+/* node.direct.setdisc */
+
+/*tex
+ For the moment we don't provide setters for math discretionaries, mainly because these are
+ special and I don't want to waste time on checking and intercepting errors. They are not that
+ widely used anyway.
+*/
+
+static int nodelib_direct_getdisc(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_head(n));
+ if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
+ nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
+ return 6;
+ } else {
+ return 3;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_pre_break(n));
+ nodelib_push_direct_or_nil(L, choice_post_break(n));
+ nodelib_push_direct_or_nil(L, choice_no_break(n));
+ if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_pre_break(n)));
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_post_break(n)));
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_post_break(n)));
+ return 6;
+ } else {
+ return 3;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getdiscpart(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, get_glyph_discpart(n));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_getpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword h = box_pre_migrated(n);
+ halfword t = tex_tail_of_node_list(h);
+ nodelib_push_direct_or_nil(L, h);
+ nodelib_push_direct_or_nil(L, t);
+ return 2;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_pre_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getpost(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_post_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword h = box_post_migrated(n);
+ halfword t = tex_tail_of_node_list(h);
+ nodelib_push_direct_or_nil(L, h);
+ nodelib_push_direct_or_nil(L, t);
+ return 2;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_post_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getreplace(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_no_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
+ return 2;
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_no_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdisc(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ int t = lua_gettop(L) ;
+ if (t > 1) {
+ tex_set_disc_field(n, pre_break_code, nodelib_valid_direct_from_index(L, 2));
+ if (t > 2) {
+ tex_set_disc_field(n, post_break_code, nodelib_valid_direct_from_index(L, 3));
+ if (t > 3) {
+ tex_set_disc_field(n, no_break_code, nodelib_valid_direct_from_index(L, 4));
+ if (t > 4) {
+ node_subtype(n) = lmt_toquarterword(L, 5);
+ if (t > 5) {
+ disc_penalty(n) = lmt_tohalfword(L, 6);
+ }
+ }
+ } else {
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ } else {
+ tex_set_disc_field(n, post_break_code, null);
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ } else {
+ tex_set_disc_field(n, pre_break_code, null);
+ tex_set_disc_field(n, post_break_code, null);
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdiscpart(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_discpart(n, luaL_optinteger(L, 2, glyph_discpart_unset));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_setpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ switch (node_type(n)) {
+ case disc_node:
+ tex_set_disc_field(n, pre_break_code, m);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_pre_migrated(n) = m;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setpost(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ switch (node_type(n)) {
+ case disc_node:
+ tex_set_disc_field(n, post_break_code, m);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_post_migrated(n) = m;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setreplace(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ tex_set_disc_field(n, no_break_code, m);
+ }
+ return 0;
+}
+
+/* node.direct.getwidth */
+/* node.direct.setwidth */
+/* node.direct.getheight (for consistency) */
+/* node.direct.setheight (for consistency) */
+/* node.direct.getdepth (for consistency) */
+/* node.direct.setdepth (for consistency) */
+
+/* split ifs for clearity .. compiler will optimize */
+
+static int nodelib_direct_getwidth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_width(n));
+ break;
+ case align_record_node:
+ lua_pushinteger(L, box_width(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, box_size(n));
+ return 2;
+ }
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_width(n));
+ break;
+ case glue_node:
+ case glue_spec_node:
+ lua_pushinteger(L, glue_amount(n));
+ break;
+ case glyph_node:
+ lua_pushnumber(L, tex_glyph_width(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ return 2;
+ }
+ break;
+ case kern_node:
+ lua_pushinteger(L, kern_amount(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, kern_expansion(n));
+ return 2;
+ }
+ break;
+ case math_node:
+ lua_pushinteger(L, math_amount(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setwidth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ box_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ box_size(n) = lmt_roundnumber(L, 3);
+ box_package_state(n) = package_dimension_size_set;
+ }
+ break;
+ case rule_node:
+ rule_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case glue_node:
+ case glue_spec_node:
+ glue_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case kern_node:
+ kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case math_node:
+ math_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getindex(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_index(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_index(n));
+ break;
+ case mark_node:
+ lua_pushinteger(L, mark_index(n));
+ break;
+ case adjust_node:
+ lua_pushinteger(L, adjust_index(n));
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setindex(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_box_index(index)) {
+ box_index(n) = index;
+ } else {
+ /* error or just ignore */
+ }
+ break;
+ }
+ case insert_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ } else {
+ /* error or just ignore */
+ }
+ break;
+ }
+ case mark_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_mark(index)) {
+ mark_index(n) = index;
+ }
+ }
+ break;
+ case adjust_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_adjust_index(index)) {
+ adjust_index(n) = index;
+ }
+ }
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_getheight(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_height(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_height(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_total_height(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, tex_glyph_height(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, noad_height(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setheight(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword h = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ h = lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_height(n) = h;
+ break;
+ case rule_node:
+ rule_height(n) = h;
+ break;
+ case insert_node:
+ insert_total_height(n) = h;
+ break;
+ case fence_noad:
+ noad_height(n) = h;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getdepth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_depth(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_depth(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_max_depth(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, tex_glyph_depth(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, noad_depth(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setdepth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword d = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ d = lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_depth(n) = d;
+ break;
+ case rule_node:
+ rule_depth(n) = d;
+ break;
+ case insert_node:
+ insert_max_depth(n) = d;
+ break;
+ case fence_noad:
+ noad_depth(n) = d;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_gettotal(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, (lua_Integer) box_total(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, (lua_Integer) rule_total(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, (lua_Integer) insert_total_height(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, (lua_Integer) tex_glyph_total(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, (lua_Integer) noad_total(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_settotal(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case insert_node:
+ insert_total_height(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L,2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getshift */
+/* node.direct.setshift */
+
+static int nodelib_direct_getshift(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_shift_amount(n));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setshift(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ box_shift_amount(n) = (halfword) lmt_roundnumber(L,2);
+ } else {
+ box_shift_amount(n) = 0;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.hasgeometry */
+/* node.direct.getgeometry */
+/* node.direct.setgeometry */
+/* node.direct.getorientation */
+/* node.direct.setorientation */
+
+static int nodelib_direct_hasgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_geometry(n)) {
+ lua_pushinteger(L, box_geometry(n));
+ return 1;
+ }
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+static int nodelib_direct_getgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_geometry(n)) {
+ lua_pushinteger(L, box_geometry(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushboolean(L, tex_has_box_geometry(n, offset_geometry));
+ lua_pushboolean(L, tex_has_box_geometry(n, orientation_geometry));
+ lua_pushboolean(L, tex_has_box_geometry(n, anchor_geometry));
+ return 4;
+ } else {
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+static int nodelib_direct_setgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ box_geometry(n) = (singleword) lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getorientation(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_orientation(n));
+ lua_pushinteger(L, box_x_offset(n));
+ lua_pushinteger(L, box_y_offset(n));
+ lua_pushinteger(L, box_w_offset(n));
+ lua_pushinteger(L, box_h_offset(n));
+ lua_pushinteger(L, box_d_offset(n));
+ return 6;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setorientation(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ box_orientation(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ default:
+ box_orientation(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ box_x_offset(n) = lmt_tohalfword(L, 3);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 3)) {
+ break;
+ }
+ default:
+ box_x_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 4)) {
+ case LUA_TNUMBER:
+ box_y_offset(n) = lmt_tohalfword(L, 4);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 4)) {
+ break;
+ }
+ default:
+ box_y_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 5)) {
+ case LUA_TNUMBER:
+ box_w_offset(n) = lmt_tohalfword(L, 5);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 5)) {
+ break;
+ }
+ default:
+ box_w_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 6)) {
+ case LUA_TNUMBER:
+ box_h_offset(n) = lmt_tohalfword(L, 6);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 6)) {
+ break;
+ }
+ default:
+ box_h_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 7)) {
+ case LUA_TNUMBER:
+ box_d_offset(n) = lmt_tohalfword(L, 7);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 7)) {
+ break;
+ }
+ default:
+ box_d_offset(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.setoptions */
+/* node.direct.getoptions */
+
+static int nodelib_direct_getoptions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_options(n));
+ return 1;
+ case disc_node:
+ lua_pushinteger(L, disc_options(n));
+ return 1;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ lua_pushinteger(L, noad_options(n));
+ return 1;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_options(n));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setoptions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ set_glyph_options(n, lmt_tohalfword(L, 2));
+ break;
+ case disc_node:
+ set_disc_options(n, lmt_tohalfword(L, 2));
+ break;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ noad_options(n) = lmt_tohalfword(L, 2);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_options(n) = lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getwhd */
+/* node.direct.setwhd */
+
+static int nodelib_direct_getwhd(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_width(n));
+ lua_pushinteger(L, box_height(n));
+ lua_pushinteger(L, box_depth(n));
+ return 3;
+ case rule_node:
+ lua_pushinteger(L, rule_width(n));
+ lua_pushinteger(L, rule_height(n));
+ lua_pushinteger(L, rule_depth(n));
+ return 3;
+ case glyph_node:
+ /* or glyph_dimensions: */
+ lua_pushinteger(L, tex_glyph_width(n));
+ lua_pushinteger(L, tex_glyph_height(n));
+ lua_pushinteger(L, tex_glyph_depth(n));
+ if (lua_toboolean(L,2)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ return 4;
+ } else {
+ return 3;
+ }
+ case glue_node:
+ n = glue_leader_ptr(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setwhd(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ {
+ int top = lua_gettop(L) ;
+ if (top > 1) {
+ if ((lua_type(L, 2) == LUA_TNUMBER)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 2);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 2) {
+ if ((lua_type(L, 3) == LUA_TNUMBER)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 3) {
+ if ((lua_type(L, 4) == LUA_TNUMBER)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 4);
+ } else {
+ /*Leave as is */
+ }
+ }
+ }
+ }
+ }
+ break;
+ case rule_node:
+ {
+ int top = lua_gettop(L) ;
+ if (top > 1) {
+ if ((lua_type(L, 2) == LUA_TNUMBER)) {
+ rule_width(n) = (halfword) lmt_roundnumber(L, 2);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 2) {
+ if ((lua_type(L, 3) == LUA_TNUMBER)) {
+ rule_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 3) {
+ if ((lua_type(L, 4) == LUA_TNUMBER)) {
+ rule_depth(n) = (halfword) lmt_roundnumber(L, 4);
+ } else {
+ /*Leave as is */
+ }
+ }
+ }
+ }
+ }
+ break;
+ case glue_node:
+ n = glue_leader_ptr(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_hasdimensions(lua_State *L)
+{
+ int b = 0;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ b = (box_width(n) > 0) || (box_total(n) > 0);
+ break;
+ case rule_node:
+ b = (rule_width(n) > 0) || (rule_total(n) > 0);
+ break;
+ case glyph_node:
+ b = tex_glyph_has_dimensions(n);
+ break;
+ case glue_node:
+ {
+ halfword l = glue_leader_ptr(n);
+ if (l) {
+ switch (node_type(l)) {
+ case hlist_node:
+ case vlist_node:
+ b = (box_width(l) > 0) || (box_total(l) > 0);
+ break;
+ case rule_node:
+ b = (rule_width(l) > 0) || (rule_total(l) > 0);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, b);
+ return 1;
+}
+
+/* node.direct.getglyphwhd */
+
+/*tex
+
+ When the height and depth of a box is calculated the |y-offset| is taken into account. In \LUATEX\
+ this is different for the height and depth, an historic artifact. However, because that can be
+ controlled we now have this helper, mostly for tracing purposes because it listens to the mode
+ parameter (and one can emulate other scenarios already).
+
+*/
+
+static int nodelib_direct_getglyphdimensions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaledwhd whd = tex_glyph_dimensions_ex(n);
+ lua_pushinteger(L, whd.wd);
+ lua_pushinteger(L, whd.ht);
+ lua_pushinteger(L, whd.dp);
+ lua_pushinteger(L, glyph_expansion(n)); /* in case we need it later on */
+ nodelib_aux_pushxyscales(L, n);
+ return 6;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_getkerndimension(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == kern_node) {
+ lua_pushinteger(L, tex_kern_dimension_ex(n));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* node.direct.getlist */
+
+static int nodelib_direct_getlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ nodelib_push_direct_or_nil_node_prev(L, box_list(n));
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ nodelib_push_direct_or_nil_node_prev(L, kernel_math_list(n));
+ break;
+ case insert_node:
+ /* kind of fuzzy */
+ nodelib_push_direct_or_nil_node_prev(L, insert_list(n));
+ break;
+ case adjust_node:
+ nodelib_push_direct_or_nil_node_prev(L, adjust_list(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ kernel_math_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case insert_node:
+ /* kind of fuzzy */
+ insert_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case adjust_node:
+ adjust_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getleader */
+/* node.direct.setleader */
+
+static int nodelib_direct_getleader(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glue_node) {
+ nodelib_push_direct_or_nil(L, glue_leader_ptr(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setleader(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glue_node) {
+ glue_leader_ptr(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+/* node.direct.getdata */
+/* node.direct.setdata */
+
+/*tex
+
+ These getter and setter get |data| as well as |value| fields. One can make them equivalent to
+ |getvalue| and |setvalue| if needed.
+
+*/
+
+static int nodelib_direct_getdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_data(n));
+ return 1;
+ case rule_node:
+ lua_pushinteger(L, rule_data(n));
+ return 1;
+ case glue_node:
+ lua_pushinteger(L, glue_data(n));
+ return 1;
+ case boundary_node:
+ lua_pushinteger(L, boundary_data(n));
+ return 1;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ nodelib_push_attribute_data(L, n);
+ break;
+ case attribute_value_subtype:
+ /*tex Only used for introspection so it's okay to return 2 values. */
+ lua_pushinteger(L, attribute_index(n));
+ lua_pushinteger(L, attribute_value(n));
+ return 2;
+ default:
+ /*tex We just ignore. */
+ break;
+ }
+ case mark_node:
+ if (lua_toboolean(L, 2)) {
+ lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0);
+ } else {
+ lmt_token_list_to_lua(L, mark_ptr(n));
+ }
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setdata(lua_State *L) /* data and value */
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case rule_node:
+ rule_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case glue_node:
+ glue_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case boundary_node:
+ boundary_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case attribute_node:
+ /*tex Not supported for now! */
+ break;
+ case mark_node:
+ tex_delete_token_reference(mark_ptr(n));
+ mark_ptr(n) = lmt_token_list_from_lua(L, 2); /* check ref */
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.get[left|right|]delimiter */
+/* node.direct.set[left|right|]delimiter */
+
+static int nodelib_direct_getleftdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_left_delimiter(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getrightdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_right_delimiter(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_node(L, n, fence_delimiter_list(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_node(L, n, radical_left_delimiter(n));
+ return 1;
+ case accent_noad:
+ nodelib_push_direct_or_node(L, n, accent_middle_character(n)); /* not really a delimiter */
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setleftdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setrightdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_middle_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case fence_noad:
+ fence_delimiter_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case accent_noad:
+ accent_middle_character(n) = nodelib_valid_direct_from_index(L, 2); /* not really a delimiter */
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.get[top|bottom] */
+/* node.direct.set[top|bottom] */
+
+static int nodelib_direct_gettop(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ nodelib_push_direct_or_nil(L, accent_top_character(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_nil(L, fence_delimiter_top(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getbottom(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ nodelib_push_direct_or_nil(L, accent_bottom_character(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_nil(L, fence_delimiter_bottom(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_settop(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ accent_top_character(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ case fence_noad:
+ fence_delimiter_top(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setbottom(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ accent_bottom_character(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ case fence_noad:
+ fence_delimiter_bottom(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.get[numerator|denominator] */
+/* node.direct.set[numerator|denominator] */
+
+static int nodelib_direct_getnumerator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_numerator(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getdenominator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_denominator(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setnumerator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_numerator(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdenominator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_denominator(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getdegree */
+/* node.direct.setdegree */
+
+static int nodelib_direct_getdegree(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_degree(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setdegree(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case radical_noad:
+ radical_degree(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getchoice */
+/* node.direct.setchoice */
+
+static int nodelib_direct_getchoice(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ halfword c = null;
+ if (n && node_type(n) == choice_node) {
+ switch (lmt_tointeger(L, 2)) {
+ case 1: c =
+ choice_display_mlist(n);
+ break;
+ case 2: c =
+ choice_text_mlist(n);
+ break;
+ case 3:
+ c = choice_script_mlist(n);
+ break;
+ case 4:
+ c = choice_script_script_mlist(n);
+ break;
+ }
+ }
+ nodelib_push_direct_or_nil(L, c);
+ return 1;
+}
+
+static int nodelib_direct_setchoice(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == choice_node) {
+ halfword c = nodelib_valid_direct_from_index(L, 2);
+ switch (lmt_tointeger(L, 2)) {
+ case 1:
+ choice_display_mlist(n) = c;
+ break;
+ case 2:
+ choice_text_mlist(n) = c;
+ break;
+ case 3:
+ choice_script_mlist(n) = c;
+ break;
+ case 4:
+ choice_script_script_mlist(n) = c;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This is an experiment, we have a field left that we can use as attribute. */
+
+static int nodelib_direct_getglyphdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && (node_type(n) == glyph_node) && (glyph_data(n) != unused_attribute_value)) {
+ lua_pushinteger(L, glyph_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setglyphdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ glyph_data(n) = (halfword) luaL_optinteger(L, 2, unused_attribute_value);
+ }
+ return 0;
+}
+
+/* node.direct.getnext */
+/* node.direct.setnext */
+
+static int nodelib_direct_getnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_next(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == lmt_tohalfword(L, 2)) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getprev */
+/* node.direct.setprev */
+
+static int nodelib_direct_getprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_prev(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == lmt_tohalfword(L, 2)) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getboth */
+/* node.direct.setboth */
+
+static int nodelib_direct_getboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+static int nodelib_direct_setboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_prev(n) = nodelib_valid_direct_from_index(L, 2);
+ node_next(n) = nodelib_valid_direct_from_index(L, 3);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword typ = lmt_tohalfword(L, 2);
+ halfword prv = node_prev(n);
+ halfword nxt = node_next(n);
+ nodelib_push_direct_or_nil(L, prv && node_type(prv) == typ ? prv : null);
+ nodelib_push_direct_or_nil(L, nxt && node_type(nxt) == typ ? nxt : null);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.direct.setlink */
+/* node.direct.setsplit */
+
+/*
+ a b b nil c d : prev-a-b-c-d-next
+ nil a b b nil c d nil : nil-a-b-c-d-nil
+*/
+
+static int nodelib_direct_setlink(lua_State *L)
+{
+ int n = lua_gettop(L);
+ halfword h = null; /* head node */
+ halfword t = null; /* tail node */
+ for (int i = 1; i <= n; i++) {
+ /*
+ We don't go for the tail of the current node because we can inject between existing nodes
+ and the nodes themselves can have old values for prev and next, so ... only single nodes
+ are looked at!
+ */
+ if (lua_type(L, i) == LUA_TNUMBER) {
+ halfword c = nodelib_valid_direct_from_index(L, i); /* current node */
+ if (c) {
+ if (c != t) {
+ if (t) {
+ node_next(t) = c;
+ node_prev(c) = t;
+ } else if (i > 1) {
+ /* we assume that the first node is a kind of head */
+ node_prev(c) = null;
+ }
+ t = c;
+ if (! h) {
+ h = t;
+ }
+ } else {
+ /* we ignore duplicate nodes which can be tails or the previous */
+ }
+ } else {
+ /* we ignore bad nodes, but we could issue a message */
+ }
+ } else if (t) {
+ /* safeguard: a nil in the list can be meant as end so we nil the next of tail */
+ node_next(t) = null;
+ } else {
+ /* we just ignore nil nodes and have no tail yet */
+ }
+ }
+ nodelib_push_direct_or_nil(L, h);
+ return 1;
+}
+
+static int nodelib_direct_setsplit(lua_State *L)
+{
+ halfword l = nodelib_valid_direct_from_index(L, 1);
+ halfword r = nodelib_valid_direct_from_index(L, 2); /* maybe default to next */
+ if (l && r) {
+ if (l != r) {
+ node_prev(node_next(l)) = null;
+ node_next(node_prev(r)) = null;
+ }
+ node_next(l) = null;
+ node_prev(r) = null;
+ }
+ return 0;
+}
+
+/*tex Local_par nodes can have frozen properties. */
+
+static int nodelib_direct_getparstate(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (! p) {
+ p = tex_find_par_par(cur_list.head);
+ } else if (node_type(p) != par_node) {
+ while (node_prev(p)) {
+ p = node_prev(p);
+ }
+ }
+ if (p && node_type(p) == par_node) {
+ int limited = lua_toboolean(L, 2);
+ lua_createtable(L, 0, 24);
+ if (p && node_type(p) == par_node) {
+ /* todo: optional: all skip components */
+ lua_push_integer_at_key(L, hsize, tex_get_par_par(p, par_hsize_code));
+ lua_push_integer_at_key(L, leftskip, glue_amount(tex_get_par_par(p, par_left_skip_code)));
+ lua_push_integer_at_key(L, rightskip, glue_amount(tex_get_par_par(p, par_right_skip_code)));
+ lua_push_integer_at_key(L, hangindent, tex_get_par_par(p, par_hang_indent_code));
+ lua_push_integer_at_key(L, hangafter, tex_get_par_par(p, par_hang_after_code));
+ lua_push_integer_at_key(L, parindent, tex_get_par_par(p, par_par_indent_code));
+ if (! limited) {
+ lua_push_integer_at_key(L, parfillleftskip, glue_amount(tex_get_par_par(p, par_par_fill_left_skip_code)));
+ lua_push_integer_at_key(L, parfillskip, glue_amount(tex_get_par_par(p, par_par_fill_right_skip_code)));
+ lua_push_integer_at_key(L, parinitleftskip, glue_amount(tex_get_par_par(p, par_par_init_left_skip_code)));
+ lua_push_integer_at_key(L, parinitrightskip, glue_amount(tex_get_par_par(p, par_par_init_right_skip_code)));
+ lua_push_integer_at_key(L, adjustspacing, tex_get_par_par(p, par_adjust_spacing_code));
+ lua_push_integer_at_key(L, protrudechars, tex_get_par_par(p, par_protrude_chars_code));
+ lua_push_integer_at_key(L, pretolerance, tex_get_par_par(p, par_pre_tolerance_code));
+ lua_push_integer_at_key(L, tolerance, tex_get_par_par(p, par_tolerance_code));
+ lua_push_integer_at_key(L, emergencystretch, tex_get_par_par(p, par_emergency_stretch_code));
+ lua_push_integer_at_key(L, looseness, tex_get_par_par(p, par_looseness_code));
+ lua_push_integer_at_key(L, lastlinefit, tex_get_par_par(p, par_last_line_fit_code));
+ lua_push_integer_at_key(L, linepenalty, tex_get_par_par(p, par_line_penalty_code));
+ lua_push_integer_at_key(L, interlinepenalty, tex_get_par_par(p, par_inter_line_penalty_code));
+ lua_push_integer_at_key(L, clubpenalty, tex_get_par_par(p, par_club_penalty_code));
+ lua_push_integer_at_key(L, widowpenalty, tex_get_par_par(p, par_widow_penalty_code));
+ lua_push_integer_at_key(L, displaywidowpenalty, tex_get_par_par(p, par_display_widow_penalty_code));
+ lua_push_integer_at_key(L, orphanpenalty, tex_get_par_par(p, par_orphan_penalty_code));
+ lua_push_integer_at_key(L, brokenpenalty, tex_get_par_par(p, par_broken_penalty_code));
+ lua_push_integer_at_key(L, adjdemerits, tex_get_par_par(p, par_adj_demerits_code));
+ lua_push_integer_at_key(L, doublehyphendemerits, tex_get_par_par(p, par_double_hyphen_demerits_code));
+ lua_push_integer_at_key(L, finalhyphendemerits, tex_get_par_par(p, par_final_hyphen_demerits_code));
+ lua_push_integer_at_key(L, baselineskip, glue_amount(tex_get_par_par(p, par_baseline_skip_code)));
+ lua_push_integer_at_key(L, lineskip, glue_amount(tex_get_par_par(p, par_line_skip_code)));
+ lua_push_integer_at_key(L, lineskiplimit, tex_get_par_par(p, par_line_skip_limit_code));
+ lua_push_integer_at_key(L, shapingpenaltiesmode, tex_get_par_par(p, par_shaping_penalties_mode_code));
+ lua_push_integer_at_key(L, shapingpenalty, tex_get_par_par(p, par_shaping_penalty_code));
+ }
+ lua_push_specification_at_key(L, parshape, tex_get_par_par(p, par_par_shape_code));
+ if (! limited) {
+ lua_push_specification_at_key(L, interlinepenalties, tex_get_par_par(p, par_inter_line_penalties_code));
+ lua_push_specification_at_key(L, clubpenalties, tex_get_par_par(p, par_club_penalties_code));
+ lua_push_specification_at_key(L, widowpenalties, tex_get_par_par(p, par_widow_penalties_code));
+ lua_push_specification_at_key(L, displaywidowpenalties, tex_get_par_par(p, par_display_widow_penalties_code));
+ lua_push_specification_at_key(L, orphanpenalties, tex_get_par_par(p, par_orphan_penalties_code));
+ }
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* node.type (converts id numbers to type names) */
+
+static int nodelib_hybrid_type(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ halfword i = lmt_tohalfword(L, 1);
+ if (tex_nodetype_is_visible(i)) {
+ lua_push_key_by_index(lmt_interface.node_data[i].lua);
+ return 1;
+ }
+ } else if (lmt_maybe_isnode(L, 1)) {
+ lua_push_key(node);
+ return 1;
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.new (allocate a new node) */
+
+static halfword nodelib_new_node(lua_State *L)
+{
+ quarterword i = unknown_node;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ i = lmt_toquarterword(L, 1);
+ if (! tex_nodetype_is_visible(i)) {
+ i = unknown_node;
+ }
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data);
+ break;
+ }
+ if (tex_nodetype_is_visible(i)) {
+ quarterword j = unknown_subtype;
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ j = lmt_toquarterword(L, 2);
+ break;
+ case LUA_TSTRING:
+ j = nodelib_aux_get_node_subtype_id_from_name(L, 2, lmt_interface.node_data[i].subtypes);
+ break;
+ }
+ return tex_new_node(i, (j == unknown_subtype) ? 0 : j);
+ } else {
+ return luaL_error(L, "invalid node id for creating new node");
+ }
+}
+
+static int nodelib_userdata_new(lua_State *L)
+{
+ lmt_push_node_fast(L, nodelib_new_node(L));
+ return 1;
+}
+
+/* node.direct.new */
+
+static int nodelib_direct_new(lua_State *L)
+{
+ lua_pushinteger(L, nodelib_new_node(L));
+ return 1;
+}
+
+static int nodelib_direct_newtextglyph(lua_State* L)
+{
+ halfword glyph = tex_new_text_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
+ nodelib_aux_setattributelist(L, glyph, 3);
+ lua_pushinteger(L, glyph);
+ return 1;
+}
+
+static int nodelib_direct_newmathglyph(lua_State* L)
+{
+ /*tex For now we don't set a properties, group and/or index here. */
+ halfword glyph = tex_new_math_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
+ nodelib_aux_setattributelist(L, glyph, 3);
+ lua_pushinteger(L, glyph);
+ return 1;
+}
+
+/* node.free (this function returns the 'next' node, because that may be helpful) */
+
+static int nodelib_userdata_free(lua_State *L)
+{
+ if (lua_gettop(L) < 1) {
+ lua_pushnil(L);
+ } else if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ halfword p = node_next(n);
+ tex_flush_node(n);
+ lmt_push_node_fast(L, p);
+ }
+ return 1;
+}
+
+/* node.direct.free */
+
+static int nodelib_direct_free(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword p = node_next(n);
+ tex_flush_node(n);
+ n = p;
+ } else {
+ n = null;
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+/* node.flushnode (no next returned) */
+
+static int nodelib_userdata_flushnode(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ tex_flush_node(n);
+ }
+ return 0;
+}
+
+/* node.direct.flush_node */
+
+static int nodelib_direct_flushnode(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_flush_node(n);
+ }
+ return 0;
+}
+
+/* node.flushlist */
+
+static int nodelib_userdata_flushlist(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n_ptr = lmt_check_isnode(L, 1);
+ tex_flush_node_list(n_ptr);
+ }
+ return 0;
+}
+
+/* node.direct.flush_list */
+
+static int nodelib_direct_flushlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_flush_node_list(n);
+ }
+ return 0;
+}
+
+/* node.remove */
+
+static int nodelib_userdata_remove(lua_State *L)
+{
+ if (lua_gettop(L) < 2) {
+ return luaL_error(L, "Not enough arguments for node.remove()");
+ } else {
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ return 2;
+ } else {
+ halfword current = lmt_check_isnode(L, 2);
+ halfword removed = current;
+ int remove = lua_toboolean(L, 3);
+ if (head == current) {
+ if (node_prev(current)){
+ node_next(node_prev(current)) = node_next(current);
+ }
+ if (node_next(current)){
+ node_prev(node_next(current)) = node_prev(current);
+ }
+ head = node_next(current);
+ current = node_next(current);
+ } else {
+ halfword t = node_prev(current);
+ if (t) {
+ node_next(t) = node_next(current);
+ if (node_next(current)) {
+ node_prev(node_next(current)) = t;
+ }
+ current = node_next(current);
+ } else {
+ return luaL_error(L, "Bad arguments to node.remove()");
+ }
+ }
+ lmt_push_node_fast(L, head);
+ lmt_push_node_fast(L, current);
+ if (remove) {
+ tex_flush_node(removed);
+ return 2;
+ } else {
+ lmt_push_node_fast(L, removed);
+ node_next(removed) = null;
+ node_prev(removed) = null;
+ return 3;
+ }
+ }
+ }
+}
+
+/* node.direct.remove */
+
+static int nodelib_direct_remove(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ if (current) {
+ halfword removed = current;
+ int remove = lua_toboolean(L, 3);
+ halfword prev = node_prev(current);
+ if (head == current) {
+ halfword next = node_next(current);
+ if (prev){
+ node_next(prev) = next;
+ }
+ if (next){
+ node_prev(next) = prev;
+ }
+ head = node_next(current);
+ current = head;
+ } else {
+ if (prev) {
+ halfword next = node_next(current);
+ node_next(prev) = next;
+ if (next) {
+ node_prev(next) = prev;
+ }
+ current = next;
+ } else {
+ /* tex_formatted_warning("nodes","invalid arguments to node.remove"); */
+ return 2;
+ }
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, current);
+ if (remove) {
+ tex_flush_node(removed);
+ return 2;
+ } else {
+ nodelib_push_direct_or_nil(L, removed);
+ node_next(removed) = null;
+ node_prev(removed) = null;
+ return 3;
+ }
+ } else {
+ lua_pushinteger(L, head);
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.insertbefore (insert a node in a list) */
+
+static int nodelib_userdata_insertbefore(lua_State *L)
+{
+ if (lua_gettop(L) < 3) {
+ return luaL_error(L, "Not enough arguments for node.insertbefore()");
+ } else if (lua_isnil(L, 3)) {
+ lua_settop(L, 2);
+ } else {
+ halfword n = lmt_check_isnode(L, 3);
+ if (lua_isnil(L, 1)) {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lmt_push_node_fast(L, n);
+ lua_pushvalue(L, -1);
+ } else {
+ halfword current;
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ current = tex_tail_of_node_list(head);
+ } else {
+ current = lmt_check_isnode(L, 2);
+ }
+ if (head != current) {
+ halfword t = node_prev(current);
+ if (t) {
+ tex_couple_nodes(t, n);
+ } else {
+ return luaL_error(L, "Bad arguments to node.insertbefore()");
+ }
+ }
+ tex_couple_nodes(n, current);
+ lmt_push_node_fast(L, (head == current) ? n : head);
+ lmt_push_node_fast(L, n);
+ }
+ }
+ return 2;
+}
+
+/* node.direct.insertbefore */
+
+static int nodelib_direct_insertbefore(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 3);
+ if (n) {
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ /* no head, ignore current */
+ if (head) {
+ if (! current) {
+ current = tex_tail_of_node_list(head);
+ }
+ if (head != current) {
+ halfword prev = node_prev(current);
+ if (prev) {
+ tex_couple_nodes(prev, n);
+ } else {
+ /* error so just quit and return originals */
+ return 2;
+ }
+ }
+ tex_couple_nodes(n, current); /* nice but incompatible: tex_couple_nodes(tail_of_list(n),current) */
+ lua_pushinteger(L, (head == current) ? n : head);
+ lua_pushinteger(L, n);
+ } else {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ lua_pushinteger(L, n);
+ /* n, n */
+ }
+ } else {
+ lua_settop(L, 2);
+ }
+ return 2;
+}
+
+/* node.insertafter */
+
+static int nodelib_userdata_insertafter(lua_State *L)
+{
+ if (lua_gettop(L) < 3) {
+ return luaL_error(L, "Not enough arguments for node.insertafter()");
+ } else if (lua_isnil(L, 3)) {
+ lua_settop(L, 2);
+ } else {
+ halfword n = lmt_check_isnode(L, 3);
+ if (lua_isnil(L, 1)) {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lmt_push_node_fast(L, n);
+ lua_pushvalue(L, -1);
+ } else {
+ halfword current;
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ current = head;
+ while (node_next(current)) {
+ current = node_next(current);
+ }
+ } else {
+ current = lmt_check_isnode(L, 2);
+ }
+ tex_try_couple_nodes(n, node_next(current));
+ tex_couple_nodes(current, n);
+ lua_pop(L, 2);
+ lmt_push_node_fast(L, n);
+ }
+ }
+ return 2;
+}
+
+/* node.direct.insertafter */
+
+static int nodelib_direct_insertafter(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword n = nodelib_valid_direct_from_index(L, 3);
+ if (n) {
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ if (! current) {
+ current = head;
+ while (node_next(current)) {
+ current = node_next(current);
+ }
+ }
+ tex_try_couple_nodes(n, node_next(current)); /* nice but incompatible: try_couple_nodes(tail_of_list(n), node_next(current)); */
+ tex_couple_nodes(current, n);
+ lua_pop(L, 2);
+ lua_pushinteger(L, n);
+ } else {
+ /* no head, ignore current */
+ node_next(n) = null;
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ lua_pushvalue(L, -1);
+ /* n, n */
+ }
+ } else {
+ lua_settop(L, 2);
+ }
+ return 2;
+}
+
+/* */
+
+static int nodelib_direct_appendaftertail(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (h && n) {
+ tex_couple_nodes(tex_tail_of_node_list(h), n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_prependbeforehead(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (h && n) {
+ tex_couple_nodes(n, tex_head_of_node_list(h));
+ }
+ return 0;
+}
+
+/* node.copylist */
+
+/*tex
+
+ We need to use an intermediate variable as otherwise target is used in the loop and subfields
+ get overwritten (or something like that) which results in crashes and unexpected side effects.
+
+*/
+
+static int nodelib_userdata_copylist(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ return 1; /* the nil itself */
+ } else {
+ halfword m;
+ halfword s = null;
+ halfword n = lmt_check_isnode(L, 1);
+ if ((lua_gettop(L) > 1) && (! lua_isnil(L, 2))) {
+ s = lmt_check_isnode(L, 2);
+ }
+ m = tex_copy_node_list(n, s);
+ lmt_push_node_fast(L, m);
+ return 1;
+ }
+}
+
+/* node.direct.copylist */
+
+static int nodelib_direct_copylist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ halfword s = nodelib_valid_direct_from_index(L, 2);
+ if (n) {
+ halfword m = tex_copy_node_list(n, s);
+ lua_pushinteger(L, m);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.show (node, threshold, max) */
+/* node.direct.show */
+
+static int nodelib_userdata_show(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
+ }
+ return 0;
+}
+
+static int nodelib_direct_show(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
+ }
+ return 0;
+}
+
+/* node.serialize(node, details, threshold, max) */
+/* node.direct.serialize */
+
+static int nodelib_aux_showlist(lua_State* L, halfword box)
+{
+ if (box) {
+ luaL_Buffer buffer;
+ int saved_selector = lmt_print_state.selector;
+ halfword levels = tracing_levels_par;
+ halfword online = tracing_online_par;
+ halfword details = show_node_details_par;
+ halfword depth = lmt_opthalfword(L, 3, show_box_depth_par);
+ halfword breadth = lmt_opthalfword(L, 4, show_box_breadth_par);
+ tracing_levels_par = 0;
+ tracing_online_par = 0;
+ show_node_details_par = lmt_opthalfword(L, 2, details);
+ lmt_print_state.selector = luabuffer_selector_code;
+ lmt_lua_state.used_buffer = &buffer;
+ luaL_buffinit(L, &buffer);
+ tex_show_node_list(box, depth, breadth);
+ tex_print_ln();
+ luaL_pushresult(&buffer);
+ lmt_lua_state.used_buffer = NULL;
+ lmt_print_state.selector = saved_selector;
+ show_node_details_par = details;
+ tracing_levels_par = levels;
+ tracing_online_par = online;
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+}
+
+static int nodelib_common_serialized(lua_State *L, halfword n)
+{
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ return nodelib_aux_showlist(L, n);
+ default:
+ {
+ halfword prv = null;
+ halfword nxt = null;
+ if (tex_nodetype_has_prev(n)) {
+ prv = node_prev(n);
+ node_prev(n) = null;
+ }
+ if (tex_nodetype_has_next(n)) {
+ nxt = node_next(n);
+ node_next(n) = null;
+ }
+ nodelib_aux_showlist(L, n);
+ if (prv) {
+ node_prev(n) = prv;
+ }
+ if (nxt) {
+ node_next(n) = nxt;
+ }
+ return 1;
+ }
+ }
+ }
+ lua_pushliteral(L, "");
+ return 1;
+}
+
+static int nodelib_userdata_serialized(lua_State *L)
+{
+ return nodelib_common_serialized(L, lmt_check_isnode(L, 1));
+}
+
+/* node.direct.show */
+
+static int nodelib_direct_serialized(lua_State *L)
+{
+ return nodelib_common_serialized(L, nodelib_valid_direct_from_index(L, 1));
+}
+
+
+/* node.copy (deep copy) */
+
+static int nodelib_userdata_copy(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ n = tex_copy_node(n);
+ lmt_push_node_fast(L, n);
+ }
+ return 1;
+}
+
+/* node.direct.copy (deep copy) */
+
+static int nodelib_direct_copy(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ /* beware, a glue node can have number 0 (zeropt) so we cannot test for null) */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_copy_node(n);
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ return 1;
+}
+
+/* node.direct.copyonly (use with care) */
+
+static int nodelib_direct_copyonly(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_copy_node_only(n);
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ return 1;
+}
+
+/* node.write (output a node to tex's processor) */
+/* node.append (idem but no attributes) */
+
+static int nodelib_userdata_write(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = lmt_check_isnode(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ while (m) {
+ tex_tail_append(m);
+ if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
+ attach_current_attribute_list(m);
+ }
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+static int nodelib_userdata_append(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = lmt_check_isnode(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tail_append(n);
+ while (m) {
+ tex_tail_append(m);
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+*/
+
+/* node.direct.write (output a node to tex's processor) */
+/* node.direct.append (idem no attributes) */
+
+static int nodelib_direct_write(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = nodelib_valid_direct_from_index(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ while (m) {
+ tex_tail_append(m);
+ if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
+ attach_current_attribute_list(m);
+ }
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+static int nodelib_direct_appendtocurrentlist(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = nodelib_valid_direct_from_index(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ while (m) {
+ tex_tail_append(m);
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+*/
+
+/* node.direct.last */
+
+static int nodelib_direct_lastnode(lua_State *L)
+{
+ halfword m = tex_pop_tail();
+ lua_pushinteger(L, m);
+ return 1;
+}
+
+/* node.direct.hpack */
+
+static int nodelib_aux_packing(lua_State *L, int slot)
+{
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, slot);
+ if (lua_key_eq(s, exactly)) {
+ return packing_exactly;
+ } else if (lua_key_eq(s, additional)) {
+ return packing_additional;
+ } else if (lua_key_eq(s, expanded)) {
+ return packing_expanded;
+ } else if (lua_key_eq(s, substitute)) {
+ return packing_substitute;
+ } else if (lua_key_eq(s, adapted)) {
+ return packing_adapted;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ int m = (int) lua_tointeger(L, slot);
+ if (m >= packing_exactly && m <= packing_adapted) {
+ return m;
+ }
+ break;
+ }
+ }
+ return packing_additional;
+}
+
+static int nodelib_direct_hpack(lua_State *L)
+{
+ halfword p;
+ int w = 0;
+ int m = packing_additional;
+ singleword d = direction_def_value;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ if (top > 1) {
+ w = lmt_roundnumber(L, 2);
+ if (top > 2) {
+ m = nodelib_aux_packing(L, 3);
+ if (top > 3) {
+ d = nodelib_getdirection(L, 4);
+ }
+ }
+ }
+ } else {
+ n = null;
+ }
+ p = tex_hpack(n, w, m, d, holding_none_option);
+ lua_pushinteger(L, p);
+ lua_pushinteger(L, lmt_packaging_state.last_badness);
+ lua_pushinteger(L, lmt_packaging_state.last_overshoot);
+ return 3;
+}
+
+static int nodelib_direct_repack(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ int top = lua_gettop(L);
+ int w = top > 1 ? lmt_roundnumber(L, 2) : 0;
+ int m = top > 2 ? nodelib_aux_packing(L, 3) : packing_additional;
+ tex_repack(n, w, m);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_freeze(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ tex_freeze(n, lua_toboolean(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/* node.direct.vpack */
+
+static int nodelib_direct_verticalbreak(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ scaled ht = lmt_roundnumber(L, 2);
+ scaled dp = lmt_roundnumber(L, 3);
+ n = tex_vert_break(n, ht, dp);
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+static int nodelib_direct_vpack(lua_State *L)
+{
+ halfword p;
+ int w = 0;
+ int m = packing_additional;
+ singleword d = direction_def_value;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ if (top > 1) {
+ w = lmt_roundnumber(L, 2);
+ if (top > 2) {
+ switch (lua_type(L, 3)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 3);
+ if (lua_key_eq(s, additional)) {
+ m = packing_additional;
+ } else if (lua_key_eq(s, exactly)) {
+ m = packing_exactly;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ m = (int) lua_tointeger(L, 3);
+ if (m != packing_exactly && m != packing_additional) {
+ m = packing_additional;
+ }
+ break;
+ }
+ }
+ if (top > 3) {
+ d = nodelib_getdirection(L, 4);
+ }
+ }
+ }
+ } else {
+ n = null;
+ }
+ p = tex_vpack(n, w, m, max_dimen, d, holding_none_option);
+ lua_pushinteger(L, p);
+ lua_pushinteger(L, lmt_packaging_state.last_badness);
+ return 2;
+}
+
+/* node.direct.dimensions */
+/* node.direct.rangedimensions */
+/* node.direct.naturalwidth */
+
+static int nodelib_direct_dimensions(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ scaledwhd siz = { 0, 0, 0 };
+ glueratio g_mult = normal_glue_multiplier;
+ int vertical = 0;
+ int g_sign = normal_glue_sign;
+ int g_order = normal_glue_order;
+ int i = 1;
+ halfword n = null;
+ halfword p = null;
+ if (top > 3) {
+ i += 3;
+ g_mult = (glueratio) lua_tonumber(L, 1); /* integer or float */
+ g_sign = tex_checked_glue_sign(lmt_tohalfword(L, 2));
+ g_order = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ }
+ n = nodelib_valid_direct_from_index(L, i);
+ if (lua_type(L, i + 1) == LUA_TBOOLEAN) {
+ vertical = lua_toboolean(L, i + 1);
+ } else {
+ p = nodelib_valid_direct_from_index(L, i + 1);
+ vertical = lua_toboolean(L, i + 2);
+ }
+ if (n) {
+ if (vertical) {
+ siz = tex_natural_vsizes(n, p, g_mult, g_sign, g_order);
+ } else {
+ siz = tex_natural_hsizes(n, p, g_mult, g_sign, g_order);
+ }
+ }
+ lua_pushinteger(L, siz.wd);
+ lua_pushinteger(L, siz.ht);
+ lua_pushinteger(L, siz.dp);
+ return 3;
+ } else {
+ return luaL_error(L, "missing argument to 'dimensions' (direct node expected)");
+ }
+}
+
+static int nodelib_direct_rangedimensions(lua_State *L) /* parent, first, last */
+{
+ int top = lua_gettop(L);
+ if (top > 1) {
+ scaledwhd siz = { 0, 0, 0 };
+ int vertical = 0;
+ halfword l = nodelib_valid_direct_from_index(L, 1); /* parent */
+ halfword n = nodelib_valid_direct_from_index(L, 2); /* first */
+ halfword p = n;
+ if (lua_type(L, 3) == LUA_TBOOLEAN) {
+ vertical = lua_toboolean(L, 3);
+ } else {
+ p = nodelib_valid_direct_from_index(L, 3); /* last */
+ vertical = lua_toboolean(L, 4);
+ }
+ if (l && n) {
+ if (vertical) {
+ siz = tex_natural_vsizes(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ } else {
+ siz = tex_natural_hsizes(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ }
+ }
+ lua_pushinteger(L, siz.wd);
+ lua_pushinteger(L, siz.ht);
+ lua_pushinteger(L, siz.dp);
+ return 3;
+ } else {
+ return luaL_error(L, "missing argument to 'rangedimensions' (2 or more direct nodes expected)");
+ }
+}
+
+static int nodelib_direct_naturalwidth(lua_State *L) /* parent, first, [last] */
+{
+ int top = lua_gettop(L);
+ if (top > 1) {
+ scaled wd = 0;
+ halfword l = nodelib_valid_direct_from_index(L, 1); /* parent */
+ halfword n = nodelib_valid_direct_from_index(L, 2); /* first */
+ halfword p = nodelib_valid_direct_from_index(L, 3); /* last */
+ if (l && n) {
+ wd = tex_natural_width(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ }
+ lua_pushinteger(L, wd);
+ return 1;
+ } else {
+ return luaL_error(L, "missing argument to 'naturalwidth' (2 or more direct nodes expected)");
+ }
+}
+
+static int nodelib_direct_naturalhsize(lua_State *L)
+{
+ scaled wd = 0;
+ halfword c = null;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ wd = tex_natural_hsize(n, &c);
+ }
+ lua_pushinteger(L, wd);
+ lua_pushinteger(L, c ? glue_amount(c) : 0);
+ nodelib_push_direct_or_nil(L, c);
+ return 3;
+}
+
+static int nodelib_direct_mlisttohlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int style = lmt_get_math_style(L, 2, text_style);
+ int penalties = lua_toboolean(L, 3);
+ int beginclass = lmt_optinteger(L, 4, unset_noad_class);
+ int endclass = lmt_optinteger(L, 5, unset_noad_class);
+ if (! valid_math_class_code(beginclass)) {
+ beginclass = unset_noad_class;
+ }
+ if (! valid_math_class_code(endclass)) {
+ endclass = unset_noad_class;
+ }
+ n = tex_mlist_to_hlist(n, penalties, style, beginclass, endclass, NULL);
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+/*tex
+
+ This function is similar to |get_node_type_id|, for field identifiers. It has to do some more
+ work, because not all identifiers are valid for all types of nodes. We can make this faster if
+ needed but when this needs to be called often something is wrong with the code.
+
+*/
+
+static int nodelib_aux_get_node_field_id(lua_State *L, int n, int node)
+{
+ int t = node_type(node);
+ const char *s = lua_tostring(L, n);
+ if (! s) {
+ return -2;
+ } else if (lua_key_eq(s, next)) {
+ return 0;
+ } else if (lua_key_eq(s, id)) {
+ return 1;
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ return 2;
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ return 3;
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ return -1;
+ }
+ } else {
+ value_info *fields = lmt_interface.node_data[t].fields;
+ if (fields) {
+ if (lua_key_eq(s, list)) {
+ const char *sh = lua_key(head);
+ for (int j = 0; fields[j].lua; j++) {
+ if (fields[j].name == s || fields[j].name == sh) {
+ return j + 3;
+ }
+ }
+ } else {
+ for (int j = 0; fields[j].lua; j++) {
+ if (fields[j].name == s) {
+ return j + 3;
+ }
+ }
+ }
+ }
+ }
+ return -2;
+}
+
+/* node.hasfield */
+
+static int nodelib_userdata_hasfield(lua_State *L)
+{
+ int i = -2;
+ if (! lua_isnil(L, 1)) {
+ i = nodelib_aux_get_node_field_id(L, 2, lmt_check_isnode(L, 1));
+ }
+ lua_pushboolean(L, (i != -2));
+ return 1;
+}
+
+/* node.direct.hasfield */
+
+static int nodelib_direct_hasfield(lua_State *L)
+{
+ int i = -2;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ i = nodelib_aux_get_node_field_id(L, 2, n);
+ }
+ lua_pushboolean(L, (i != -2));
+ return 1;
+}
+
+/* node.types */
+
+static int nodelib_shared_types(lua_State *L)
+{
+ lua_newtable(L);
+ for (int i = 0; lmt_interface.node_data[i].id != -1; i++) {
+ if (lmt_interface.node_data[i].visible) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_rawseti(L, -2, lmt_interface.node_data[i].id);
+ }
+ }
+ return 1;
+}
+
+/* node.fields (fetch the list of valid fields) */
+
+static int nodelib_shared_fields(lua_State *L)
+{
+ int offset = 2;
+ int t = nodelib_aux_get_valid_node_type_id(L, 1);
+ int f = lua_toboolean(L, 2);
+ value_info *fields = lmt_interface.node_data[t].fields;
+ lua_newtable(L);
+ if (f) {
+ lua_push_key(next);
+ lua_push_key(node);
+ lua_rawset(L, -3);
+ lua_push_key(id)
+ lua_push_key(integer);
+ lua_rawset(L, -3);
+ if (tex_nodetype_has_subtype(t)) {
+ lua_push_key(subtype);
+ lua_push_key(integer);
+ lua_rawset(L, -3);
+ offset++;
+ }
+ if (tex_nodetype_has_prev(t)) {
+ lua_push_key(prev);
+ lua_push_key(node);
+ lua_rawset(L, -3);
+ }
+ if (fields) {
+ for (lua_Integer i = 0; fields[i].lua != 0; i++) {
+ /* todo: use other macros */
+ lua_push_key_by_index(fields[i].lua);
+ lua_push_key_by_index(lmt_interface.field_type_values[fields[i].type].lua);
+ // lua_pushinteger(L, fields[i].type);
+ lua_rawset(L, -3);
+ }
+ }
+ } else {
+ lua_push_key(next);
+ lua_rawseti(L, -2, 0);
+ lua_push_key(id);
+ lua_rawseti(L, -2, 1);
+ if (tex_nodetype_has_subtype(t)) {
+ lua_push_key(subtype);
+ lua_rawseti(L, -2, 2);
+ offset++;
+ }
+ if (tex_nodetype_has_prev(t)) {
+ lua_push_key(prev);
+ lua_rawseti(L, -2, -1);
+ }
+ if (fields) {
+ for (lua_Integer i = 0; fields[i].lua != 0; i++) {
+ // lua_push_key_by_index(L, fields[i].lua);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, fields[i].lua);
+ lua_rawseti(L, -2, i + offset);
+ }
+ }
+ }
+ return 1;
+}
+
+/* These should move to texlib ... which might happen. */
+
+static int nodelib_shared_values(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ /*
+ delimiter options (bit set)
+ delimiter modes (bit set)
+ */
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, glue) || lua_key_eq(s, fill)) {
+ return lmt_push_info_values(L, lmt_interface.node_fill_values);
+ } else if (lua_key_eq(s, dir)) {
+ return lmt_push_info_values(L, lmt_interface.direction_values);
+ } else if (lua_key_eq(s, math)) {
+ /*tex A bit strange place, so moved to lmttexlib. */
+ return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
+ } else if (lua_key_eq(s, style)) {
+ /*tex A bit strange place, so moved to lmttexlib. */
+ return lmt_push_info_values(L, lmt_interface.math_style_values);
+ } else if (lua_key_eq(s, page)) {
+ /*tex These are never used, whatsit related. */
+ return lmt_push_info_values(L, lmt_interface.page_contribute_values);
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_shared_subtypes(lua_State *L)
+{
+ value_info *subtypes = NULL;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ {
+ /* official accessors */
+ const char *s = lua_tostring(L,1);
+ if (lua_key_eq(s, glyph)) subtypes = lmt_interface.node_data[glyph_node] .subtypes;
+ else if (lua_key_eq(s, glue)) subtypes = lmt_interface.node_data[glue_node] .subtypes;
+ else if (lua_key_eq(s, dir)) subtypes = lmt_interface.node_data[dir_node] .subtypes;
+ else if (lua_key_eq(s, mark)) subtypes = lmt_interface.node_data[mark_node] .subtypes;
+ else if (lua_key_eq(s, boundary)) subtypes = lmt_interface.node_data[boundary_node] .subtypes;
+ else if (lua_key_eq(s, penalty)) subtypes = lmt_interface.node_data[penalty_node] .subtypes;
+ else if (lua_key_eq(s, kern)) subtypes = lmt_interface.node_data[kern_node] .subtypes;
+ else if (lua_key_eq(s, rule)) subtypes = lmt_interface.node_data[rule_node] .subtypes;
+ else if (lua_key_eq(s, list)
+ || lua_key_eq(s, hlist)
+ || lua_key_eq(s, vlist)) subtypes = lmt_interface.node_data[hlist_node] .subtypes; /* too many but ok as reserved */
+ else if (lua_key_eq(s, adjust)) subtypes = lmt_interface.node_data[adjust_node] .subtypes;
+ else if (lua_key_eq(s, disc)) subtypes = lmt_interface.node_data[disc_node] .subtypes;
+ else if (lua_key_eq(s, math)) subtypes = lmt_interface.node_data[math_node] .subtypes;
+ else if (lua_key_eq(s, noad)) subtypes = lmt_interface.node_data[simple_noad] .subtypes;
+ else if (lua_key_eq(s, radical)) subtypes = lmt_interface.node_data[radical_noad] .subtypes;
+ else if (lua_key_eq(s, accent)) subtypes = lmt_interface.node_data[accent_noad] .subtypes;
+ else if (lua_key_eq(s, fence)) subtypes = lmt_interface.node_data[fence_noad] .subtypes;
+ else if (lua_key_eq(s, choice)) subtypes = lmt_interface.node_data[choice_node] .subtypes;
+ else if (lua_key_eq(s, par)) subtypes = lmt_interface.node_data[par_node] .subtypes;
+ else if (lua_key_eq(s, attribute)) subtypes = lmt_interface.node_data[attribute_node].subtypes;
+ }
+ break;
+ case LUA_TNUMBER:
+ switch (lua_tointeger(L, 1)) {
+ case glyph_node: subtypes = lmt_interface.node_data[glyph_node] .subtypes; break;
+ case glue_node: subtypes = lmt_interface.node_data[glue_node] .subtypes; break;
+ case dir_node: subtypes = lmt_interface.node_data[dir_node] .subtypes; break;
+ case boundary_node: subtypes = lmt_interface.node_data[boundary_node] .subtypes; break;
+ case penalty_node: subtypes = lmt_interface.node_data[penalty_node] .subtypes; break;
+ case kern_node: subtypes = lmt_interface.node_data[kern_node] .subtypes; break;
+ case rule_node: subtypes = lmt_interface.node_data[rule_node] .subtypes; break;
+ case hlist_node: subtypes = lmt_interface.node_data[hlist_node] .subtypes; break;
+ case vlist_node: subtypes = lmt_interface.node_data[vlist_node] .subtypes; break;
+ case adjust_node: subtypes = lmt_interface.node_data[adjust_node] .subtypes; break;
+ case disc_node: subtypes = lmt_interface.node_data[disc_node] .subtypes; break;
+ case math_node: subtypes = lmt_interface.node_data[math_node] .subtypes; break;
+ case simple_noad: subtypes = lmt_interface.node_data[simple_noad] .subtypes; break;
+ case radical_noad: subtypes = lmt_interface.node_data[radical_noad] .subtypes; break;
+ case accent_noad: subtypes = lmt_interface.node_data[accent_noad] .subtypes; break;
+ case fence_noad: subtypes = lmt_interface.node_data[fence_noad] .subtypes; break;
+ case choice_node: subtypes = lmt_interface.node_data[choice_node] .subtypes; break;
+ case par_node: subtypes = lmt_interface.node_data[par_node] .subtypes; break;
+ case attribute_node: subtypes = lmt_interface.node_data[attribute_node].subtypes; break;
+ }
+ break;
+ }
+ if (subtypes) {
+ lua_newtable(L);
+ for (int i = 0; subtypes[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, subtypes[i].lua);
+ lua_rawseti(L, -2, subtypes[i].id);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.slide */
+
+static int nodelib_direct_slide(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ node_prev(node_next(n)) = n;
+ n = node_next(n);
+ }
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.tail (find the end of a list) */
+
+static int nodelib_userdata_tail(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ n = node_next(n);
+ }
+ lmt_push_node_fast(L, n);
+ } else {
+ /*tex We keep the old userdata. */
+ }
+ }
+ return 1;
+}
+
+/* node.direct.tail */
+
+static int nodelib_direct_tail(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ n = node_next(n);
+ }
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.endofmath */
+
+static int nodelib_direct_endofmath(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) == math_node && node_subtype(n) == end_inline_math) {
+ lua_pushinteger(L, n);
+ return 1;
+ } else {
+ int level = 1;
+ while (node_next(n)) {
+ n = node_next(n);
+ if (n && node_type(n) == math_node) {
+ switch (node_subtype(n)) {
+ case begin_inline_math:
+ ++level;
+ break;
+ case end_inline_math:
+ --level;
+ if (level > 0) {
+ break;
+ } else {
+ lua_pushinteger(L, n);
+ return 1;
+ }
+
+ }
+ }
+ }
+ // if (level > 0) {
+ // /* something is wrong */
+ // }
+ }
+ }
+ return 0;
+}
+
+/* node.hasattribute (gets attribute) */
+
+static int nodelib_userdata_hasattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ int key = lmt_tointeger(L, 2);
+ int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
+ if (val > unused_attribute_value) {
+ lua_pushinteger(L, val);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.has_attribute */
+
+static int nodelib_direct_hasattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int key = nodelib_valid_direct_from_index(L, 2);
+ int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
+ if (val > unused_attribute_value) {
+ lua_pushinteger(L, val);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.get_attribute */
+
+static int nodelib_userdata_getattribute(lua_State *L)
+{
+ halfword p = lmt_check_isnode(L, 1);
+ if (tex_nodetype_has_attributes(node_type(p))) {
+ p = node_attr(p);
+ if (p) {
+ p = node_next(p);
+ if (p) {
+ int i = lmt_optinteger(L, 2, 0);
+ while (p) {
+ if (attribute_index(p) == i) {
+ int v = attribute_value(p);
+ if (v == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, v);
+ return 1;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_findattributerange(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword i = lmt_tohalfword(L, 2);
+ while (h) {
+ if (tex_nodetype_has_attributes(node_type(h))) {
+ halfword p = node_attr(h);
+ if (p) {
+ p = node_next(p);
+ while (p) {
+ if (attribute_index(p) == i) {
+ if (attribute_value(p) == unused_attribute_value) {
+ break;
+ } else {
+ halfword t = h;
+ while (node_next(t)) {
+ t = node_next(t);
+ }
+ while (t != h) {
+ if (tex_nodetype_has_attributes(node_type(t))) {
+ halfword a = node_attr(t);
+ if (a) {
+ a = node_next(a);
+ while (a) {
+ if (attribute_index(a) == i) {
+ if (attribute_value(a) == unused_attribute_value) {
+ break;
+ } else {
+ goto FOUND;
+ }
+ } else if (attribute_index(a) > i) {
+ break;
+ }
+ a = node_next(a);
+ }
+ }
+ }
+ t = node_prev(t);
+ }
+ FOUND:
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, t);
+ return 2;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ h = node_next(h);
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getattribute */
+/* node.direct.setattribute */
+/* node.direct.unsetattribute */
+/* node.direct.findattribute */
+
+static int nodelib_direct_getattribute(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) {
+ if (node_type(p) != attribute_node) {
+ p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
+ }
+ if (p) {
+ if (node_subtype(p) == attribute_list_subtype) {
+ p = node_next(p);
+ }
+ if (p) {
+ halfword index = lmt_opthalfword(L, 2, 0);
+ while (p) {
+ halfword i = attribute_index(p);
+ if (i == index) {
+ int v = attribute_value(p);
+ if (v == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, v);
+ return 1;
+ }
+ } else if (i > index) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getattributes(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) {
+ if (node_type(p) != attribute_node) {
+ p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
+ }
+ if (p) {
+ if (node_subtype(p) == attribute_list_subtype) {
+ p = node_next(p);
+ }
+ if (p) {
+ int top = lua_gettop(L);
+ for (int i = 2; i <= top; i++) {
+ halfword a = lmt_tohalfword(L, i);
+ halfword n = p;
+ halfword v = unused_attribute_value;
+ while (n) {
+ halfword id = attribute_index(n);
+ if (id == a) {
+ v = attribute_value(n);
+ break;
+ } else if (id > a) {
+ break;
+ } else {
+ n = node_next(n);
+ }
+ }
+ if (v == unused_attribute_value) {
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, v);
+ }
+ }
+ return top - 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n))) { // already checked
+ halfword index = lmt_tohalfword(L, 2);
+ halfword value = lmt_optinteger(L, 3, unused_attribute_value);
+ // if (value == unused_attribute_value) {
+ // tex_unset_attribute(n, index, value);
+ // } else {
+ tex_set_attribute(n, index, value);
+ // }
+ }
+ return 0;
+}
+
+/* set_attributes(n,[initial,]key1,val1,key2,val2,...) */
+
+static int nodelib_direct_setattributes(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n))) {
+ int top = lua_gettop(L);
+ int ini = 2;
+ if (lua_type(L, 2) == LUA_TBOOLEAN) {
+ ++ini;
+ if (lua_toboolean(L, 2) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ }
+ for (int i = ini; i <= top; i += 2) {
+ halfword key = lmt_tohalfword(L, i);
+ halfword val = lmt_optinteger(L, i + 1, unused_attribute_value);
+ // if (val == unused_attribute_value) {
+ // tex_unset_attribute(p, key, val);
+ // } else {
+ tex_set_attribute(n, key, val);
+ // }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_patchattributes(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) { /* todo: check if attributes */
+ halfword att = null;
+ int top = lua_gettop(L);
+ for (int i = 2; i <= top; i += 2) {
+ halfword index = lmt_tohalfword(L, i);
+ halfword value = lua_type(L, i + 1) == LUA_TNUMBER ? lmt_tohalfword(L, i + 1) : unused_attribute_value;
+ if (att) {
+ att = tex_patch_attribute_list(att, index, value);
+ } else {
+ att = tex_copy_attribute_list_set(node_attr(p), index, value);
+ }
+ }
+ tex_attach_attribute_list_attribute(p, att);
+ }
+ return 0;
+}
+
+static int nodelib_direct_findattribute(lua_State *L) /* returns attr value and node */
+{
+ halfword c = nodelib_valid_direct_from_index(L, 1);
+ if (c) {
+ halfword i = lmt_tohalfword(L, 2);
+ while (c) {
+ if (tex_nodetype_has_attributes(node_type(c))) {
+ halfword p = node_attr(c);
+ if (p) {
+ p = node_next(p);
+ while (p) {
+ if (attribute_index(p) == i) {
+ halfword ret = attribute_value(p);
+ if (ret == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, ret);
+ lua_pushinteger(L, c);
+ return 2;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ c = node_next(c);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_unsetattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword key = lmt_checkhalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ halfword ret = tex_unset_attribute(n, key, val);
+ if (ret > unused_attribute_value) { /* != */
+ lua_pushinteger(L, ret);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+static int nodelib_direct_unsetattributes(lua_State *L)
+{
+ halfword key = lmt_checkhalfword(L, 1);
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ halfword last = nodelib_valid_direct_from_index(L, 3);
+ if (first) {
+ tex_unset_attributes(first, last, key);
+ }
+ return 0;
+}
+
+/* node.set_attribute */
+/* node.unset_attribute */
+
+static int nodelib_userdata_setattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ halfword key = lmt_tohalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ tex_unset_attribute(n, key, val);
+ } else {
+ tex_set_attribute(n, key, val);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_userdata_unsetattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ halfword key = lmt_checkhalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ halfword ret = tex_unset_attribute(n, key, val);
+ if (ret > unused_attribute_value) {
+ lua_pushinteger(L, ret);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getglue */
+/* node.direct.setglue */
+/* node.direct.iszeroglue */
+
+static int nodelib_direct_getglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ lua_pushinteger(L, glue_amount(n));
+ lua_pushinteger(L, glue_stretch(n));
+ lua_pushinteger(L, glue_shrink(n));
+ lua_pushinteger(L, glue_stretch_order(n));
+ lua_pushinteger(L, glue_shrink_order(n));
+ return 5;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushnumber(L, (double) box_glue_set(n)); /* float */
+ lua_pushinteger(L, box_glue_order(n));
+ lua_pushinteger(L, box_glue_sign(n));
+ return 3;
+ case math_node:
+ lua_pushinteger(L, math_amount(n));
+ lua_pushinteger(L, math_stretch(n));
+ lua_pushinteger(L, math_shrink(n));
+ lua_pushinteger(L, math_stretch_order(n));
+ lua_pushinteger(L, math_shrink_order(n));
+ return 5;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ glue_amount(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
+ glue_stretch(n) = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
+ glue_shrink(n) = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
+ glue_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
+ glue_shrink_order(n) = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_glue_set(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (glueratio) lua_tonumber(L, 2) : 0;
+ box_glue_order(n) = tex_checked_glue_sign((top > 2 && lua_type(L, 3) == LUA_TNUMBER) ? (halfword) lua_tointeger(L, 3) : 0);
+ box_glue_sign(n) = tex_checked_glue_order((top > 3 && lua_type(L, 4) == LUA_TNUMBER) ? (halfword) lua_tointeger(L, 4) : 0);
+ break;
+ case math_node:
+ math_amount(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
+ math_stretch(n) = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
+ math_shrink(n) = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
+ math_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
+ math_shrink_order(n) = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_iszeroglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ lua_pushboolean(L, glue_amount(n) == 0 && glue_stretch(n) == 0 && glue_shrink(n) == 0);
+ return 1;
+ case hlist_node:
+ case vlist_node:
+ lua_pushboolean(L, box_glue_set(n) == 0.0 && box_glue_order(n) == 0 && box_glue_sign(n) == 0);
+ return 1;
+ case math_node:
+ lua_pushboolean(L, math_amount(n) == 0 && math_stretch(n) == 0 && math_shrink(n) == 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* direct.startofpar */
+
+static int nodelib_direct_startofpar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_pushboolean(L, n && tex_is_start_of_par_node(n));
+ return 1;
+}
+
+/* iteration */
+
+static int nodelib_aux_nil(lua_State *L)
+{
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.traverse */
+/* node.direct.traverse_id */
+/* node.direct.traverse_char */
+/* node.direct.traverse_glyph */
+/* node.direct.traverse_list */
+/* node.direct.traverse_leader */
+
+static int nodelib_direct_aux_next(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverse(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseid(lua_State *L)
+{
+ if (lua_isnil(L, 2)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (n) {
+ if (lua_toboolean(L, 3)) {
+ if (lua_toboolean(L, 4)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_direct_aux_prev_filtered, 1);
+ } else {
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_direct_aux_next_filtered, 1);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_char(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ lua_pushinteger(L, glyph_data(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_char(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ lua_pushinteger(L, glyph_data(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traversechar(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_char, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_char, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_glyph(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != glyph_node) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_glyph(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != glyph_node) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseglyph(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_glyph, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_glyph, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_list(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_list(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverselist(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_list, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_list, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+/*tex This is an experiment. */
+
+static int nodelib_direct_aux_next_leader(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_leader(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseleader(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_leader, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_leader, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+
+/*tex This is an experiment. */
+
+static int nodelib_direct_aux_next_content(lua_State *L)
+{
+ halfword t;
+ halfword l = null;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t) {
+ switch (node_type(t)) {
+ case glyph_node:
+ case disc_node:
+ case rule_node:
+ goto FOUND;
+ case glue_node:
+ l = glue_leader_ptr(t);
+ if (l) {
+ goto FOUND;
+ } else {
+ break;
+ }
+ case hlist_node:
+ case vlist_node:
+ l = box_list(t);
+ goto FOUND;
+ }
+ t = node_next(t);
+ }
+ lua_pushnil(L);
+ return 1;
+ FOUND:
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ if (l) {
+ nodelib_push_direct_or_nil(L, l);
+ return 4;
+ } else {
+ return 3;
+ }
+}
+
+static int nodelib_direct_aux_prev_content(lua_State *L)
+{
+ halfword t;
+ halfword l = null;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t) {
+ switch (node_type(t)) {
+ case glyph_node:
+ case disc_node:
+ case rule_node:
+ goto FOUND;
+ case glue_node:
+ l = glue_leader_ptr(t);
+ if (l) {
+ goto FOUND;
+ } else {
+ break;
+ }
+ case hlist_node:
+ case vlist_node:
+ l = box_list(t);
+ goto FOUND;
+ }
+ t = node_prev(t);
+ }
+ lua_pushnil(L);
+ return 1;
+ FOUND:
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ if (l) {
+ nodelib_push_direct_or_nil(L, l);
+ return 4;
+ } else {
+ return 3;
+ }
+}
+
+static int nodelib_direct_traversecontent(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_content, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_content, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+/* node.traverse */
+/* node.traverse_id */
+/* node.traverse_char */
+/* node.traverse_glyph */
+/* node.traverse_list */
+
+static int nodelib_aux_next(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L, 1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_aux_prev(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L, 1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_traverse(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = lmt_check_isnode(L, 1);
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_aux_prev, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_aux_next, 0);
+ }
+ lmt_push_node_fast(L, n);
+ lua_pushnil(L);
+ return 3;
+ }
+}
+
+static int nodelib_aux_next_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ /* first call */
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L,1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_next(t);
+ lua_settop(L,2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_next(t);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_aux_prev_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ /* first call */
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L,1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_prev(t);
+ lua_settop(L,2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_prev(t);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_traverse_id(lua_State *L)
+{
+ if (lua_isnil(L, 2)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = lmt_check_isnode(L, 2);
+ if (lua_toboolean(L, 3)) {
+ if (lua_toboolean(L, 4)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_aux_prev_filtered, 1);
+ } else {
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_aux_next_filtered, 1);
+ }
+ lmt_push_node_fast(L, n);
+ lua_pushnil(L);
+ return 3;
+ }
+}
+
+/* node.direct.length */
+/* node.direct.count */
+
+/*tex As with some other function that have a |last| we don't take that one into account. */
+
+static int nodelib_direct_length(lua_State *L)
+{
+ halfword first = nodelib_valid_direct_from_index(L, 1);
+ halfword last = nodelib_valid_direct_from_index(L, 2);
+ int count = 0;
+ if (first) {
+ while (first != last) {
+ count++;
+ first = node_next(first);
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+static int nodelib_direct_count(lua_State *L)
+{
+ quarterword id = lmt_toquarterword(L, 1);
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ halfword last = nodelib_valid_direct_from_index(L, 3);
+ int count = 0;
+ if (first) {
+ while (first != last) {
+ if (node_type(first) == id) {
+ count++;
+ }
+ first = node_next(first);
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+/*tex A few helpers for later usage: */
+
+inline static int nodelib_getattribute_value(lua_State *L, halfword n, int index)
+{
+ halfword key = (halfword) lua_tointeger(L, index);
+ halfword val = tex_has_attribute(n, key, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, val);
+ }
+ return 1;
+}
+
+inline static void nodelib_setattribute_value(lua_State *L, halfword n, int kindex, int vindex)
+{
+ if (lua_gettop(L) >= kindex) {
+ halfword key = lmt_tohalfword(L, kindex);
+ halfword val = lmt_opthalfword(L, vindex, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ tex_unset_attribute(n, key, val);
+ } else {
+ tex_set_attribute(n, key, val);
+ }
+ } else {
+ luaL_error(L, "incorrect number of arguments");
+ }
+}
+
+/* node.direct.getfield */
+/* node.getfield */
+
+/*tex
+
+ The order is somewhat determined by the occurance of nodes and importance of fields. We use
+ |somenode[9]| as interface to attributes ... 30\% faster than has_attribute (1) because there
+ is no \LUA\ function overhead, and (2) because we already know that we deal with a node so no
+ checking is needed. The fast typecheck is needed (lua_check... is a slow down actually).
+
+ This is just a reminder for me: when used in the build page routine the |last_insert_ptr| and
+ |best_insert_ptr| are sort of tricky as the first in a list can be a fake node (zero zero list
+ being next). Because no properties are accessed this works ok. In the getfield routines we
+ can assume that these nodes are never seen (the pagebuilder constructs insert nodes using that
+ data). But it is something to keep an eye on when we open up more or add callbacks. So there
+ is a comment below.
+
+*/
+
+static int nodelib_common_getfield(lua_State *L, int direct, halfword n)
+{
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ {
+ return nodelib_getattribute_value(L, n, 2);
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 2);
+ int t = node_type(n);
+ if (lua_key_eq(s, id)) {
+ lua_pushinteger(L, t);
+ } else if (lua_key_eq(s, next)) {
+ if (tex_nodetype_has_next(t)) {
+ nodelib_push_direct_or_node(L, direct, node_next(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ nodelib_push_direct_or_node(L, direct, node_prev(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ nodelib_push_direct_or_node(L, direct, node_attr(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ lua_pushinteger(L, node_subtype(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else {
+ switch(t) {
+ case glyph_node:
+ if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, glyph_font(n));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, glyph_character(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L, glyph_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L, glyph_y_offset(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L, glyph_data(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, tex_glyph_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, tex_glyph_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ // lua_pushinteger(L, char_depth_from_glyph(n));
+ lua_pushinteger(L, tex_glyph_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ // lua_pushinteger(L, char_total_from_glyph(n));
+ lua_pushinteger(L, tex_glyph_total(n));
+ } else if (lua_key_eq(s, scale)) {
+ lua_pushinteger(L, glyph_scale(n));
+ } else if (lua_key_eq(s, xscale)) {
+ lua_pushinteger(L, glyph_x_scale(n));
+ } else if (lua_key_eq(s, yscale)) {
+ lua_pushinteger(L, glyph_y_scale(n));
+ } else if (lua_key_eq(s, expansion)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ } else if (lua_key_eq(s, state)) {
+ lua_pushinteger(L, get_glyph_state(n));
+ } else if (lua_key_eq(s, script)) {
+ lua_pushinteger(L, get_glyph_script(n));
+ } else if (lua_key_eq(s, language)) {
+ lua_pushinteger(L, get_glyph_language(n));
+ } else if (lua_key_eq(s, lhmin)) {
+ lua_pushinteger(L, get_glyph_lhmin(n));
+ } else if (lua_key_eq(s, rhmin)) {
+ lua_pushinteger(L, get_glyph_rhmin(n));
+ } else if (lua_key_eq(s, left)) {
+ lua_pushinteger(L, get_glyph_left(n));
+ } else if (lua_key_eq(s, right)) {
+ lua_pushinteger(L, get_glyph_right(n));
+ } else if (lua_key_eq(s, uchyph)) {
+ lua_pushinteger(L, get_glyph_uchyph(n));
+ } else if (lua_key_eq(s, hyphenate)) {
+ lua_pushinteger(L, get_glyph_hyphenate(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, get_glyph_options(n));
+ } else if (lua_key_eq(s, discpart)) {
+ lua_pushinteger(L, get_glyph_discpart(n));
+ } else if (lua_key_eq(s, protected)) {
+ lua_pushinteger(L, glyph_protected(n));
+ } else if (lua_key_eq(s, properties)) {
+ lua_pushinteger(L, glyph_properties(n));
+ } else if (lua_key_eq(s, group)) {
+ lua_pushinteger(L, glyph_group(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, glyph_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ /* candidates: whd (width,height,depth) */
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, box_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, box_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, box_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, box_total(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ } else if (lua_key_eq(s, shift)) {
+ lua_pushinteger(L, box_shift_amount(n));
+ } else if (lua_key_eq(s, glueorder)) {
+ lua_pushinteger(L, box_glue_order(n));
+ } else if (lua_key_eq(s, gluesign)) {
+ lua_pushinteger(L, box_glue_sign(n));
+ } else if (lua_key_eq(s, glueset)) {
+ lua_pushnumber(L, (double) box_glue_set(n)); /* float */
+ } else if (lua_key_eq(s, geometry)) {
+ lua_pushinteger(L, box_geometry(n));
+ } else if (lua_key_eq(s, orientation)) {
+ lua_pushinteger(L, box_orientation(n));
+ } else if (lua_key_eq(s, anchor)) {
+ lua_pushinteger(L, box_anchor(n));
+ } else if (lua_key_eq(s, source)) {
+ lua_pushinteger(L, box_source_anchor(n));
+ } else if (lua_key_eq(s, target)) {
+ lua_pushinteger(L, box_target_anchor(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L, box_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L, box_y_offset(n));
+ } else if (lua_key_eq(s, woffset)) {
+ lua_pushinteger(L, box_w_offset(n));
+ } else if (lua_key_eq(s, hoffset)) {
+ lua_pushinteger(L, box_h_offset(n));
+ } else if (lua_key_eq(s, doffset)) {
+ lua_pushinteger(L, box_d_offset(n));
+ } else if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, box_pre_migrated(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, box_post_migrated(n));
+ } else if (lua_key_eq(s, state)) {
+ lua_pushinteger(L, box_package_state(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, box_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case disc_node:
+ if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, disc_pre_break_head(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, disc_post_break_head(n));
+ } else if (lua_key_eq(s, replace)) {
+ nodelib_push_direct_or_node(L, direct, disc_no_break_head(n));
+ } else if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, disc_penalty(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, disc_options(n));
+ } else if (lua_key_eq(s, class)) {
+ lua_pushinteger(L, disc_class(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case glue_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, glue_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, glue_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, glue_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, glue_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, glue_shrink_order(n));
+ } else if (lua_key_eq(s, leader)) {
+ nodelib_push_direct_or_node(L, direct, glue_leader_ptr(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, glue_font(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L, glue_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case kern_node:
+ if (lua_key_eq(s, kern)) {
+ lua_pushinteger(L, kern_amount(n));
+ } else if (lua_key_eq(s, expansion)) {
+ lua_pushinteger(L, kern_expansion(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case penalty_node:
+ if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, penalty_amount(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case rule_node:
+ /* candidates: whd (width,height,depth) */
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, rule_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, rule_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, rule_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, rule_total(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L,rule_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L,rule_y_offset(n));
+ } else if (lua_key_eq(s, left)) {
+ lua_pushinteger(L,rule_left(n));
+ } else if (lua_key_eq(s, right)) {
+ lua_pushinteger(L,rule_right(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L,rule_data(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ } else if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, rule_character(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case dir_node:
+ if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, dir_direction(n));
+ } else if (lua_key_eq(s, level)) {
+ lua_pushinteger(L, dir_level(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case whatsit_node:
+ lua_pushnil(L);
+ break;
+ case par_node:
+ /* not all of them here */
+ if (lua_key_eq(s, interlinepenalty)) {
+ lua_pushinteger(L, tex_get_local_interline_penalty(n));
+ } else if (lua_key_eq(s, brokenpenalty)) {
+ lua_pushinteger(L, tex_get_local_broken_penalty(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, par_dir(n));
+ } else if (lua_key_eq(s, leftbox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_left(n));
+ } else if (lua_key_eq(s, leftboxwidth)) {
+ lua_pushinteger(L, tex_get_local_left_width(n));
+ } else if (lua_key_eq(s, rightbox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_right(n));
+ } else if (lua_key_eq(s, rightboxwidth)) {
+ lua_pushinteger(L, tex_get_local_right_width(n));
+ } else if (lua_key_eq(s, middlebox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_middle(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, kernel_math_family(n));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, kernel_math_character(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, kernel_math_options(n));
+ } else if (lua_key_eq(s, properties)) {
+ lua_pushinteger(L, kernel_math_properties(n));
+ } else if (lua_key_eq(s, group)) {
+ lua_pushinteger(L, kernel_math_group(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, kernel_math_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case mark_node:
+ if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ lua_pushinteger(L, mark_index(n));
+ } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
+ if (lua_toboolean(L, 3)) {
+ lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0);
+ } else {
+ lmt_token_list_to_lua(L, mark_ptr(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case insert_node:
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, cost)) {
+ lua_pushinteger(L, insert_float_cost(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, insert_max_depth(n));
+ } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
+ lua_pushinteger(L, insert_total_height(n));
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, insert_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case math_node:
+ if (lua_key_eq(s, surround)) {
+ lua_pushinteger(L, math_surround(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, math_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, math_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, math_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, math_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, math_shrink_order(n));
+ } else if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, math_penalty(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case style_node:
+ if (lua_key_eq(s, style)) {
+ lmt_push_math_style_name(L, style_style(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case parameter_node:
+ if (lua_key_eq(s, style)) {
+ lmt_push_math_style_name(L, parameter_style(n));
+ } else if (lua_key_eq(s, name)) {
+ lmt_push_math_parameter(L, parameter_name(n));
+ } else if (lua_key_eq(s, value)) {
+ halfword code = parameter_name(n);
+ if (code < 0 || code >= math_parameter_last) {
+ /* error */
+ lua_pushnil(L);
+ } else if (math_parameter_value_type(code)) {
+ /* todo, see tex_getmathparm */
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, parameter_value(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (lua_key_eq(s, nucleus)) {
+ nodelib_push_direct_or_nil(L, noad_nucleus(n));
+ } else if (lua_key_eq(s, sub)) {
+ nodelib_push_direct_or_nil(L, noad_subscr(n));
+ } else if (lua_key_eq(s, sup)) {
+ nodelib_push_direct_or_nil(L, noad_supscr(n));
+ } else if (lua_key_eq(s, prime)) {
+ nodelib_push_direct_or_nil(L, noad_prime(n));
+ } else if (lua_key_eq(s, subpre)) {
+ nodelib_push_direct_or_nil(L, noad_subprescr(n));
+ } else if (lua_key_eq(s, suppre)) {
+ nodelib_push_direct_or_nil(L, noad_supprescr(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, noad_options(n));
+ } else if (lua_key_eq(s, source)) {
+ lua_pushinteger(L, noad_source(n));
+ } else if (lua_key_eq(s, scriptorder)) {
+ lua_pushinteger(L, noad_script_order(n));
+ } else if (lua_key_eq(s, class)) {
+ lua_pushinteger(L, get_noad_main_class(n));
+ lua_pushinteger(L, get_noad_left_class(n));
+ lua_pushinteger(L, get_noad_right_class(n));
+ return 3;
+ } else {
+ switch(t) {
+ case simple_noad:
+ lua_pushnil(L);
+ break;
+ case radical_noad:
+ if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
+ nodelib_push_direct_or_node(L, direct, radical_left_delimiter(n));
+ } else if (lua_key_eq(s, right)) {
+ nodelib_push_direct_or_node(L, direct, radical_right_delimiter(n));
+ } else if (lua_key_eq(s, degree)) {
+ nodelib_push_direct_or_node(L, direct, radical_degree(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, noad_width(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case fraction_noad:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, fraction_rule_thickness(n));
+ } else if (lua_key_eq(s, numerator)) {
+ nodelib_push_direct_or_nil(L, fraction_numerator(n));
+ } else if (lua_key_eq(s, denominator)) {
+ nodelib_push_direct_or_nil(L, fraction_denominator(n));
+ } else if (lua_key_eq(s, left)) {
+ nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
+ } else if (lua_key_eq(s, right)) {
+ nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
+ } else if (lua_key_eq(s, middle)) {
+ nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
+ } else if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, noad_family(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case accent_noad:
+ if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_top_character(n));
+ } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_bottom_character(n));
+ } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_middle_character(n));
+ } else if (lua_key_eq(s, fraction)) {
+ lua_pushinteger(L, accent_fraction(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case fence_noad:
+ if (lua_key_eq(s, delimiter)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_list(n));
+ } else if (lua_key_eq(s, top)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_top(n));
+ } else if (lua_key_eq(s, bottom)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_bottom(n));
+ } else if (lua_key_eq(s, italic)) {
+ lua_pushinteger(L, noad_italic(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, noad_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, noad_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, noad_total(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ }
+ }
+ break;
+ case delimiter_node:
+ if (lua_key_eq(s, smallfamily)) {
+ lua_pushinteger(L, delimiter_small_family(n));
+ } else if (lua_key_eq(s, smallchar)) {
+ lua_pushinteger(L, delimiter_small_character(n));
+ } else if (lua_key_eq(s, largefamily)) {
+ lua_pushinteger(L, delimiter_large_family(n));
+ } else if (lua_key_eq(s, largechar)) {
+ lua_pushinteger(L, delimiter_large_character(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, kernel_math_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case split_node:
+ if (lua_key_eq(s, index)) {
+ lua_push_integer(L, split_insert_index(n));
+ } else if (lua_key_eq(s, lastinsert)) {
+ nodelib_push_direct_or_node(L, direct, split_last_insert(n)); /* see comment */
+ } else if (lua_key_eq(s, bestinsert)) {
+ nodelib_push_direct_or_node(L, direct, split_best_insert(n)); /* see comment */
+ } else if (lua_key_eq(s, broken)) {
+ nodelib_push_direct_or_node(L, direct, split_broken(n));
+ } else if (lua_key_eq(s, brokeninsert)) {
+ nodelib_push_direct_or_node(L, direct, split_broken_insert(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case choice_node:
+ /*tex We could check and combine some here but who knows how things evolve. */
+ if (lua_key_eq(s, display)) {
+ nodelib_push_direct_or_node(L, direct, choice_display_mlist(n));
+ } else if (lua_key_eq(s, text)) {
+ nodelib_push_direct_or_node(L, direct, choice_text_mlist(n));
+ } else if (lua_key_eq(s, script)) {
+ nodelib_push_direct_or_node(L, direct, choice_script_mlist(n));
+ } else if (lua_key_eq(s, scriptscript)) {
+ nodelib_push_direct_or_node(L, direct, choice_script_script_mlist(n));
+ } else if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, choice_pre_break(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, choice_post_break(n));
+ } else if (lua_key_eq(s, replace)) {
+ nodelib_push_direct_or_node(L, direct, choice_no_break(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ if (lua_key_eq(s, count)) {
+ lua_pushinteger(L, attribute_count(n));
+ } else if (lua_key_eq(s, data)) {
+ nodelib_push_attribute_data(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case attribute_value_subtype:
+ if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
+ lua_pushinteger(L, attribute_index(n));
+ } else if (lua_key_eq(s, value)) {
+ lua_pushinteger(L, attribute_value(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ break;
+ case adjust_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, adjust_list(n));
+ } else if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ lua_pushinteger(L, adjust_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case unset_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, box_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, box_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, box_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, box_total(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, box_glue_shrink(n));
+ } else if (lua_key_eq(s, glueorder)) {
+ lua_pushinteger(L, box_glue_order(n));
+ } else if (lua_key_eq(s, gluesign)) {
+ lua_pushinteger(L, box_glue_sign(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, box_glue_stretch(n));
+ } else if (lua_key_eq(s, count)) {
+ lua_pushinteger(L, box_span_count(n));
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ /*
+ case attribute_list_node:
+ lua_pushnil(L);
+ break;
+ */
+ case boundary_node:
+ if (lua_key_eq(s, data) || lua_key_eq(s, value)) {
+ lua_pushinteger(L, boundary_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case glue_spec_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, glue_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, glue_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, glue_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, glue_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, glue_shrink_order(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ lua_pushnil(L);
+ break;
+ }
+ }
+ return 1;
+}
+
+static int nodelib_direct_getfield(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ return nodelib_common_getfield(L, 1, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_index(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ if (n) {
+ return nodelib_common_getfield(L, 0, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_getfield(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ return nodelib_common_getfield(L, 0, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+/* node.setfield */
+/* node.direct.setfield */
+
+/*
+ We used to check for glue_spec nodes in some places but if you do such a you have it coming
+ anyway.
+*/
+
+static int nodelib_common_setfield(lua_State *L, int direct, halfword n)
+{
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ {
+ nodelib_setattribute_value(L, n, 2, 3);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 2);
+ int t = node_type(n);
+ if (lua_key_eq(s, next)) {
+ if (tex_nodetype_has_next(t)) {
+ node_next(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ node_prev(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ tex_attach_attribute_list_attribute(n, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ node_subtype(n) = lmt_toquarterword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else {
+ switch(t) {
+ case glyph_node:
+ if (lua_key_eq(s, font)) {
+ glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, char)) {
+ glyph_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, xoffset)) {
+ glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, yoffset)) {
+ glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, scale)) {
+ glyph_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_scale(n)) {
+ glyph_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, xscale)) {
+ glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_x_scale(n)) {
+ glyph_x_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, yscale)) {
+ glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_y_scale(n)) {
+ glyph_y_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, data)) {
+ glyph_data(n) = lmt_opthalfword(L, 3, unused_attribute_value);
+ } else if (lua_key_eq(s, expansion)) {
+ glyph_expansion(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, state)) {
+ set_glyph_state(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, script)) {
+ set_glyph_script(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, language)) {
+ set_glyph_language(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, left)) {
+ set_glyph_left(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, right)) {
+ set_glyph_right(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, lhmin)) {
+ set_glyph_lhmin(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, rhmin)) {
+ set_glyph_rhmin(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, uchyph)) {
+ set_glyph_uchyph(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, hyphenate)) {
+ set_glyph_hyphenate(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, options)) {
+ set_glyph_options(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, discpart)) {
+ set_glyph_discpart(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, protected)) {
+ glyph_protected(n) = lmt_tosingleword(L, 3);
+ } else if (lua_key_eq(s, width)) {
+ /* not yet */
+ } else if (lua_key_eq(s, height)) {
+ /* not yet */
+ } else if (lua_key_eq(s, depth)) {
+ /* not yet */
+ } else if (lua_key_eq(s, properties)) {
+ glyph_properties(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, group)) {
+ glyph_group(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ glyph_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case hlist_node:
+ case vlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, width)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, direction)) {
+ box_dir(n) = (singleword) nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, shift)) {
+ box_shift_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, glueorder)) {
+ box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, gluesign)) {
+ box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, glueset)) {
+ box_glue_set(n) = (glueratio) lua_tonumber(L, 3); /* integer or float */
+ } else if (lua_key_eq(s, geometry)) {
+ box_geometry(n) = (singleword) lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, orientation)) {
+ box_orientation(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, anchor)) {
+ box_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, source)) {
+ box_source_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, target)) {
+ box_target_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, xoffset)) {
+ box_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, yoffset)) {
+ box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, woffset)) {
+ box_w_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, hoffset)) {
+ box_h_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, doffset)) {
+ box_d_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, pre)) {
+ box_pre_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
+ } else if (lua_key_eq(s, post)) {
+ box_post_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
+ } else if (lua_key_eq(s, state)) {
+ box_package_state(n) = (singleword) lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ box_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case disc_node:
+ if (lua_key_eq(s, pre)) {
+ tex_set_disc_field(n, pre_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, post)) {
+ tex_set_disc_field(n, post_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, replace)) {
+ tex_set_disc_field(n, no_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, penalty)) {
+ disc_penalty(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ disc_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, class)) {
+ disc_class(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case glue_node:
+ if (lua_key_eq(s, width)) {
+ glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ glue_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ glue_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, leader)) {
+ glue_leader_ptr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, font)) {
+ glue_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, data)) {
+ glue_data(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case kern_node:
+ if (lua_key_eq(s, kern)) {
+ kern_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, expansion)) {
+ kern_expansion(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case penalty_node:
+ if (lua_key_eq(s, penalty)) {
+ penalty_amount(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case rule_node:
+ if (lua_key_eq(s, width)) {
+ rule_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ rule_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ rule_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, xoffset)) {
+ rule_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, yoffset)) {
+ rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, left)) {
+ rule_left(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, right)) {
+ rule_right(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, data)) {
+ rule_data(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, font)) {
+ tex_set_rule_font(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, fam)) {
+ tex_set_rule_family(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, char)) {
+ rule_character(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case dir_node:
+ if (lua_key_eq(s, direction)) {
+ dir_direction(n) = nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, level)) {
+ dir_level(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case whatsit_node:
+ return 0;
+ case par_node:
+ /* not all of them here */
+ if (lua_key_eq(s, interlinepenalty)) {
+ tex_set_local_interline_penalty(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, brokenpenalty)) {
+ tex_set_local_broken_penalty(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, direction)) {
+ par_dir(n) = nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, leftbox)) {
+ par_box_left(n) = nodelib_getlist(L, 3);
+ } else if (lua_key_eq(s, leftboxwidth)) {
+ tex_set_local_left_width(n, lmt_roundnumber(L, 3));
+ } else if (lua_key_eq(s, rightbox)) {
+ par_box_right(n) = nodelib_getlist(L, 3);
+ } else if (lua_key_eq(s, rightboxwidth)) {
+ tex_set_local_right_width(n, lmt_roundnumber(L, 3));
+ } else if (lua_key_eq(s, middlebox)) {
+ par_box_middle(n) = nodelib_getlist(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case math_char_node:
+ case math_text_char_node:
+ if (lua_key_eq(s, fam)) {
+ kernel_math_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, char)) {
+ kernel_math_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ kernel_math_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, properties)) {
+ kernel_math_properties(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, group)) {
+ kernel_math_group(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ kernel_math_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case mark_node:
+ if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ halfword m = lmt_tohalfword(L, 3);
+ if (tex_valid_mark(m)) {
+ mark_index(n) = m;
+ }
+ } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
+ tex_delete_token_reference(mark_ptr(n));
+ mark_ptr(n) = lmt_token_list_from_lua(L, 3); /* check ref */
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case insert_node:
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, cost)) {
+ insert_float_cost(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ insert_max_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
+ insert_total_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ insert_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case math_node:
+ if (lua_key_eq(s, surround)) {
+ math_surround(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, width)) {
+ math_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ math_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ math_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ math_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ math_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, penalty)) {
+ math_penalty(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case style_node:
+ if (lua_key_eq(s, style)) {
+ style_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
+ } else {
+ /* return nodelib_cantset(L, n, s); */
+ }
+ return 0;
+ case parameter_node:
+ if (lua_key_eq(s, style)) {
+ parameter_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
+ } else if (lua_key_eq(s, name)) {
+ parameter_name(n) = lmt_get_math_parameter(L, 2, parameter_name(n));
+ } else if (lua_key_eq(s, value)) {
+ halfword code = parameter_name(n);
+ if (code < 0 || code >= math_parameter_last) {
+ /* error */
+ } else if (math_parameter_value_type(code)) {
+ /* todo, see tex_setmathparm */
+ } else {
+ parameter_value(n) = lmt_tohalfword(L, 3);
+ }
+ }
+ return 0;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ /* fence has less */
+ if (lua_key_eq(s, nucleus)) {
+ noad_nucleus(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, sub)) {
+ noad_subscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, sup)) {
+ noad_supscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, subpre)) {
+ noad_subprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, suppre)) {
+ noad_supprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, prime)) {
+ noad_prime(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, source)) {
+ noad_source(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ noad_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, scriptorder)) {
+ noad_script_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, class)) {
+ halfword c = lmt_tohalfword(L, 3);
+ set_noad_main_class(n, c);
+ set_noad_left_class(n, lmt_opthalfword(L, 4, c));
+ set_noad_right_class(n, lmt_opthalfword(L, 5, c));
+ } else {
+ switch (t) {
+ case simple_noad:
+ // return nodelib_cantset(L, n, s);
+ break;
+ case radical_noad:
+ if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
+ radical_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, right)) {
+ radical_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, degree)) {
+ radical_degree(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, width)) {
+ noad_width(n) = lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case fraction_noad:
+ if (lua_key_eq(s, width)) {
+ fraction_rule_thickness(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, numerator)) {
+ fraction_numerator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, denominator)) {
+ fraction_denominator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, left)) {
+ fraction_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, right)) {
+ fraction_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, middle)) {
+ fraction_middle_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, fam)) {
+ set_noad_family(n, lmt_tohalfword(L, 3));
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case accent_noad:
+ if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
+ accent_top_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
+ accent_bottom_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
+ accent_middle_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, fraction)) {
+ accent_fraction(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case fence_noad:
+ if (lua_key_eq(s, delimiter)) {
+ fence_delimiter_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, top)) {
+ fence_delimiter_top(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bottom)) {
+ fence_delimiter_bottom(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, italic)) {
+ noad_italic(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ noad_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ noad_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ }
+ }
+ return 0;
+ case delimiter_node:
+ if (lua_key_eq(s, smallfamily)) {
+ delimiter_small_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, smallchar)) {
+ delimiter_small_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, largefamily)) {
+ delimiter_large_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, largechar)) {
+ delimiter_large_character(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case sub_box_node:
+ case sub_mlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ kernel_math_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case split_node: /* might go away */
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ split_insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, lastinsert)) {
+ split_last_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bestinsert)) {
+ split_best_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, broken)) {
+ split_broken(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, brokeninsert)) {
+ split_broken_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case choice_node:
+ if (lua_key_eq(s, display)) {
+ choice_display_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, text)) {
+ choice_text_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, script)) {
+ choice_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, scriptscript)) {
+ choice_script_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ if (lua_key_eq(s, count)) {
+ attribute_count(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case attribute_value_subtype:
+ if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
+ attribute_index(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, value)) {
+ attribute_value(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ default:
+ /* just ignored */
+ return 0;
+ }
+ break;
+ case adjust_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ adjust_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_adjust_index(index)) {
+ adjust_index(n) = index;
+ }
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case unset_node:
+ if (lua_key_eq(s, width)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, direction)) {
+ box_dir(n) = (singleword) nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ box_glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, glueorder)) {
+ box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, gluesign)) {
+ box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, stretch)) {
+ box_glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, count)) {
+ box_span_count(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case boundary_node:
+ if (lua_key_eq(s, value)) {
+ boundary_data(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case glue_spec_node:
+ if (lua_key_eq(s, width)) {
+ glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ glue_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ glue_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ default:
+ return luaL_error(L, "you can't assign to a %s node (%d)\n", lmt_interface.node_data[t].name, n);
+ }
+ CANTSET:
+ return luaL_error(L,"you can't set field %s in a %s node (%d)", s, lmt_interface.node_data[t].name, n);
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setfield(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_common_setfield(L, 1, n);
+ }
+ return 0;
+}
+
+static int nodelib_userdata_newindex(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ if (n) {
+ nodelib_common_setfield(L, 0, n);
+ }
+ return 0;
+}
+
+static int nodelib_userdata_setfield(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ nodelib_common_setfield(L, 0, n);
+ }
+ return 0;
+}
+
+/* tex serializing */
+
+static int verbose = 1; /* This might become an option (then move this in a state)! */
+
+static void nodelib_tostring(lua_State *L, halfword n, const char *tag)
+{
+ char msg[256];
+ char a[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
+ char v[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
+ halfword t = node_type(n);
+ halfword s = node_subtype(n);
+ node_info nd = lmt_interface.node_data[t];
+ if (tex_nodetype_has_prev(t) && node_prev(n)) {
+ snprintf(a, 7, "%6d", (int) node_prev(n));
+ }
+ if (node_next(n)) {
+ snprintf(v, 7, "%6d", (int) node_next(n));
+ }
+ if (t == whatsit_node) {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ } else if (! tex_nodetype_has_subtype(n)) {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s>", tag, a, (int) n, v, nd.name);
+ } else if (verbose) {
+ /*tex Sloooow! But subtype lists can have holes. */
+ value_info *sd = nd.subtypes;
+ int j = -1;
+ if (sd) {
+ // if (t == glyph_node) {
+ // s = tex_subtype_of_glyph(n);
+ // }
+ if (s >= nd.first && s <= nd.last) {
+ for (int i = 0; ; i++) {
+ if (sd[i].id == s) {
+ j = i;
+ break ;
+ } else if (sd[i].id < 0) {
+ break;
+ }
+ }
+ }
+ }
+ if (j < 0) {
+ snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ } else {
+ snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %s>", tag, a, (int) n, v, nd.name, sd[j].name);
+ }
+ } else {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ }
+ lua_pushstring(L, (const char *) msg);
+}
+
+/* __tostring node.tostring */
+
+static int nodelib_userdata_tostring(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ nodelib_tostring(L, n, lua_key(node));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.tostring */
+
+static int nodelib_direct_tostring(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_tostring(L, n, lua_key(direct));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* __eq */
+
+static int nodelib_userdata_equal(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ halfword m = *((halfword *) lua_touserdata(L, 2));
+ lua_pushboolean(L, (n == m));
+ return 1;
+}
+
+/* node.ligaturing */
+
+static int nodelib_direct_ligaturing(lua_State *L)
+{
+ if (lua_gettop(L) >= 1) {
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
+ halfword p = node_prev(h);
+ tex_couple_nodes(tmp_head, h);
+ node_tail(tmp_head) = t;
+ t = tex_handle_ligaturing(tmp_head, t);
+ if (p) {
+ node_next(p) = node_next(tmp_head) ;
+ }
+ node_prev(node_next(tmp_head)) = p ;
+ lua_pushinteger(L, node_next(tmp_head));
+ lua_pushinteger(L, t);
+ lua_pushboolean(L, 1);
+ tex_flush_node(tmp_head);
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ lua_pushboolean(L, 0);
+ return 2;
+}
+
+/* node.kerning */
+
+static int nodelib_direct_kerning(lua_State *L)
+{
+ if (lua_gettop(L) >= 1) {
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
+ halfword p = node_prev(h);
+ tex_couple_nodes(tmp_head, h);
+ node_tail(tmp_head) = t;
+ t = tex_handle_kerning(tmp_head, t);
+ if (p) {
+ node_next(p) = node_next(tmp_head) ;
+ }
+ node_prev(node_next(tmp_head)) = p ;
+ lua_pushinteger(L, node_next(tmp_head));
+ if (t) {
+ lua_pushinteger(L, t);
+ } else {
+ lua_pushnil(L);
+ }
+ lua_pushboolean(L, 1);
+ tex_flush_node(tmp_head);
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ lua_pushboolean(L, 0);
+ return 2;
+}
+
+/*tex
+ It's more consistent to have it here (so we will alias in lang later). Todo: if no glyph then
+ quit.
+*/
+
+static int nodelib_direct_hyphenating(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ if (! t) {
+ t = h;
+ while (node_next(t)) {
+ t = node_next(t);
+ }
+ }
+ tex_hyphenate_list(h, t); /* todo: grab new tail */
+ } else {
+ /*tex We could consider setting |h| and |t| to |null|. */
+ }
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, t);
+ lua_pushboolean(L, 1);
+ return 3;
+}
+
+static int nodelib_direct_collapsing(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword c1 = lmt_optinteger(L, 2, ex_hyphen_char_par);
+ halfword c2 = lmt_optinteger(L, 3, 0x2013);
+ halfword c3 = lmt_optinteger(L, 4, 0x2014);
+ tex_collapse_list(h, c1, c2, c3);
+ }
+ lua_pushinteger(L, h);
+ return 1;
+}
+
+/* node.protect_glyphs */
+/* node.unprotect_glyphs */
+
+inline static void nodelib_aux_protect_all(halfword h)
+{
+ while (h) {
+ if (node_type(h) == glyph_node) {
+ glyph_protected(h) = glyph_protected_text_code;
+ }
+ h = node_next(h);
+ }
+}
+inline static void nodelib_aux_unprotect_all(halfword h)
+{
+ while (h) {
+ if (node_type(h) == glyph_node) {
+ glyph_protected(h) = glyph_unprotected_code;
+ }
+ h = node_next(h);
+ }
+}
+
+inline static void nodelib_aux_protect_node(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_protected(n) = glyph_protected_text_code;
+ break;
+ case disc_node:
+ nodelib_aux_protect_all(disc_no_break_head(n));
+ nodelib_aux_protect_all(disc_pre_break_head(n));
+ nodelib_aux_protect_all(disc_post_break_head(n));
+ break;
+ }
+}
+
+inline static void nodelib_aux_unprotect_node(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_protected(n) = glyph_unprotected_code;
+ break;
+ case disc_node:
+ nodelib_aux_unprotect_all(disc_no_break_head(n));
+ nodelib_aux_unprotect_all(disc_pre_break_head(n));
+ nodelib_aux_unprotect_all(disc_post_break_head(n));
+ break;
+ }
+}
+
+/* node.direct.protect_glyphs */
+/* node.direct.unprotect_glyphs */
+
+static int nodelib_direct_protectglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_aux_protect_node(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_unprotectglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_aux_unprotect_node(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_protectglyphs(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword tail = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ while (head) {
+ nodelib_aux_protect_node(head);
+ if (head == tail) {
+ break;
+ } else {
+ head = node_next(head);
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_unprotectglyphs(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword tail = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ while (head) {
+ nodelib_aux_unprotect_node(head);
+ if (head == tail) {
+ break;
+ } else {
+ head = node_next(head);
+ }
+ }
+ }
+ return 0;
+}
+
+/* node.direct.first_glyph */
+
+static int nodelib_direct_firstglyph(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword savetail = null;
+ if (t) {
+ savetail = node_next(t);
+ node_next(t) = null;
+ }
+ /*tex
+ We go to the first unprocessed character so that is one with a value <= 0xFF and we
+ don't care about what the value is.
+ */
+ while (h && (node_type(h) != glyph_node || glyph_protected(h))) {
+ h = node_next(h);
+ }
+ if (savetail) {
+ node_next(t) = savetail;
+ }
+ lua_pushinteger(L, h);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.find_node(head) : node, subtype*/
+/* node.direct.find_node(head,subtype) : node */
+
+static int nodelib_direct_findnode(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword t = lmt_tohalfword(L, 2);
+ if (lua_gettop(L) > 2) {
+ halfword s = lmt_tohalfword(L, 3);
+ while (h) {
+ if (node_type(h) == t && node_subtype(h) == s) {
+ lua_pushinteger(L, h);
+ return 1;
+ } else {
+ h = node_next(h);
+ }
+ }
+ } else {
+ while (h) {
+ if (node_type(h) == t) {
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, node_subtype(h));
+ return 2;
+ } else {
+ h = node_next(h);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.has_glyph */
+
+static int nodelib_direct_hasglyph(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ while (h) {
+ switch (node_type(h)) {
+ case glyph_node:
+ case disc_node:
+ nodelib_push_direct_or_nil(L, h);
+ return 1;
+ default:
+ h = node_next(h);
+ break;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.getword */
+
+static inline int nodelib_aux_in_word(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ case disc_node:
+ return 1;
+ case kern_node:
+ return node_subtype(n) == font_kern_subtype;
+ default:
+ return 0;
+ }
+}
+
+static int nodelib_direct_getwordrange(lua_State *L)
+{
+ halfword m = nodelib_valid_direct_from_index(L, 1);
+ if (m) {
+ /*tex We don't check on type if |m|. */
+ halfword l = m;
+ halfword r = m;
+ while (node_prev(l) && nodelib_aux_in_word(node_prev(l))) {
+ l = node_prev(l);
+ }
+ while (node_next(r) && nodelib_aux_in_word(node_next(r))) {
+ r = node_next(r);
+ }
+ nodelib_push_direct_or_nil(L, l);
+ nodelib_push_direct_or_nil(L, r);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.inuse */
+
+static int nodelib_userdata_inuse(lua_State *L)
+{
+ int counts[max_node_type + 1] = { 0 };
+ int n = tex_n_of_used_nodes(&counts[0]);
+ lua_createtable(L, 0, max_node_type);
+ for (int i = 0; i < max_node_type; i++) {
+ if (counts[i]) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_pushinteger(L, counts[i]);
+ lua_rawset(L, -3);
+ }
+ }
+ lua_pushinteger(L, n);
+ return 2;
+}
+
+/*tex A bit of a cheat: some nodes can turn into another one due to the same size. */
+
+static int nodelib_userdata_instock(lua_State *L)
+{
+ int counts[max_node_type + 1] = { 0 };
+ int n = 0;
+ lua_createtable(L, 0, max_node_type);
+ for (int i = 1; i < max_chain_size; i++) {
+ halfword p = lmt_node_memory_state.free_chain[i];
+ while (p) {
+ if (node_type(p) <= max_node_type) {
+ ++counts[node_type(p)];
+ }
+ p = node_next(p);
+ }
+ }
+ for (int i = 0; i < max_node_type; i++) {
+ if (counts[i]) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_pushinteger(L, counts[i]);
+ lua_rawset(L, -3);
+ n += counts[i];
+ }
+ }
+ lua_pushinteger(L, n);
+ return 2;
+}
+
+
+/* node.usedlist */
+
+static int nodelib_userdata_usedlist(lua_State *L)
+{
+ lmt_push_node_fast(L, tex_list_node_mem_usage());
+ return 1;
+}
+
+/* node.direct.usedlist */
+
+static int nodelib_direct_usedlist(lua_State *L)
+{
+ lua_pushinteger(L, tex_list_node_mem_usage());
+ return 1;
+}
+
+/* node.direct.protrusionskipable(node m) */
+
+static int nodelib_direct_protrusionskipable(lua_State *L)
+{
+ halfword n = lmt_tohalfword(L, 1);
+ if (n) {
+ lua_pushboolean(L, tex_protrusion_skipable(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.currentattributes(node m) */
+
+static int nodelib_userdata_currentattributes(lua_State* L)
+{
+ halfword n = tex_current_attribute_list();
+ if (n) {
+ lmt_push_node_fast(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.currentattributes(node m) */
+
+static int nodelib_direct_currentattributes(lua_State* L)
+{
+ halfword n = tex_current_attribute_list();
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.todirect */
+
+static int nodelib_direct_todirect(lua_State* L)
+{
+ if (lua_type(L, 1) != LUA_TNUMBER) {
+ /* assume node, no further testing, used in known situations */
+ void* n = lua_touserdata(L, 1);
+ if (n) {
+ lua_pushinteger(L, *((halfword*)n));
+ }
+ else {
+ lua_pushnil(L);
+ }
+ } /* else assume direct and returns argument */
+ return 1;
+}
+
+static int nodelib_direct_tovaliddirect(lua_State* L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.tonode */
+
+static int nodelib_direct_tonode(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword* a = (halfword*) lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ } /* else assume node and return argument */
+ return 1;
+}
+
+/* direct.ischar */
+/* direct.isglyph */
+
+/*tex
+
+ This can save a lookup call, but although there is a little benefit it doesn't pay of in the end
+ as we have to simulate it in \MKIV.
+
+ \starttyping
+ if (glyph_data(n) != unused_attribute_value) {
+ lua_pushinteger(L, glyph_data(n));
+ return 2;
+ }
+ \stoptyping
+
+ possible return values:
+
+ \starttyping
+ <nil when no node>
+ <nil when no glyph> <id of node>
+ <false when glyph and already marked as done or when not>
+ <character code when font matches or when no font passed>
+ \stoptyping
+
+ data : when checked should be equal, false or nil is zero
+ state : when checked should be equal, unless false or zero
+
+*/
+
+static int nodelib_direct_check_char(lua_State* L, halfword n)
+{
+ if (! glyph_protected(n)) {
+ halfword b = 0;
+ halfword f = (halfword) lua_tointegerx(L, 2, &b);
+ if (! b) {
+ goto OKAY;
+ } else if (f == glyph_font(n)) {
+ switch (lua_gettop(L)) {
+ case 2:
+ /* (node,font) */
+ goto OKAY;
+ case 3:
+ /* (node,font,data) */
+ if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ case 4:
+ /* (node,font,data,state) */
+ if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
+ halfword state = (halfword) lua_tointegerx(L, 4, NULL);
+ if (! state || state == glyph_state(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ case 5:
+ /* (node,font,data,scale,xscale,yscale) */
+ if (lua_tointeger(L, 3) == glyph_scale(n) && lua_tointeger(L, 4) == glyph_x_scale(n) && lua_tointeger(L, 5) == glyph_y_scale(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ case 6:
+ /* (node,font,data,scale,xscale,yscale) */
+ if (lua_tointegerx(L, 3, NULL) == glyph_data(n) && lua_tointeger(L, 4) == glyph_scale(n) && lua_tointeger(L, 5) == glyph_x_scale(n) && lua_tointeger(L, 6) == glyph_y_scale(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ /* case 7: */
+ /* (node,font,data,scale,scale,xscale,yscale)*/
+ }
+ }
+ }
+ return -1;
+ OKAY:
+ return glyph_character(n);
+}
+
+static int nodelib_direct_ischar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) != glyph_node) {
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 2;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+ }
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+/*
+ This one is kind of special and is a way to quickly test what we are at now and what is
+ coming. It saves some extra calls but has a rather hybrid set of return values, depending
+ on the situation:
+
+ \starttyping
+ isnextchar(n,[font],[data],[state],[scale,xscale,yscale])
+ isprevchar(n,[font],[data],[state],[scale,xscale,yscale])
+
+ glyph : nil | next false | next char | next char nextchar
+ otherwise : nil | next false id
+ \stoptyping
+
+ Beware: it is not always measurable faster than multiple calls but it can make code look a
+ bit better (at least in \CONTEXT\ where we can use it a few times). There are more such
+ hybrid helpers where the return value depends on the node type.
+
+ The second glyph is okay when the most meaningful properties are the same. We assume that
+ states can differ so we don't check for that. One of the few assumptions when using
+ \CONTEXT.
+
+*/
+
+inline static int nodelib_aux_similar_glyph(halfword first, halfword second)
+{
+ return
+ node_type(second) == glyph_node
+ && glyph_font(second) == glyph_font(first)
+ && glyph_data(second) == glyph_data(first)
+ /* && glyph_state(second) == glyph_state(first) */
+ && glyph_scale(second) == glyph_scale(first)
+ && glyph_x_scale(second) == glyph_x_scale(first)
+ && glyph_y_scale(second) == glyph_y_scale(first)
+ ;
+}
+
+static int nodelib_direct_isnextchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* beware, don't mix push and pop */
+ halfword nxt = node_next(n);
+ if (node_type(n) != glyph_node) {
+ nodelib_push_direct_or_nil(L, nxt);
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 3;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ nodelib_push_direct_or_nil(L, nxt);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ if (nxt && nodelib_aux_similar_glyph(n, nxt)) {
+ lua_pushinteger(L, glyph_character(nxt));
+ return 3;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 2;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isprevchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* beware, don't mix push and pop */
+ halfword prv = node_prev(n);
+ if (node_type(n) != glyph_node) {
+ nodelib_push_direct_or_nil(L, prv);
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 3;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ nodelib_push_direct_or_nil(L, prv);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ if (prv && nodelib_aux_similar_glyph(n, prv)) {
+ lua_pushinteger(L, glyph_character(prv));
+ return 3;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 2;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ } else {
+ lua_pushnil(L); /* no glyph at all */
+ lua_pushnil(L); /* no glyph at all */
+ }
+ return 2;
+}
+
+static int nodelib_direct_isnextglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isprevglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+
+/* direct.usesfont */
+
+inline static int nodelib_aux_uses_font_disc(lua_State *L, halfword n, halfword font)
+{
+ while (n) {
+ if ((node_type(n) == glyph_node) && (glyph_font(n) == font)) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ n = node_next(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_usesfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword f = lmt_tohalfword(L, 2);
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushboolean(L, glyph_font(n) == f);
+ return 1;
+ case disc_node:
+ if (nodelib_aux_uses_font_disc(L, disc_pre_break_head(n), f)) {
+ return 1;
+ } else if (nodelib_aux_uses_font_disc(L, disc_post_break_head(n), f)) {
+ return 1;
+ } else if (nodelib_aux_uses_font_disc(L, disc_no_break_head(n), f)) {
+ return 1;
+ }
+ /*
+ {
+ halfword c = disc_pre_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ c = disc_post_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ c = disc_no_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ }
+ */
+ break;
+ /* todo: other node types */
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+/* boxes */
+
+/* node.getbox = tex.getbox */
+/* node.setbox = tex.setbox */
+
+/* node.direct.getbox */
+/* node.direct.setbox */
+
+static int nodelib_direct_getbox(lua_State *L)
+{
+ int id = lmt_get_box_id(L, 1, 1);
+ if (id >= 0) {
+ int t = tex_get_tex_box_register(id, 0);
+ if (t) {
+ lua_pushinteger(L, t);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setbox(lua_State *L)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int id = lmt_get_box_id(L, slot++, 1);
+ if (id >= 0) {
+ int n;
+ switch (lua_type(L, slot)) {
+ case LUA_TBOOLEAN:
+ {
+ n = lua_toboolean(L, slot);
+ if (n == 0) {
+ n = null;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ case LUA_TNIL:
+ n = null;
+ break;
+ default:
+ {
+ n = nodelib_valid_direct_from_index(L, slot);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ break;
+ default:
+ /*tex Alternatively we could |hpack|. */
+ return luaL_error(L, "setbox: incompatible node type (%s)\n",get_node_name(node_type(n)));
+ }
+ }
+ }
+ break;
+ }
+ tex_set_tex_box_register(id, n, flags, 0);
+ }
+ return 0;
+}
+
+/* node.isnode(n) */
+
+static int nodelib_userdata_isnode(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ lua_pushinteger (L, n);
+ } else {
+ lua_pushboolean (L, 0);
+ }
+ return 1;
+}
+
+/* node.direct.isdirect(n) (handy for mixed usage testing) */
+
+static int nodelib_direct_isdirect(lua_State *L)
+{
+ if (lua_type(L, 1) != LUA_TNUMBER) {
+ lua_pushboolean(L, 0); /* maybe valid test too */
+ }
+ /* else return direct */
+ return 1;
+}
+
+/* node.direct.isnode(n) (handy for mixed usage testing) */
+
+static int nodelib_direct_isnode(lua_State *L)
+{
+ if (! lmt_maybe_isnode(L, 1)) {
+ lua_pushboolean(L, 0);
+ } else {
+ /*tex Assume and return node. */
+ }
+ return 1;
+}
+
+/*tex Maybe we should allocate a proper index |0 .. var_mem_max| but not now. */
+
+static int nodelib_userdata_getproperty(lua_State *L)
+{ /* <node> */
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lua_rawgeti(L, -1, n); /* actually it is a hash */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_getproperty(lua_State *L)
+{ /* <direct> */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lua_rawgeti(L, -1, n); /* actually it is a hash */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_userdata_setproperty(lua_State *L)
+{
+ /* <node> <value> */
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_settop(L, 2);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <node> <value> <propertytable> */
+ lua_replace(L, -3);
+ /* <propertytable> <value> */
+ lua_rawseti(L, -2, n); /* actually it is a hash */
+ }
+ return 0;
+}
+
+static int nodelib_direct_setproperty(lua_State *L)
+{
+ /* <direct> <value> */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_settop(L, 2);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <node> <value> <propertytable> */
+ lua_replace(L, 1);
+ /* <propertytable> <value> */
+ lua_rawseti(L, 1, n); /* actually it is a hash */
+ }
+ return 0;
+}
+
+/*tex
+
+ These two getters are kind of tricky as they can mess up the otherwise hidden table. But
+ normally these are under control of the macro package so we can control it somewhat.
+
+*/
+
+static int nodelib_direct_getpropertiestable(lua_State *L)
+{ /* <node|direct> */
+ if (lua_toboolean(L, lua_gettop(L))) {
+ /*tex Beware: this can have side effects when used without care. */
+ lmt_initialize_properties(1);
+ }
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ return 1;
+}
+
+static int nodelib_userdata_getpropertiestable(lua_State *L)
+{ /* <node|direct> */
+ lua_get_metatablelua(node_properties_indirect);
+ return 1;
+}
+
+/* extra helpers */
+
+static void nodelib_direct_effect_done(lua_State *L, halfword amount, halfword stretch, halfword shrink, halfword stretch_order, halfword shrink_order)
+{
+ halfword parent = nodelib_valid_direct_from_index(L, 2);
+ if (parent) {
+ halfword sign = box_glue_sign(parent);
+ if (sign != normal_glue_sign) {
+ switch (node_type(parent)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ double w = (double) amount;
+ switch (sign) {
+ case stretching_glue_sign:
+ if (stretch_order == box_glue_order(parent)) {
+ w += stretch * (double) box_glue_set(parent);
+ }
+ break;
+ case shrinking_glue_sign:
+ if (shrink_order == box_glue_order(parent)) {
+ w -= shrink * (double) box_glue_set(parent);
+ }
+ break;
+ }
+ if (lua_toboolean(L, 3)) {
+ lua_pushinteger(L, lmt_roundedfloat(w));
+ } else {
+ lua_pushnumber(L, w);
+ }
+ return;
+ }
+ }
+ }
+ }
+ lua_pushinteger(L, amount);
+}
+
+static int nodelib_direct_effectiveglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ nodelib_direct_effect_done(L, glue_amount(n), glue_stretch(n), glue_shrink(n),glue_stretch_order(n), glue_shrink_order(n));
+ break;
+ case math_node:
+ if (math_surround(n)) {
+ lua_pushinteger(L, math_surround(n));
+ } else {
+ nodelib_direct_effect_done(L, math_amount(n), math_stretch(n), math_shrink(n), math_stretch_order(n), math_shrink_order(n));
+ }
+ break;
+ default:
+ lua_pushinteger(L, 0);
+ break;
+ }
+ } else {
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+/*tex
+
+ Disc nodes are kind of special in the sense that their head is not the head as we see it, but
+ a special node that has status info of which head and tail are part. Normally when proper
+ set/get functions are used this status node is all right but if a macro package permits
+ arbitrary messing around, then it can at some point call the following cleaner, just before
+ linebreaking kicks in. This one is not called automatically because if significantly slows down
+ the line break routing.
+
+*/
+
+static int nodelib_direct_checkdiscretionaries(lua_State *L) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (n) {
+ if (node_type(n) == disc_node) {
+ tex_check_disc_field(n);
+ }
+ n = node_next(n) ;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_checkdiscretionary(lua_State *L) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ halfword p = disc_pre_break_head(n);
+ disc_pre_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ p = disc_post_break_head(n);
+ disc_post_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ p = disc_no_break_head(n);
+ disc_no_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ }
+ return 0;
+}
+
+static int nodelib_direct_flattendiscretionaries(lua_State *L)
+{
+ int count = 0;
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ head = tex_flatten_discretionaries(head, &count, lua_toboolean(L, 2)); /* nest */
+ } else {
+ head = null;
+ }
+ nodelib_push_direct_or_nil(L, head);
+ lua_pushinteger(L, count);
+ return 2;
+}
+
+static int nodelib_direct_softenhyphens(lua_State *L)
+{
+ int found = 0;
+ int replaced = 0;
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ tex_soften_hyphens(head, &found, &replaced);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ lua_pushinteger(L, found);
+ lua_pushinteger(L, replaced);
+ return 3;
+}
+
+/*tex The fields related to input tracking: */
+
+static int nodelib_direct_setinputfields(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* there is no need to test for tag and line as two arguments are mandate */
+ halfword tag = lmt_tohalfword(L, 2);
+ halfword line = lmt_tohalfword(L, 3);
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_input_file(n) = tag;
+ glyph_input_line(n) = line;
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_input_file(n) = tag;
+ box_input_line(n) = line;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getinputfields(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_input_file(n));
+ lua_pushinteger(L, glyph_input_line(n));
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_input_file(n));
+ lua_pushinteger(L, box_input_line(n));
+ break;
+ default:
+ return 0;
+ }
+ return 2;
+ }
+ return 0;
+}
+
+static int nodelib_direct_makeextensible(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 3) {
+ halfword fnt = lmt_tohalfword(L, 1);
+ halfword chr = lmt_tohalfword(L, 2);
+ halfword target = lmt_tohalfword(L, 3);
+ halfword size = lmt_opthalfword(L, 4, 0);
+ halfword overlap = lmt_opthalfword(L, 5, 65536);
+ halfword attlist = null;
+ halfword b = null;
+ int horizontal = 0;
+ if (top >= 4) {
+ overlap = lmt_tohalfword(L, 4);
+ if (top >= 5) {
+ horizontal = lua_toboolean(L, 5);
+ if (top >= 6) {
+ attlist = nodelib_valid_direct_from_index(L, 6);
+ }
+ }
+ }
+ b = tex_make_extensible(fnt, chr, target, overlap, horizontal, attlist, size);
+ lua_pushinteger(L, b);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/*tex experiment */
+
+static int nodelib_direct_flattenleaders(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ int count = 0;
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ tex_flatten_leaders(n, &count);
+ break;
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+/*tex test */
+
+static int nodelib_direct_isvalid(lua_State *L)
+{
+ lua_pushboolean(L, nodelib_valid_direct_from_index(L, 1));
+ return 1;
+}
+
+/* getlinestuff : LS RS LH RH ID PF FIRST LAST */
+
+inline static halfword set_effective_width(halfword source, halfword sign, halfword order, double glue)
+{
+ halfword amount = glue_amount(source);
+ switch (sign) {
+ case stretching_glue_sign:
+ if (glue_stretch_order(source) == order) {
+ return amount + scaledround((double) glue_stretch(source) * glue);
+ } else {
+ break;
+ }
+ case shrinking_glue_sign:
+ if (glue_shrink_order(source) == order) {
+ return amount + scaledround((double) glue_shrink(source) * glue);
+ } else {
+ break;
+ }
+ }
+ return amount;
+}
+
+static int nodelib_direct_getnormalizedline(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == hlist_node && node_subtype(n) == line_list) {
+ halfword head = box_list(n);
+ halfword tail = head;
+ halfword first = head;
+ halfword last = tail;
+ halfword current = head;
+ halfword ls = 0;
+ halfword rs = 0;
+ halfword is = 0;
+ halfword pr = 0;
+ halfword pl = 0;
+ halfword ir = 0;
+ halfword il = 0;
+ halfword lh = 0;
+ halfword rh = 0;
+ halfword sign = box_glue_sign(n);
+ halfword order = box_glue_order(n);
+ double glue = box_glue_set(n);
+ while (current) {
+ tail = current ;
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case left_skip_glue : ls = set_effective_width(current, sign, order, glue); break;
+ case right_skip_glue : rs = set_effective_width(current, sign, order, glue); break;
+ case par_fill_left_skip_glue : pl = set_effective_width(current, sign, order, glue); break;
+ case par_fill_right_skip_glue : pr = set_effective_width(current, sign, order, glue); break;
+ case par_init_left_skip_glue : il = set_effective_width(current, sign, order, glue); break;
+ case par_init_right_skip_glue : ir = set_effective_width(current, sign, order, glue); break;
+ case indent_skip_glue : is = set_effective_width(current, sign, order, glue); break;
+ case left_hang_skip_glue : lh = set_effective_width(current, sign, order, glue); break;
+ case right_hang_skip_glue : rh = set_effective_width(current, sign, order, glue); break;
+ }
+ }
+ current = node_next(current);
+ }
+ current = head;
+ while (current) {
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case left_skip_glue:
+ case par_fill_left_skip_glue:
+ case par_init_left_skip_glue:
+ case indent_skip_glue:
+ case left_hang_skip_glue:
+ first = current;
+ current = node_next(current);
+ break;
+ default:
+ current = null;
+ break;
+ }
+ } else {
+ current = null;
+ }
+ }
+ current = tail;
+ while (current) {
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case right_skip_glue:
+ case par_fill_right_skip_glue:
+ case par_init_right_skip_glue:
+ case right_hang_skip_glue:
+ last = current;
+ current = node_prev(current);
+ break;
+ default:
+ current = null;
+ break;
+ }
+ } else {
+ current = null;
+ }
+ }
+ lua_createtable(L, 0, 14); /* we could add some more */
+ lua_push_integer_at_key(L, leftskip, ls);
+ lua_push_integer_at_key(L, rightskip, rs);
+ lua_push_integer_at_key(L, lefthangskip, lh);
+ lua_push_integer_at_key(L, righthangskip, rh);
+ lua_push_integer_at_key(L, indent, is);
+ lua_push_integer_at_key(L, parfillleftskip, pl);
+ lua_push_integer_at_key(L, parfillrightskip, pr);
+ lua_push_integer_at_key(L, parinitleftskip, il);
+ lua_push_integer_at_key(L, parinitrightskip, ir);
+ lua_push_integer_at_key(L, first, first); /* points to a skip */
+ lua_push_integer_at_key(L, last, last); /* points to a skip */
+ lua_push_integer_at_key(L, head, head);
+ lua_push_integer_at_key(L, tail, tail);
+ // lua_push_integer_at_key(L, width, box_width(n));
+ return 1;
+ }
+ return 0;
+}
+
+/*tex new */
+
+static int nodelib_direct_ignoremathskip(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == math_node) {
+ lua_pushboolean(L, tex_ignore_math_skip(n));
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+static int nodelib_direct_reverse(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_reversed_node_list(n);
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+static int nodelib_direct_exchange(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ if (first) {
+ halfword second = nodelib_valid_direct_from_index(L, 3);
+ if (! second) {
+ second = node_next(first);
+ }
+ if (second) {
+ halfword pf = node_prev(first);
+ halfword ns = node_next(second);
+ if (first == head) {
+ head = second;
+ } else if (second == head) {
+ head = first;
+ }
+ if (second == node_next(first)) {
+ node_prev(first) = second;
+ node_next(second) = first;
+ } else {
+ halfword nf = node_next(first);
+ halfword ps = node_prev(second);
+ node_prev(first) = ps;
+ if (ps) {
+ node_next(ps) = first;
+ }
+ node_next(second) = nf;
+ if (nf) {
+ node_prev(nf) = second;
+ }
+ }
+ node_next(first) = ns;
+ node_prev(second) = pf;
+ if (pf) {
+ node_next(pf) = second;
+ }
+ if (ns) {
+ node_prev(ns) = first;
+ }
+ }
+ }
+ }
+ nodelib_push_direct_or_nil(L, head);
+ return 1;
+}
+
+/*tex experiment */
+
+inline static halfword nodelib_aux_migrate_decouple(halfword head, halfword current, halfword next, halfword *first, halfword *last)
+{
+ halfword prev = node_prev(current);
+ tex_uncouple_node(current);
+ if (current == head) {
+ node_prev(next) = null;
+ head = next;
+ } else {
+ tex_try_couple_nodes(prev, next);
+ }
+ if (*first) {
+ tex_couple_nodes(*last, current);
+ } else {
+ *first = current;
+ }
+ *last = current;
+ return head;
+}
+
+static halfword lmt_direct_migrate_locate(halfword head, halfword *first, halfword *last, int inserts, int marks)
+{
+ halfword current = head;
+ while (current) {
+ halfword next = node_next(current);
+ switch (node_type(current)) {
+ case vlist_node:
+ case hlist_node:
+ {
+ halfword list = box_list(current);
+ if (list) {
+ box_list(current) = lmt_direct_migrate_locate(list, first, last, inserts, marks);
+ }
+ break;
+ }
+ case insert_node:
+ {
+ if (inserts) {
+ head = nodelib_aux_migrate_decouple(head, current, next, first, last);
+ halfword list = insert_list(current);
+ if (list) {
+ insert_list(current) = lmt_direct_migrate_locate(list, first, last, inserts, marks);
+ }
+ }
+ break;
+ }
+ case mark_node:
+ {
+ if (marks) {
+ head = nodelib_aux_migrate_decouple(head, current, next, first, last);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ current = next;
+ }
+ return head;
+}
+
+static int nodelib_direct_migrate(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ int inserts = lua_type(L, 3) == LUA_TBOOLEAN ? lua_toboolean(L, 2) : 1;
+ int marks = lua_type(L, 2) == LUA_TBOOLEAN ? lua_toboolean(L, 3) : 1;
+ halfword first = null;
+ halfword last = null;
+ halfword current = head;
+ while (current) {
+ switch (node_type(current)) {
+ case vlist_node:
+ case hlist_node:
+ {
+ halfword list = box_list(current);
+ if (list) {
+ box_list(current) = lmt_direct_migrate_locate(list, &first, &last, inserts, marks);
+ }
+ break;
+ }
+ case insert_node:
+ if (inserts) {
+ halfword list = insert_list(current);
+ if (list) {
+ insert_list(current) = lmt_direct_migrate_locate(list, &first, &last, inserts, marks);
+ }
+ break;
+ }
+ }
+ current = node_next(current);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, first);
+ nodelib_push_direct_or_nil(L, last);
+ return 3;
+ }
+ return 0;
+}
+
+/*tex experiment */
+
+static int nodelib_aux_no_left(halfword n, halfword l, halfword r)
+{
+ if (tex_has_glyph_option(n, (singleword) l)) {
+ return 1;
+ } else {
+ n = node_prev(n);
+ if (n) {
+ if (node_type(n) == disc_node) {
+ n = disc_no_break_tail(n);
+ }
+ if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) r)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_aux_no_right(halfword n, halfword r, halfword l)
+{
+ if (tex_has_glyph_option(n, (singleword) r)) {
+ return 1;
+ } else {
+ n = node_next(n);
+ if (node_type(n) == disc_node) {
+ n = disc_no_break_head(n);
+ }
+ if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) l)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_hasglyphoption(lua_State *L)
+{
+ halfword current = nodelib_valid_direct_from_index(L, 1);
+ int result = 0;
+ if (current && node_type(current) == glyph_node) {
+ int option = lua_tointeger(L, 2);
+ switch (option) {
+ case glyph_option_normal_glyph: // 0x00
+ break;
+ case glyph_option_no_left_ligature: // 0x01
+ result = nodelib_aux_no_left(current, glyph_option_no_left_ligature, glyph_option_no_right_ligature);
+ break;
+ case glyph_option_no_right_ligature: // 0x02
+ result = nodelib_aux_no_right(current, glyph_option_no_right_ligature, glyph_option_no_left_ligature);
+ break;
+ case glyph_option_no_left_kern: // 0x04
+ result = nodelib_aux_no_left(current, glyph_option_no_left_kern, glyph_option_no_right_kern);
+ break;
+ case glyph_option_no_right_kern: // 0x08
+ result = nodelib_aux_no_right(current, glyph_option_no_right_kern, glyph_option_no_left_kern);
+ break;
+ case glyph_option_no_expansion: // 0x10
+ /* some day */
+ break;
+ case glyph_option_no_protrusion: // 0x20
+ /* some day */
+ break;
+ case glyph_option_no_italic_correction:
+ case glyph_option_math_discretionary:
+ case glyph_option_math_italics_too:
+ result = tex_has_glyph_option(current, option);
+ break;
+ }
+ }
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+static int nodelib_direct_getspeciallist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ halfword head = null;
+ halfword tail = null;
+ if (! s) {
+ /* error */
+ } else if (lua_key_eq(s, pageinserthead)) {
+ head = tex_get_special_node_list(page_insert_list_type, &tail);
+ } else if (lua_key_eq(s, contributehead)) {
+ head = tex_get_special_node_list(contribute_list_type, &tail);
+ } else if (lua_key_eq(s, pagehead)) {
+ head = tex_get_special_node_list(page_list_type, &tail);
+ } else if (lua_key_eq(s, temphead)) {
+ head = tex_get_special_node_list(temp_list_type, &tail);
+ } else if (lua_key_eq(s, holdhead)) {
+ head = tex_get_special_node_list(hold_list_type, &tail);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ head = tex_get_special_node_list(post_adjust_list_type, &tail);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ head = tex_get_special_node_list(pre_adjust_list_type, &tail);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ head = tex_get_special_node_list(post_migrate_list_type, &tail);
+ } else if (lua_key_eq(s, premigratehead)) {
+ head = tex_get_special_node_list(pre_migrate_list_type, &tail);
+ } else if (lua_key_eq(s, alignhead)) {
+ head = tex_get_special_node_list(align_list_type, &tail);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ head = tex_get_special_node_list(page_discards_list_type, &tail);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ head = tex_get_special_node_list(split_discards_list_type, &tail);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, tail);
+ return 2;
+}
+
+static int nodelib_direct_isspeciallist(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ int istail = 0;
+ int checked = tex_is_special_node_list(head, &istail);
+ if (checked >= 0) {
+ lua_pushinteger(L, checked);
+ if (istail) {
+ lua_pushboolean(L, 1);
+ return 2;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setspeciallist(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 2);
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ /* error */
+ } else if (lua_key_eq(s, pageinserthead)) {
+ tex_set_special_node_list(page_insert_list_type, head);
+ } else if (lua_key_eq(s, contributehead)) {
+ tex_set_special_node_list(contribute_list_type, head);
+ } else if (lua_key_eq(s, pagehead)) {
+ tex_set_special_node_list(page_list_type, head);
+ } else if (lua_key_eq(s, temphead)) {
+ tex_set_special_node_list(temp_list_type, head);
+ } else if (lua_key_eq(s, holdhead)) {
+ tex_set_special_node_list(hold_list_type, head);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ tex_set_special_node_list(post_adjust_list_type, head);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ tex_set_special_node_list(pre_adjust_list_type, head);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ tex_set_special_node_list(post_migrate_list_type, head);
+ } else if (lua_key_eq(s, premigratehead)) {
+ tex_set_special_node_list(pre_migrate_list_type, head);
+ } else if (lua_key_eq(s, alignhead)) {
+ tex_set_special_node_list(align_list_type, head);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ tex_set_special_node_list(page_discards_list_type, head);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ tex_set_special_node_list(split_discards_list_type, head);
+ }
+ return 0;
+}
+
+/*tex
+ This is just an experiment, so it might go away. Using a list can be a bit faster that traverse
+ (2-4 times) but you only see a difference on very last lists and even then one need some 10K
+ loops to notice it. If that gain is needed, I bet that the document takes a while to process
+ anyway.
+*/
+
+static int nodelib_direct_getnodes(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int i = 0;
+ /* maybe count */
+ lua_newtable(L);
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ int t = lua_tonumber(L, 2);
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ int s = lua_tonumber(L, 3);
+ while (n) {
+ if (node_type(n) == t && node_subtype(n) == s) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ }
+ n = node_next(n);
+ }
+ } else {
+ while (n) {
+ if (node_type(n) == t) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ }
+ n = node_next(n);
+ }
+ }
+ } else {
+ while (n) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ n = node_next(n);
+ }
+ }
+ if (i) {
+ return 1;
+ } else {
+ lua_pop(L, 1);
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/*tex experiment */
+
+static int nodelib_direct_getusedattributes(lua_State* L)
+{
+ lua_newtable(L); /* todo: preallocate */
+ for (int current = lmt_node_memory_state.nodes_data.top; current > lmt_node_memory_state.reserved; current--) {
+ if (lmt_node_memory_state.nodesizes[current] > 0 && (node_type(current) == attribute_node && node_subtype(current) != attribute_list_subtype)) {
+ if (lua_rawgeti(L, -1, attribute_index(current)) == LUA_TTABLE) {
+ lua_pushboolean(L, 1);
+ lua_rawseti(L, -2, attribute_value(current));
+ lua_pop(L, 1);
+ /* not faster: */
+ // if (lua_rawgeti(L, -1, attribute_value(current)) != LUA_TBOOLEAN) {
+ // lua_pushboolean(L, 1);
+ // lua_rawseti(L, -3, attribute_value(current));
+ // }
+ // lua_pop(L, 2);
+ } else {
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushboolean(L, 1);
+ lua_rawseti(L, -2, attribute_value(current));
+ lua_rawseti(L, -2, attribute_index(current));
+ }
+ }
+ }
+ return 1;
+}
+
+static int nodelib_shared_getcachestate(lua_State *L)
+{
+ lua_pushboolean(L, attribute_cache_disabled);
+ return 1;
+}
+
+/*tex done */
+
+static int nodelib_get_property_t(lua_State *L)
+{ /* <table> <node> */
+ halfword n = lmt_check_isnode(L, 2);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <table> <node> <properties> */
+ lua_rawgeti(L, -1, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_set_property_t(lua_State *L)
+{
+ /* <table> <node> <value> */
+ halfword n = lmt_check_isnode(L, 2);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <table> <node> <value> <properties> */
+ lua_insert(L, -2);
+ /* <table> <node> <properties> <value> */
+ lua_rawseti(L, -2, n);
+ }
+ return 0;
+}
+
+/* */
+
+static int nodelib_hybrid_gluetostring(lua_State *L)
+{
+ halfword glue = lua_type(L, 1) == LUA_TNUMBER ? nodelib_valid_direct_from_index(L, 1): lmt_maybe_isnode(L, 1);
+ if (glue) {
+ switch (node_type(glue)) {
+ case glue_node:
+ case glue_spec_node:
+ {
+ int saved_selector = lmt_print_state.selector;
+ char *str = NULL;
+ lmt_print_state.selector = new_string_selector_code;
+ tex_print_spec(glue, pt_unit);
+ str = tex_take_string(NULL);
+ lmt_print_state.selector = saved_selector;
+ lua_pushstring(L, str);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static const struct luaL_Reg nodelib_p[] = {
+ { "__index", nodelib_get_property_t },
+ { "__newindex", nodelib_set_property_t },
+ { NULL, NULL },
+};
+
+void lmt_initialize_properties(int set_size)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (lmt_node_memory_state.node_properties_id) {
+ /*tex
+ We should clean up but for now we accept a leak because these tables are still empty,
+ and when you do this once again you're probably messing up. This should actually be
+ enough:
+ */
+ luaL_unref(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lmt_node_memory_state.node_properties_id = 0;
+ }
+ if (set_size) {
+ tex_engine_get_config_number("propertiessize", &lmt_node_memory_state.node_properties_table_size);
+ if (lmt_node_memory_state.node_properties_table_size < 0) {
+ lmt_node_memory_state.node_properties_table_size = 0;
+ }
+ /*tex It's a hash, not an array because we jump by size. */
+ lua_createtable(L, 0, lmt_node_memory_state.node_properties_table_size);
+ } else {
+ lua_newtable(L);
+ }
+ /* <properties table> */
+ lmt_node_memory_state.node_properties_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ /* not needed, so unofficial */
+ lua_pushstring(L, NODE_PROPERTIES_DIRECT);
+ /* <direct identifier> */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <direct identifier> <properties table> */
+ lua_settable(L, LUA_REGISTRYINDEX);
+ /* */
+ lua_pushstring(L, NODE_PROPERTIES_INDIRECT);
+ /* <indirect identifier> */
+ lua_newtable(L);
+ /* <indirect identifier> <stub table> */
+ luaL_newmetatable(L, NODE_PROPERTIES_INSTANCE);
+ /* <indirect identifier> <stub table> <metatable> */
+ luaL_setfuncs(L, nodelib_p, 0);
+ /* <indirect identifier> <stub table> <metatable> */
+ lua_setmetatable(L, -2);
+ /* <indirect identifier> <stub table> */
+ lua_settable(L, LUA_REGISTRYINDEX);
+ /* */
+}
+
+/* node.direct.* */
+
+static const struct luaL_Reg nodelib_direct_function_list[] = {
+ { "checkdiscretionaries", nodelib_direct_checkdiscretionaries },
+ { "checkdiscretionary", nodelib_direct_checkdiscretionary },
+ { "copy", nodelib_direct_copy },
+ { "copylist", nodelib_direct_copylist },
+ { "copyonly", nodelib_direct_copyonly },
+ { "count", nodelib_direct_count },
+ { "currentattributes", nodelib_direct_currentattributes },
+ { "dimensions", nodelib_direct_dimensions },
+ { "effectiveglue", nodelib_direct_effectiveglue },
+ { "endofmath", nodelib_direct_endofmath },
+ { "findattribute", nodelib_direct_findattribute },
+ { "findattributerange", nodelib_direct_findattributerange },
+ { "findnode", nodelib_direct_findnode },
+ { "firstglyph", nodelib_direct_firstglyph },
+ { "flattendiscretionaries", nodelib_direct_flattendiscretionaries },
+ { "softenhyphens", nodelib_direct_softenhyphens },
+ { "flushlist", nodelib_direct_flushlist },
+ { "flushnode", nodelib_direct_flushnode },
+ { "free", nodelib_direct_free },
+ { "getattribute", nodelib_direct_getattribute },
+ { "getattributes", nodelib_direct_getattributes },
+ { "getpropertiestable", nodelib_direct_getpropertiestable },
+ { "getinputfields", nodelib_direct_getinputfields },
+ { "getattributelist", nodelib_direct_getattributelist },
+ { "getboth", nodelib_direct_getboth },
+ { "getbottom", nodelib_direct_getbottom },
+ { "getbox", nodelib_direct_getbox },
+ { "getchar", nodelib_direct_getchar },
+ { "getchardict", nodelib_direct_getchardict },
+ { "getcharspec", nodelib_direct_getcharspec },
+ { "getchoice", nodelib_direct_getchoice },
+ { "getclass", nodelib_direct_getclass },
+ { "getstate", nodelib_direct_getstate },
+ { "getscript", nodelib_direct_getscript },
+ { "getdata", nodelib_direct_getdata },
+ { "getleftdelimiter", nodelib_direct_getleftdelimiter },
+ { "getrightdelimiter", nodelib_direct_getrightdelimiter },
+ { "getdelimiter", nodelib_direct_getdelimiter },
+ { "getdenominator", nodelib_direct_getdenominator },
+ { "getdegree", nodelib_direct_getdegree },
+ { "getdepth", nodelib_direct_getdepth },
+ { "getdirection", nodelib_direct_getdirection },
+ { "getdisc", nodelib_direct_getdisc },
+ { "getdiscpart", nodelib_direct_getdiscpart },
+ { "getexpansion", nodelib_direct_getexpansion },
+ { "getfam", nodelib_direct_getfam },
+ { "getfield", nodelib_direct_getfield },
+ { "getfont", nodelib_direct_getfont },
+ { "getglue", nodelib_direct_getglue },
+ { "getglyphdata", nodelib_direct_getglyphdata },
+ { "getheight", nodelib_direct_getheight },
+ { "getindex", nodelib_direct_getindex },
+ { "getid", nodelib_direct_getid },
+ { "getkern", nodelib_direct_getkern },
+ { "getlanguage", nodelib_direct_getlanguage },
+ { "getleader", nodelib_direct_getleader },
+ { "getlist", nodelib_direct_getlist },
+ { "getnext", nodelib_direct_getnext },
+ { "getnormalizedline", nodelib_direct_getnormalizedline },
+ { "getnodes", nodelib_direct_getnodes },
+ { "getnucleus", nodelib_direct_getnucleus },
+ { "getnumerator", nodelib_direct_getnumerator },
+ { "getoffsets", nodelib_direct_getoffsets },
+ { "getanchors", nodelib_direct_getanchors },
+ { "gettop", nodelib_direct_gettop },
+ { "getscales", nodelib_direct_getscales },
+ { "getscale", nodelib_direct_getscale },
+ { "getxscale", nodelib_direct_getxscale },
+ { "getyscale", nodelib_direct_getyscale },
+ { "xscaled", nodelib_direct_xscaled },
+ { "yscaled", nodelib_direct_yscaled },
+ { "getxyscales", nodelib_direct_getxyscales },
+ { "getoptions", nodelib_direct_getoptions },
+ { "hasgeometry", nodelib_direct_hasgeometry },
+ { "getgeometry", nodelib_direct_getgeometry },
+ { "setgeometry", nodelib_direct_setgeometry },
+ { "getorientation", nodelib_direct_getorientation },
+ { "getpenalty", nodelib_direct_getpenalty },
+ { "getpost", nodelib_direct_getpost },
+ { "getpre", nodelib_direct_getpre },
+ { "getprev", nodelib_direct_getprev },
+ { "getproperty", nodelib_direct_getproperty },
+ { "getreplace", nodelib_direct_getreplace },
+ { "getshift", nodelib_direct_getshift },
+ { "getsub", nodelib_direct_getsub },
+ { "getsubpre", nodelib_direct_getsubpre },
+ { "getsubtype", nodelib_direct_getsubtype },
+ { "getsup", nodelib_direct_getsup },
+ { "getsuppre", nodelib_direct_getsuppre },
+ { "getprime", nodelib_direct_getprime },
+ { "gettotal" , nodelib_direct_gettotal },
+ { "getwhd", nodelib_direct_getwhd },
+ { "getwidth", nodelib_direct_getwidth },
+ { "getwordrange", nodelib_direct_getwordrange },
+ { "getparstate", nodelib_direct_getparstate },
+ { "hasattribute", nodelib_direct_hasattribute },
+ { "hasdimensions", nodelib_direct_hasdimensions },
+ { "hasfield", nodelib_direct_hasfield },
+ { "hasglyph", nodelib_direct_hasglyph },
+ { "hasglyphoption", nodelib_direct_hasglyphoption },
+ { "hpack", nodelib_direct_hpack },
+ { "hyphenating", nodelib_direct_hyphenating },
+ { "collapsing", nodelib_direct_collapsing }, /*tex A funny name but like |ligaturing| and |hyphenating|. */
+ { "ignoremathskip", nodelib_direct_ignoremathskip },
+ { "insertafter", nodelib_direct_insertafter },
+ { "insertbefore", nodelib_direct_insertbefore },
+ { "appendaftertail", nodelib_direct_appendaftertail },
+ { "prependbeforehead", nodelib_direct_prependbeforehead },
+ { "ischar", nodelib_direct_ischar },
+ { "isnextchar", nodelib_direct_isnextchar },
+ { "isprevchar", nodelib_direct_isprevchar },
+ { "isnextglyph", nodelib_direct_isnextglyph },
+ { "isprevglyph", nodelib_direct_isprevglyph },
+ { "isdirect", nodelib_direct_isdirect },
+ { "isglyph", nodelib_direct_isglyph },
+ { "isnode", nodelib_direct_isnode },
+ { "isvalid", nodelib_direct_isvalid },
+ { "iszeroglue", nodelib_direct_iszeroglue },
+ { "isnext", nodelib_direct_isnext },
+ { "isprev", nodelib_direct_isprev },
+ { "isboth", nodelib_direct_isboth },
+ { "kerning", nodelib_direct_kerning },
+ { "lastnode", nodelib_direct_lastnode },
+ { "length", nodelib_direct_length },
+ { "ligaturing", nodelib_direct_ligaturing },
+ { "makeextensible", nodelib_direct_makeextensible },
+ { "mlisttohlist", nodelib_direct_mlisttohlist },
+ { "naturalwidth", nodelib_direct_naturalwidth },
+ { "naturalhsize", nodelib_direct_naturalhsize },
+ { "new", nodelib_direct_new },
+ { "newtextglyph", nodelib_direct_newtextglyph },
+ { "newmathglyph", nodelib_direct_newmathglyph },
+ { "protectglyph", nodelib_direct_protectglyph },
+ { "protectglyphs", nodelib_direct_protectglyphs },
+ { "protrusionskippable", nodelib_direct_protrusionskipable },
+ { "rangedimensions", nodelib_direct_rangedimensions }, /* maybe get... */
+ { "getglyphdimensions", nodelib_direct_getglyphdimensions },
+ { "getkerndimension", nodelib_direct_getkerndimension },
+ { "patchattributes", nodelib_direct_patchattributes },
+ { "remove", nodelib_direct_remove },
+ { "repack", nodelib_direct_repack },
+ { "freeze", nodelib_direct_freeze },
+ { "setattribute", nodelib_direct_setattribute },
+ { "setattributes", nodelib_direct_setattributes },
+ { "setinputfields", nodelib_direct_setinputfields },
+ { "setattributelist", nodelib_direct_setattributelist },
+ { "setboth", nodelib_direct_setboth },
+ { "setbottom", nodelib_direct_setbottom },
+ { "setbox", nodelib_direct_setbox },
+ { "setchar", nodelib_direct_setchar },
+ { "setchardict", nodelib_direct_setchardict },
+ { "setchoice", nodelib_direct_setchoice },
+ { "setclass", nodelib_direct_setclass },
+ { "setstate", nodelib_direct_setstate },
+ { "setscript", nodelib_direct_setscript },
+ { "setdata", nodelib_direct_setdata },
+ { "setleftdelimiter", nodelib_direct_setleftdelimiter },
+ { "setrightdelimiter", nodelib_direct_setrightdelimiter },
+ { "setdelimiter", nodelib_direct_setdelimiter },
+ { "setdenominator", nodelib_direct_setdenominator },
+ { "setdegree", nodelib_direct_setdegree },
+ { "setdepth", nodelib_direct_setdepth },
+ { "setdirection", nodelib_direct_setdirection },
+ { "setdisc", nodelib_direct_setdisc },
+ { "setdiscpart", nodelib_direct_setdiscpart },
+ { "setexpansion", nodelib_direct_setexpansion },
+ { "setfam", nodelib_direct_setfam },
+ { "setfield", nodelib_direct_setfield },
+ { "setfont", nodelib_direct_setfont },
+ { "setglue", nodelib_direct_setglue },
+ { "setglyphdata", nodelib_direct_setglyphdata },
+ { "setheight", nodelib_direct_setheight },
+ { "setindex", nodelib_direct_setindex },
+ { "setkern", nodelib_direct_setkern },
+ { "setlanguage", nodelib_direct_setlanguage },
+ { "setleader", nodelib_direct_setleader },
+ { "setlink", nodelib_direct_setlink },
+ { "setlist", nodelib_direct_setlist },
+ { "setnext", nodelib_direct_setnext },
+ { "setnucleus", nodelib_direct_setnucleus },
+ { "setnumerator", nodelib_direct_setnumerator },
+ { "setoffsets", nodelib_direct_setoffsets },
+ { "addxoffset", nodelib_direct_addxoffset },
+ { "addyoffset", nodelib_direct_addyoffset },
+ { "addmargins", nodelib_direct_addmargins },
+ { "addxymargins", nodelib_direct_addxymargins },
+ { "setscales", nodelib_direct_setscales },
+ { "setanchors", nodelib_direct_setanchors },
+ { "setorientation", nodelib_direct_setorientation },
+ { "setoptions", nodelib_direct_setoptions },
+ { "setpenalty", nodelib_direct_setpenalty },
+ { "setpost", nodelib_direct_setpost },
+ { "setpre", nodelib_direct_setpre },
+ { "setprev", nodelib_direct_setprev },
+ { "setproperty", nodelib_direct_setproperty },
+ { "setreplace", nodelib_direct_setreplace },
+ { "setshift", nodelib_direct_setshift },
+ { "setsplit", nodelib_direct_setsplit },
+ { "setsub", nodelib_direct_setsub },
+ { "setsubpre", nodelib_direct_setsubpre },
+ { "setsubtype", nodelib_direct_setsubtype },
+ { "setsup", nodelib_direct_setsup },
+ { "setsuppre", nodelib_direct_setsuppre },
+ { "setprime" , nodelib_direct_setprime },
+ { "settotal" , nodelib_direct_settotal },
+ { "settop" , nodelib_direct_settop },
+ { "setwhd", nodelib_direct_setwhd },
+ { "setwidth", nodelib_direct_setwidth },
+ { "slide", nodelib_direct_slide },
+ { "startofpar", nodelib_direct_startofpar },
+ { "tail", nodelib_direct_tail },
+ { "todirect", nodelib_direct_todirect },
+ { "tonode", nodelib_direct_tonode },
+ { "tostring", nodelib_direct_tostring },
+ { "tovaliddirect", nodelib_direct_tovaliddirect },
+ { "traverse", nodelib_direct_traverse },
+ { "traversechar", nodelib_direct_traversechar },
+ { "traverseglyph", nodelib_direct_traverseglyph },
+ { "traverseid", nodelib_direct_traverseid },
+ { "traverselist", nodelib_direct_traverselist },
+ { "traversecontent", nodelib_direct_traversecontent },
+ { "traverseleader", nodelib_direct_traverseleader },
+ { "unprotectglyph", nodelib_direct_unprotectglyph },
+ { "unprotectglyphs", nodelib_direct_unprotectglyphs },
+ { "unsetattribute", nodelib_direct_unsetattribute },
+ { "unsetattributes", nodelib_direct_unsetattributes },
+ { "usedlist", nodelib_direct_usedlist },
+ { "usesfont", nodelib_direct_usesfont },
+ { "vpack", nodelib_direct_vpack },
+ { "flattenleaders", nodelib_direct_flattenleaders },
+ { "write", nodelib_direct_write },
+ /* { "appendtocurrentlist", nodelib_direct_appendtocurrentlist }, */ /* beware, we conflict in ctx */
+ { "verticalbreak", nodelib_direct_verticalbreak },
+ { "reverse", nodelib_direct_reverse },
+ { "exchange", nodelib_direct_exchange },
+ { "migrate", nodelib_direct_migrate },
+ { "getspeciallist", nodelib_direct_getspeciallist },
+ { "setspeciallist", nodelib_direct_setspeciallist },
+ { "isspeciallist", nodelib_direct_isspeciallist },
+ { "getusedattributes", nodelib_direct_getusedattributes },
+ /* dual node and direct */
+ { "type", nodelib_hybrid_type },
+ { "types", nodelib_shared_types },
+ { "fields", nodelib_shared_fields },
+ { "subtypes", nodelib_shared_subtypes },
+ { "values", nodelib_shared_values },
+ { "id", nodelib_shared_id },
+ { "show", nodelib_direct_show },
+ { "gluetostring", nodelib_hybrid_gluetostring },
+ { "serialized", nodelib_direct_serialized },
+ { "getcachestate", nodelib_shared_getcachestate },
+ { NULL, NULL },
+};
+
+/* node.* */
+
+static const struct luaL_Reg nodelib_function_list[] = {
+ /* the bare minimum for reasonable performance */
+ { "copy", nodelib_userdata_copy },
+ { "copylist", nodelib_userdata_copylist },
+ { "new", nodelib_userdata_new },
+ { "flushlist", nodelib_userdata_flushlist },
+ { "flushnode", nodelib_userdata_flushnode },
+ { "free", nodelib_userdata_free },
+ { "currentattributes", nodelib_userdata_currentattributes },
+ { "hasattribute", nodelib_userdata_hasattribute },
+ { "getattribute", nodelib_userdata_getattribute },
+ { "setattribute", nodelib_userdata_setattribute },
+ { "unsetattribute", nodelib_userdata_unsetattribute },
+ { "getpropertiestable", nodelib_userdata_getpropertiestable },
+ { "getproperty", nodelib_userdata_getproperty },
+ { "setproperty", nodelib_userdata_setproperty },
+ { "getfield", nodelib_userdata_getfield },
+ { "setfield", nodelib_userdata_setfield },
+ { "hasfield", nodelib_userdata_hasfield },
+ { "tail", nodelib_userdata_tail },
+ { "write", nodelib_userdata_write },
+ /* { "appendtocurrentlist", nodelib_userdata_append }, */ /* beware, we conflict in ctx */
+ { "isnode", nodelib_userdata_isnode },
+ { "tostring", nodelib_userdata_tostring },
+ { "usedlist", nodelib_userdata_usedlist },
+ { "inuse", nodelib_userdata_inuse },
+ { "instock", nodelib_userdata_instock },
+ { "traverse", nodelib_userdata_traverse },
+ { "traverseid", nodelib_userdata_traverse_id },
+ { "insertafter", nodelib_userdata_insertafter },
+ { "insertbefore", nodelib_userdata_insertbefore },
+ { "remove", nodelib_userdata_remove },
+ /* shared between userdata and direct */
+ { "type", nodelib_hybrid_type },
+ { "types", nodelib_shared_types },
+ { "fields", nodelib_shared_fields },
+ { "subtypes", nodelib_shared_subtypes },
+ { "values", nodelib_shared_values },
+ { "id", nodelib_shared_id },
+ { "show", nodelib_userdata_show },
+ { "gluetostring", nodelib_hybrid_gluetostring },
+ { "serialized", nodelib_userdata_serialized },
+ { "getcachestate", nodelib_shared_getcachestate },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg nodelib_metatable[] = {
+ { "__index", nodelib_userdata_index },
+ { "__newindex", nodelib_userdata_newindex },
+ { "__tostring", nodelib_userdata_tostring },
+ { "__eq", nodelib_userdata_equal },
+ { NULL, NULL },
+};
+
+int luaopen_node(lua_State *L)
+{
+ /*tex the main metatable of node userdata */
+ luaL_newmetatable(L, NODE_METATABLE_INSTANCE);
+ /* node.* */
+ luaL_setfuncs(L, nodelib_metatable, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, nodelib_function_list, 0);
+ /* node.direct */
+ lua_pushstring(L, lua_key(direct));
+ lua_newtable(L);
+ luaL_setfuncs(L, nodelib_direct_function_list, 0);
+ lua_rawset(L, -3);
+ return 1;
+}
+
+void lmt_node_list_to_lua(lua_State *L, halfword n)
+{
+ lmt_push_node_fast(L, n);
+}
+
+halfword lmt_node_list_from_lua(lua_State *L, int n)
+{
+ if (lua_isnil(L, n)) {
+ return null;
+ } else {
+ halfword list = lmt_check_isnode(L, n);
+ return list ? list : null;
+ }
+}
+
+/*tex
+ Here come the callbacks that deal with node lists. Some are called in multiple locations and
+ then get additional information passed concerning the whereabouts.
+
+ The begin paragraph callback first got |cmd| and |chr| but in the end it made more sense to
+ do it like the rest and pass a string. There is no need for more granularity.
+ */
+
+void lmt_begin_paragraph_callback(
+ int invmode,
+ int *indented,
+ int context
+)
+{
+ int callback_id = lmt_callback_defined(begin_paragraph_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lua_pushboolean(L, invmode);
+ lua_pushboolean(L, *indented);
+ lmt_push_par_begin(L, context);
+ i = lmt_callback_call(L, 3, 1, top);
+ /* done */
+ if (i) {
+ lmt_callback_error(L, top, i);
+ }
+ else {
+ *indented = lua_toboolean(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+void lmt_paragraph_context_callback(
+ int context,
+ int *ignore
+)
+{
+ int callback_id = lmt_callback_defined(paragraph_context_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_push_par_context(L, context);
+ i = lmt_callback_call(L, 1, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ }
+ else {
+ *ignore = lua_toboolean(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+void lmt_page_filter_callback(
+ int context,
+ halfword boundary
+)
+{
+ int callback_id = lmt_callback_defined(buildpage_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_push_page_context(L, context);
+ lua_push_halfword(L, boundary);
+ i = lmt_callback_call(L, 2, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+/*tex This one gets |tail| and optionally gets back |head|. */
+
+void lmt_append_line_filter_callback(
+ halfword context,
+ halfword index /* class */
+)
+{
+ if (cur_list.tail) {
+ int callback_id = lmt_callback_defined(append_line_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, node_next(cur_list.head));
+ lmt_node_list_to_lua(L, cur_list.tail);
+ lmt_push_append_line_context(L, context);
+ lua_push_halfword(L, index);
+ i = lmt_callback_call(L, 4, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ int a = lmt_node_list_from_lua(L, -1);
+ node_next(cur_list.head) = a;
+ cur_list.tail = tex_tail_of_node_list(a);
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+}
+
+/*tex
+
+ Eventually the optional fixing of lists will go away because we assume that proper double linked
+ lists get returned. Keep in mind that \TEX\ itself never looks back (we didn't change that bit,
+ at least not until now) so it's only callbacks that suffer from bad |prev| fields.
+
+*/
+
+void lmt_node_filter_callback(
+ int filterid,
+ int extrainfo,
+ halfword head_node,
+ halfword *tail_node
+)
+{
+ if (head_node) {
+ /*tex We start after head (temp). */
+ halfword start_node = node_next(head_node);
+ if (start_node) {
+ int callback_id = lmt_callback_defined(filterid);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ /*tex We make sure we have no prev */
+ node_prev(start_node) = null;
+ /*tex the action */
+ lmt_node_list_to_lua(L, start_node);
+ lmt_push_group_code(L, extrainfo);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ /*tex append to old head */
+ halfword start_done = lmt_node_list_from_lua(L, -1);
+ tex_try_couple_nodes(head_node, start_done);
+ /*tex redundant as we set top anyway */
+ lua_pop(L, 2);
+ /*tex find tail in order to update tail */
+ start_node = node_next(head_node);
+ if (start_node) {
+ /*tex maybe just always slide (harmless and fast) */
+ halfword last_node = node_next(start_node);
+ while (last_node) {
+ start_node = last_node;
+ last_node = node_next(start_node);
+ }
+ /*tex we're at the end now */
+ *tail_node = start_node;
+ } else {
+ /*tex we're already at the end */
+ *tail_node = head_node;
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+/*tex
+ Maybe this one will get extended a bit in due time.
+*/
+
+int lmt_linebreak_callback(
+ int is_broken,
+ halfword head_node,
+ halfword *new_head
+)
+{
+ if (head_node) {
+ halfword start_node = node_next(head_node);
+ if (start_node) {
+ int callback_id = lmt_callback_defined(linebreak_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (callback_id > 0 && lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ int ret = 0;
+ node_prev(start_node) = null;
+ lmt_node_list_to_lua(L, start_node);
+ lua_pushboolean(L, is_broken);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ halfword *p = lua_touserdata(L, -1);
+ if (p) {
+ int a = lmt_node_list_from_lua(L, -1);
+ tex_try_couple_nodes(*new_head, a);
+ ret = 1;
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ return ret;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void lmt_alignment_callback(
+ halfword head_node,
+ halfword context,
+ halfword attr_list,
+ halfword preamble
+)
+{
+ if (head_node || preamble) {
+ int callback_id = lmt_callback_defined(alignment_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_alignment_context(L, context);
+ lmt_node_list_to_lua(L, attr_list);
+ lmt_node_list_to_lua(L, preamble);
+ i = lmt_callback_call(L, 4, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return;
+}
+
+void lmt_local_box_callback(
+ halfword linebox,
+ halfword leftbox,
+ halfword rightbox,
+ halfword middlebox,
+ halfword linenumber,
+ scaled leftskip,
+ scaled rightskip,
+ scaled lefthang,
+ scaled righthang,
+ scaled indentation,
+ scaled parinitleftskip,
+ scaled parinitrightskip,
+ scaled parfillleftskip,
+ scaled parfillrightskip,
+ scaled overshoot
+)
+{
+ if (linebox) {
+ int callback_id = lmt_callback_defined(local_box_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, linebox);
+ lmt_node_list_to_lua(L, leftbox);
+ lmt_node_list_to_lua(L, rightbox);
+ lmt_node_list_to_lua(L, middlebox);
+ lua_pushinteger(L, linenumber);
+ lua_pushinteger(L, leftskip);
+ lua_pushinteger(L, rightskip);
+ lua_pushinteger(L, lefthang);
+ lua_pushinteger(L, righthang);
+ lua_pushinteger(L, indentation);
+ lua_pushinteger(L, parinitleftskip);
+ lua_pushinteger(L, parinitrightskip);
+ lua_pushinteger(L, parfillleftskip);
+ lua_pushinteger(L, parfillrightskip);
+ lua_pushinteger(L, overshoot);
+ i = lmt_callback_call(L, 15, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ /* todo: check if these boxes are still okay (defined) */
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+}
+
+/*tex
+ This one is a bit different from the \LUATEX\ variant. The direction parameter has been dropped
+ and prevdepth correction can be controlled.
+*/
+
+int lmt_append_to_vlist_callback(
+ halfword box,
+ int location,
+ halfword prev_depth,
+ halfword *result,
+ int *next_depth,
+ int *prev_set,
+ int *check_depth
+)
+{
+ if (box) {
+ int callback_id = lmt_callback_defined(append_to_vlist_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, box);
+ lua_push_key_by_index(location);
+ lua_pushinteger(L, (int) prev_depth);
+ i = lmt_callback_call(L, 3, 3, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ switch (lua_type(L, -3)) {
+ case LUA_TUSERDATA:
+ *result = lmt_check_isnode(L, -3);
+ break;
+ case LUA_TNIL:
+ *result = null;
+ break;
+ default:
+ tex_normal_warning("append to vlist callback", "node or nil expected");
+ break;
+ }
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ *next_depth = lmt_roundnumber(L, -2);
+ *prev_set = 1;
+ }
+ if (*result && lua_type(L, -1) == LUA_TBOOLEAN) {
+ *check_depth = lua_toboolean(L, -1);
+ }
+ lmt_callback_wrapup(L, top);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*tex
+ Here we keep the directions although they play no real role in the
+ packing process.
+ */
+
+halfword lmt_hpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ int extrainfo,
+ int pack_direction,
+ halfword attr
+)
+{
+ if (head_node) {
+ int callback_id = lmt_callback_defined(hpack_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ node_prev(head_node) = null;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_group_code(L, extrainfo);
+ lua_pushinteger(L, size);
+ lmt_push_pack_type(L, pack_type);
+ if (pack_direction >= 0) {
+ lua_pushinteger(L, pack_direction);
+ } else {
+ lua_pushnil(L);
+ }
+ /* maybe: (attr && attr != cache_disabled) */
+ lmt_node_list_to_lua(L, attr);
+ i = lmt_callback_call(L, 6, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ head_node = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return head_node;
+}
+
+extern halfword lmt_packed_vbox_filter_callback(
+ halfword box,
+ int extrainfo
+)
+{
+ if (box) {
+ int callback_id = lmt_callback_defined(packed_vbox_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, box);
+ lmt_push_group_code(L, extrainfo);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ box = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return box;
+}
+
+halfword lmt_vpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ scaled maxd,
+ int extrainfo,
+ int pack_direction,
+ halfword attr
+)
+{
+ if (head_node) {
+ int callback_id = lmt_callback_defined(extrainfo == output_group ? pre_output_filter_callback : vpack_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ node_prev(head_node) = null;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_group_code(L, extrainfo);
+ lua_pushinteger(L, size);
+ lmt_push_pack_type(L, pack_type);
+ lua_pushinteger(L, maxd);
+ if (pack_direction >= 0) {
+ lua_pushinteger(L, pack_direction);
+ } else {
+ lua_pushnil(L);
+ }
+ lmt_node_list_to_lua(L, attr);
+ i = lmt_callback_call(L, 7, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ head_node = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return head_node;
+}
diff --git a/source/luametatex/source/lua/lmtnodelib.h b/source/luametatex/source/lua/lmtnodelib.h
new file mode 100644
index 000000000..6894104b2
--- /dev/null
+++ b/source/luametatex/source/lua/lmtnodelib.h
@@ -0,0 +1,114 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LNODELIB_H
+# define LNODELIB_H
+
+extern void lmt_push_node (lua_State *L);
+extern void lmt_push_node_fast (lua_State *L, halfword n);
+extern void lmt_push_directornode (lua_State *L, halfword n, int isdirect);
+extern void lmt_node_list_to_lua (lua_State *L, halfword n);
+extern halfword lmt_node_list_from_lua (lua_State *L, int n);
+extern int lmt_get_math_style (lua_State *L, int n, int dflt);
+extern int lmt_get_math_parameter (lua_State *L, int n, int dflt);
+extern halfword lmt_check_isnode (lua_State *L, int i);
+extern halfword lmt_check_isdirect (lua_State *L, int i);
+extern halfword lmt_check_isdirectornode (lua_State *L, int i, int *isdirect);
+extern void lmt_initialize_properties (int set_size);
+
+extern halfword lmt_hpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ int extrainfo,
+ int d,
+ halfword a
+);
+
+extern halfword lmt_vpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ scaled maxd,
+ int extrainfo,
+ int d,
+ halfword a
+);
+
+extern halfword lmt_packed_vbox_filter_callback(
+ halfword box,
+ int extrainfo
+);
+
+extern void lmt_node_filter_callback(
+ int filterid,
+ int extrainfo,
+ halfword head_node,
+ halfword *tail_node
+);
+
+extern int lmt_linebreak_callback(
+ int is_broken,
+ halfword head_node,
+ halfword *new_head
+);
+
+extern void lmt_alignment_callback(
+ halfword head_node,
+ halfword context,
+ halfword attr_list,
+ halfword preamble
+);
+
+extern void lmt_local_box_callback(
+ halfword linebox,
+ halfword leftbox,
+ halfword rightbox,
+ halfword middlebox,
+ halfword linenumber,
+ scaled leftskip,
+ scaled rightskip,
+ scaled lefthang,
+ scaled righthang,
+ scaled indentation,
+ scaled parinitleftskip,
+ scaled parinitrightskip,
+ scaled parfillleftskip,
+ scaled parfillrightskip,
+ scaled overshoot
+);
+
+extern int lmt_append_to_vlist_callback(
+ halfword box,
+ int location,
+ halfword prev_depth,
+ halfword *result,
+ int *next_depth,
+ int *prev_set,
+ int *check_depth
+);
+
+extern void lmt_begin_paragraph_callback(
+ int invmode,
+ int *indented,
+ int context
+);
+
+extern void lmt_paragraph_context_callback(
+ int context,
+ int *ignore
+);
+
+
+extern void lmt_page_filter_callback(
+ int context,
+ halfword boundary
+);
+
+extern void lmt_append_line_filter_callback(
+ halfword context,
+ halfword index
+);
+
+# endif
diff --git a/source/luametatex/source/lua/lmtstatuslib.c b/source/luametatex/source/lua/lmtstatuslib.c
new file mode 100644
index 000000000..cf665ede2
--- /dev/null
+++ b/source/luametatex/source/lua/lmtstatuslib.c
@@ -0,0 +1,526 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This module has been there from the start and provides some information that doesn't really
+ fit elsewhere. In \LUATEX\ the module got extended ovet time, and in \LUAMETATEX\ most of what
+ is here has been redone, also because we want different statistics.
+
+*/
+
+# include "luametatex.h"
+
+# define STATS_METATABLE "tex.stats"
+
+typedef struct statistic_entry {
+ const char *name;
+ void *value;
+ int type;
+ int padding;
+} statistic_entry;
+
+typedef const char *(*constfunc) (void);
+typedef char *(*charfunc) (void);
+typedef lua_Number (*numfunc) (void);
+typedef int (*intfunc) (void);
+typedef int (*luafunc) (lua_State *L);
+
+static int statslib_callbackstate(lua_State *L)
+{
+ lmt_push_callback_usage(L);
+ return 1;
+}
+
+static int statslib_texstate(lua_State *L)
+{
+ lua_Integer approximate = 0
+ + (lua_Integer) lmt_string_pool_state .string_pool_data .allocated * (lua_Integer) lmt_string_pool_state .string_pool_data .itemsize
+ + (lua_Integer) lmt_string_pool_state .string_body_data .allocated * (lua_Integer) lmt_string_pool_state .string_body_data .itemsize
+ + (lua_Integer) lmt_node_memory_state .nodes_data .allocated * (lua_Integer) lmt_node_memory_state .nodes_data .itemsize
+ + (lua_Integer) lmt_node_memory_state .extra_data .allocated * (lua_Integer) lmt_node_memory_state .extra_data .itemsize
+ + (lua_Integer) lmt_token_memory_state.tokens_data .allocated * (lua_Integer) lmt_token_memory_state.tokens_data .itemsize
+ + (lua_Integer) lmt_fileio_state .io_buffer_data .allocated * (lua_Integer) lmt_fileio_state .io_buffer_data .itemsize
+ + (lua_Integer) lmt_input_state .input_stack_data .allocated * (lua_Integer) lmt_input_state .input_stack_data .itemsize
+ + (lua_Integer) lmt_input_state .in_stack_data .allocated * (lua_Integer) lmt_input_state .in_stack_data .itemsize
+ + (lua_Integer) lmt_nest_state .nest_data .allocated * (lua_Integer) lmt_nest_state .nest_data .itemsize
+ + (lua_Integer) lmt_input_state .parameter_stack_data.allocated * (lua_Integer) lmt_input_state .parameter_stack_data.itemsize
+ + (lua_Integer) lmt_save_state .save_stack_data .allocated * (lua_Integer) lmt_save_state .save_stack_data .itemsize
+ + (lua_Integer) lmt_hash_state .hash_data .allocated * (lua_Integer) lmt_hash_state .hash_data .itemsize
+ + (lua_Integer) lmt_fileio_state .io_buffer_data .allocated * (lua_Integer) lmt_fileio_state .io_buffer_data .itemsize
+ + (lua_Integer) lmt_font_state .font_data .allocated * (lua_Integer) lmt_font_state .font_data .itemsize
+ + (lua_Integer) lmt_language_state .language_data .allocated * (lua_Integer) lmt_language_state .language_data .itemsize
+ + (lua_Integer) lmt_mark_state .mark_data .allocated * (lua_Integer) lmt_mark_state .mark_data .itemsize
+ + (lua_Integer) lmt_insert_state .insert_data .allocated * (lua_Integer) lmt_insert_state .insert_data .itemsize
+ + (lua_Integer) lmt_sparse_state .sparse_data .allocated * (lua_Integer) lmt_sparse_state .sparse_data .itemsize
+ ;
+ lua_createtable(L, 0, 4);
+ lua_set_integer_by_key(L, "approximate", (int) approximate);
+ return 1;
+}
+
+static int statslib_luastate(lua_State *L)
+{
+ lua_createtable(L, 0, 6);
+ lua_set_integer_by_key(L, "functionsize", lmt_lua_state.function_table_size);
+ lua_set_integer_by_key(L, "propertiessize", lmt_node_memory_state.node_properties_table_size);
+ lua_set_integer_by_key(L, "bytecodes", lmt_lua_state.bytecode_max);
+ lua_set_integer_by_key(L, "bytecodebytes", lmt_lua_state.bytecode_bytes);
+ lua_set_integer_by_key(L, "statebytes", lmt_lua_state.used_bytes);
+ lua_set_integer_by_key(L, "statebytesmax", lmt_lua_state.used_bytes_max);
+ return 1;
+}
+
+static int statslib_errorstate(lua_State* L)
+{
+ lua_createtable(L, 0, 3);
+ lua_set_string_by_key(L, "error", lmt_error_state.last_error);
+ lua_set_string_by_key(L, "errorcontext", lmt_error_state.last_error_context);
+ lua_set_string_by_key(L, "luaerror", lmt_error_state.last_lua_error);
+ return 1;
+}
+
+static int statslib_warningstate(lua_State* L)
+{
+ lua_createtable(L, 0, 2);
+ lua_set_string_by_key(L, "warningtag", lmt_error_state.last_warning_tag);
+ lua_set_string_by_key(L, "warning", lmt_error_state.last_warning);
+ return 1;
+}
+
+static int statslib_aux_stats_name_to_id(const char *name, statistic_entry stats[])
+{
+ for (int i = 0; stats[i].name; i++) {
+ if (strcmp (stats[i].name, name) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int statslib_aux_limits_state(lua_State* L, limits_data *data)
+{
+ lua_createtable(L, 0, 4);
+ lua_set_integer_by_key(L, "set", data->size);
+ lua_set_integer_by_key(L, "min", data->minimum);
+ lua_set_integer_by_key(L, "max", data->maximum);
+ lua_set_integer_by_key(L, "top", data->top);
+ return 1;
+}
+
+static int statslib_aux_memory_state(lua_State* L, memory_data *data)
+{
+ lua_createtable(L, 0, 9);
+ lua_set_integer_by_key(L, "set", data->size); /*tex Can |memory_data_unset|. */
+ lua_set_integer_by_key(L, "min", data->minimum);
+ lua_set_integer_by_key(L, "max", data->maximum);
+ lua_set_integer_by_key(L, "mem", data->allocated);
+ lua_set_integer_by_key(L, "all", data->allocated > 0 ? (int) lmt_rounded(((double) data->allocated) * ((double) data->itemsize)) : data->allocated);
+ lua_set_integer_by_key(L, "top", data->top - data->offset);
+ lua_set_integer_by_key(L, "ptr", data->ptr - data->offset);
+ lua_set_integer_by_key(L, "ini", data->initial); /*tex Can |memory_data_unset|. */
+ lua_set_integer_by_key(L, "stp", data->step);
+ // lua_set_integer_by_key(L, "off", data->offset);
+ return 1;
+}
+
+static int statslib_errorlinestate (lua_State* L) { return statslib_aux_limits_state(L, &lmt_error_state .line_limits); }
+static int statslib_halferrorlinestate(lua_State* L) { return statslib_aux_limits_state(L, &lmt_error_state .half_line_limits); }
+static int statslib_expandstate (lua_State* L) { return statslib_aux_limits_state(L, &lmt_expand_state .limits); }
+static int statslib_stringstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_string_pool_state .string_pool_data); }
+static int statslib_poolstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_string_pool_state .string_body_data); }
+static int statslib_lookupstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_hash_state .eqtb_data); }
+static int statslib_hashstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_hash_state .hash_data); }
+static int statslib_nodestate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_node_memory_state .nodes_data); }
+static int statslib_extrastate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_node_memory_state .extra_data); }
+static int statslib_tokenstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_token_memory_state.tokens_data); }
+static int statslib_inputstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_input_state .input_stack_data); }
+static int statslib_filestate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_input_state .in_stack_data); }
+static int statslib_parameterstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_input_state .parameter_stack_data); }
+static int statslib_neststate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_nest_state .nest_data); }
+static int statslib_savestate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_save_state .save_stack_data); }
+static int statslib_bufferstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_fileio_state .io_buffer_data); }
+static int statslib_fontstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_font_state .font_data); }
+static int statslib_languagestate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_language_state .language_data); }
+static int statslib_markstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_mark_state .mark_data); }
+static int statslib_insertstate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_insert_state .insert_data); }
+static int statslib_sparsestate (lua_State* L) { return statslib_aux_memory_state(L, &lmt_sparse_state .sparse_data); }
+
+static int statslib_readstate(lua_State *L)
+{
+ lua_createtable(L, 0, 4);
+ lua_set_string_by_key (L, "filename", tex_current_input_file_name());
+ lua_set_integer_by_key(L, "iocode", lmt_input_state.cur_input.name > io_file_input_code ? io_file_input_code : lmt_input_state.cur_input.name);
+ lua_set_integer_by_key(L, "linenumber", lmt_input_state.input_line);
+ lua_set_integer_by_key(L, "skiplinenumber", lmt_condition_state.skip_line);
+ return 1;
+}
+
+static int statslib_enginestate(lua_State *L)
+{
+ lua_createtable(L, 0, 13);
+ lua_set_string_by_key (L, "logfilename", lmt_fileio_state.log_name);
+ lua_set_string_by_key (L, "banner", lmt_engine_state.luatex_banner);
+ lua_set_string_by_key (L, "luatex_engine", lmt_engine_state.engine_name);
+ lua_set_integer_by_key(L, "luatex_version", lmt_version_state.version);
+ lua_set_integer_by_key(L, "luatex_revision", lmt_version_state.revision);
+ lua_set_string_by_key(L, "luatex_verbose", lmt_version_state.verbose);
+ lua_set_integer_by_key(L, "development_id", lmt_version_state.developmentid);
+ lua_set_string_by_key (L, "copyright", lmt_version_state.copyright);
+ lua_set_integer_by_key(L, "format_id", lmt_version_state.formatid);
+ lua_set_integer_by_key(L, "tex_hash_size", hash_size);
+ lua_set_string_by_key (L, "used_compiler", lmt_version_state.compiler);
+ // lua_set_string_by_key (L, "used_libc", lmt_version_state.libc);
+ lua_set_integer_by_key(L, "run_state", lmt_main_state.run_state);
+ lua_set_boolean_by_key(L, "permit_loadlib", lmt_engine_state.permit_loadlib);
+ return 1;
+}
+
+static int statslib_aux_getstat_indeed(lua_State *L, statistic_entry stats[], int i)
+{
+ switch (stats[i].type) {
+ case 'S':
+ /* string function pointer, no copy */
+ {
+ const char *st = (*(constfunc) stats[i].value)();
+ lua_pushstring(L, st);
+ /* No freeing here! */
+ break;
+ }
+ // case 's':
+ // /* string function pointer, copy */
+ // {
+ // char *st = (*(charfunc) stats[i].value)();
+ // lua_pushstring(L, st);
+ // lmt_memory_free(st);
+ // break;
+ // }
+ // case 'N':
+ // /* number function pointer */
+ // lua_pushnumber(L, (*(numfunc) stats[i].value)());
+ // break;
+ // case 'G':
+ // /* integer function pointer */
+ // lua_pushinteger(L, (*(intfunc) stats[i].value)());
+ // break;
+ case 'g':
+ /* integer pointer */
+ lua_pushinteger(L, *(int *) (stats[i].value));
+ break;
+ case 'c':
+ /* string pointer */
+ lua_pushstring(L, *(const char **) (stats[i].value));
+ break;
+ // case 'n': /* node */
+ // /* node pointer */
+ // if (*(halfword*) (stats[i].value)) {
+ // lmt_push_node_fast(L, *(halfword *) (stats[i].value));
+ // } else {
+ // lua_pushnil(L);
+ // }
+ // break;
+ case 'b':
+ /* boolean integer pointer */
+ lua_pushboolean(L, *(int *) (stats[i].value));
+ break;
+ case 'f':
+ (*(luafunc) stats[i].value)(L);
+ break;
+ default:
+ /* nothing reasonable */
+ lua_pushnil(L);
+ break;
+ }
+ return 1;
+}
+
+static int statslib_aux_getstats_indeed(lua_State *L, statistic_entry stats[])
+{
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ const char *st = lua_tostring(L, -1);
+ int i = statslib_aux_stats_name_to_id(st, stats);
+ if (i >= 0) {
+ return statslib_aux_getstat_indeed(L, stats, i);
+ }
+ }
+ return 0;
+}
+
+static int statslib_getconstants(lua_State *L)
+{
+ lua_createtable(L, 0, 100);
+
+ lua_set_integer_by_key(L, "no_catcode_table", no_catcode_table_preset);
+ lua_set_integer_by_key(L, "default_catcode_table", default_catcode_table_preset);
+
+ lua_set_cardinal_by_key(L, "max_cardinal", max_cardinal);
+ lua_set_cardinal_by_key(L, "min_cardinal", min_cardinal);
+ lua_set_integer_by_key(L, "max_integer", max_integer);
+ lua_set_integer_by_key(L, "min_integer", min_integer);
+ lua_set_integer_by_key(L, "max_dimen", max_dimen);
+ lua_set_integer_by_key(L, "min_dimen", min_dimen);
+ lua_set_integer_by_key(L, "min_data_value", min_data_value);
+ lua_set_integer_by_key(L, "max_data_value", max_data_value);
+ lua_set_integer_by_key(L, "max_half_value", max_half_value);
+
+ lua_set_integer_by_key(L, "max_limited_scale", max_limited_scale);
+
+ lua_set_integer_by_key(L, "one_bp", one_bp);
+
+ lua_set_integer_by_key(L, "infinity", infinity);
+ lua_set_integer_by_key(L, "min_infinity", min_infinity);
+ lua_set_integer_by_key(L, "awful_bad", awful_bad);
+ lua_set_integer_by_key(L, "infinite_bad", infinite_bad);
+ lua_set_integer_by_key(L, "infinite_penalty", infinite_penalty);
+ lua_set_integer_by_key(L, "eject_penalty", eject_penalty);
+ lua_set_integer_by_key(L, "deplorable", deplorable);
+ lua_set_integer_by_key(L, "large_width_excess", large_width_excess);
+ lua_set_integer_by_key(L, "small_stretchability", small_stretchability);
+ lua_set_integer_by_key(L, "decent_criterium", decent_criterium);
+ lua_set_integer_by_key(L, "loose_criterium", loose_criterium);
+
+ lua_set_integer_by_key(L, "default_rule", default_rule);
+ lua_set_integer_by_key(L, "ignore_depth", ignore_depth);
+
+ lua_set_integer_by_key(L, "min_quarterword", min_quarterword);
+ lua_set_integer_by_key(L, "max_quarterword", max_quarterword);
+
+ lua_set_integer_by_key(L, "min_halfword", min_halfword);
+ lua_set_integer_by_key(L, "max_halfword", max_halfword);
+
+ lua_set_integer_by_key(L, "null_flag", null_flag);
+ lua_set_integer_by_key(L, "zero_glue", zero_glue);
+ lua_set_integer_by_key(L, "unity", unity);
+ lua_set_integer_by_key(L, "two", two);
+ lua_set_integer_by_key(L, "null", null);
+ lua_set_integer_by_key(L, "null_font", null_font);
+
+ lua_set_integer_by_key(L, "unused_attribute_value", unused_attribute_value);
+ lua_set_integer_by_key(L, "unused_state_value", unused_state_value);
+ lua_set_integer_by_key(L, "unused_script_value", unused_script_value);
+
+ lua_set_integer_by_key(L, "preset_rule_thickness", preset_rule_thickness);
+ lua_set_integer_by_key(L, "running_rule", null_flag);
+
+ lua_set_integer_by_key(L, "max_char_code", max_char_code);
+ lua_set_integer_by_key(L, "min_space_factor", min_space_factor);
+ lua_set_integer_by_key(L, "max_space_factor", max_space_factor);
+ lua_set_integer_by_key(L, "default_space_factor", default_space_factor);
+ lua_set_integer_by_key(L, "default_tolerance", default_tolerance);
+ lua_set_integer_by_key(L, "default_hangafter", default_hangafter);
+ lua_set_integer_by_key(L, "default_deadcycles", default_deadcycles);
+ lua_set_integer_by_key(L, "default_pre_display_gap", default_pre_display_gap);
+ lua_set_integer_by_key(L, "default_eqno_gap_step", default_eqno_gap_step);
+
+ lua_set_integer_by_key(L, "default_output_box", default_output_box);
+
+ lua_set_integer_by_key(L, "max_n_of_fonts", max_n_of_fonts);
+ lua_set_integer_by_key(L, "max_n_of_bytecodes", max_n_of_bytecodes);
+ lua_set_integer_by_key(L, "max_n_of_math_families", max_n_of_math_families);
+ lua_set_integer_by_key(L, "max_n_of_languages", max_n_of_languages);
+ lua_set_integer_by_key(L, "max_n_of_catcode_tables", max_n_of_catcode_tables);
+ /* lua_set_integer_by_key(L, "max_n_of_hjcode_tables", max_n_of_hjcode_tables); */ /* meaningless */
+ lua_set_integer_by_key(L, "max_n_of_marks", max_n_of_marks);
+
+ lua_set_integer_by_key(L, "max_character_code", max_character_code);
+ lua_set_integer_by_key(L, "max_mark_index", max_mark_index);
+
+ lua_set_integer_by_key(L, "max_toks_register_index", max_toks_register_index);
+ lua_set_integer_by_key(L, "max_box_register_index", max_box_register_index);
+ lua_set_integer_by_key(L, "max_int_register_index", max_int_register_index);
+ lua_set_integer_by_key(L, "max_dimen_register_index", max_dimen_register_index);
+ lua_set_integer_by_key(L, "max_attribute_register_index", max_attribute_register_index);
+ lua_set_integer_by_key(L, "max_glue_register_index", max_glue_register_index);
+ lua_set_integer_by_key(L, "max_mu_glue_register_index", max_mu_glue_register_index);
+
+ lua_set_integer_by_key(L, "max_bytecode_index", max_bytecode_index);
+ lua_set_integer_by_key(L, "max_math_family_index", max_math_family_index);
+ lua_set_integer_by_key(L, "max_math_class_code", max_math_class_code);
+ lua_set_integer_by_key(L, "max_function_reference", max_function_reference);
+ lua_set_integer_by_key(L, "max_category_code", max_category_code);
+
+ lua_set_integer_by_key(L, "max_newline_character", max_newline_character);
+
+ lua_set_integer_by_key(L, "max_size_of_word", max_size_of_word);
+
+ lua_set_integer_by_key(L, "tex_hash_size", hash_size);
+ lua_set_integer_by_key(L, "tex_hash_prime", hash_prime);
+ lua_set_integer_by_key(L, "tex_eqtb_size", eqtb_size);
+
+ lua_set_integer_by_key(L, "math_begin_class", math_begin_class);
+ lua_set_integer_by_key(L, "math_end_class", math_end_class);
+ lua_set_integer_by_key(L, "unused_math_family", unused_math_family);
+ lua_set_integer_by_key(L, "unused_math_style", unused_math_style);
+ lua_set_integer_by_key(L, "assumed_math_control", assumed_math_control);
+
+ lua_set_integer_by_key(L, "undefined_math_parameter", undefined_math_parameter);
+ return 1;
+}
+
+static struct statistic_entry statslib_entries[] = {
+
+ /*tex But these are now collected in tables: */
+
+ { .name = "enginestate", .value = &statslib_enginestate, .type = 'f' },
+ { .name = "errorlinestate", .value = &statslib_errorlinestate, .type = 'f' },
+ { .name = "halferrorlinestate", .value = &statslib_halferrorlinestate, .type = 'f' },
+ { .name = "expandstate", .value = &statslib_expandstate, .type = 'f' },
+ { .name = "stringstate", .value = &statslib_stringstate, .type = 'f' },
+ { .name = "poolstate", .value = &statslib_poolstate, .type = 'f' },
+ { .name = "hashstate", .value = &statslib_hashstate, .type = 'f' },
+ { .name = "lookupstate", .value = &statslib_lookupstate, .type = 'f' },
+ { .name = "nodestate", .value = &statslib_nodestate, .type = 'f' },
+ { .name = "extrastate", .value = &statslib_extrastate, .type = 'f' },
+ { .name = "tokenstate", .value = &statslib_tokenstate, .type = 'f' },
+ { .name = "inputstate", .value = &statslib_inputstate, .type = 'f' },
+ { .name = "filestate", .value = &statslib_filestate, .type = 'f' },
+ { .name = "parameterstate", .value = &statslib_parameterstate, .type = 'f' },
+ { .name = "neststate", .value = &statslib_neststate, .type = 'f' },
+ { .name = "savestate", .value = &statslib_savestate, .type = 'f' },
+ { .name = "bufferstate", .value = &statslib_bufferstate, .type = 'f' },
+ { .name = "texstate", .value = &statslib_texstate, .type = 'f' },
+ { .name = "luastate", .value = &statslib_luastate, .type = 'f' },
+ { .name = "callbackstate", .value = &statslib_callbackstate, .type = 'f' },
+ { .name = "errorstate", .value = &statslib_errorstate, .type = 'f' },
+ { .name = "warningstate", .value = &statslib_warningstate, .type = 'f' },
+ { .name = "readstate", .value = &statslib_readstate, .type = 'f' },
+ { .name = "fontstate", .value = &statslib_fontstate, .type = 'f' },
+ { .name = "languagestate", .value = &statslib_languagestate, .type = 'f' },
+ { .name = "markstate", .value = &statslib_markstate, .type = 'f' },
+ { .name = "insertstate", .value = &statslib_insertstate, .type = 'f' },
+ { .name = "sparsestate", .value = &statslib_sparsestate, .type = 'f' },
+
+ /*tex We keep these as direct accessible keys: */
+
+ { .name = "filename", .value = (void *) &tex_current_input_file_name, .type = 'S' },
+ { .name = "logfilename", .value = (void *) &lmt_fileio_state.log_name, .type = 'c' },
+ { .name = "banner", .value = (void *) &lmt_engine_state.luatex_banner, .type = 'c' },
+ { .name = "luatex_engine", .value = (void *) &lmt_engine_state.engine_name, .type = 'c' },
+ { .name = "luatex_version", .value = (void *) &lmt_version_state.version, .type = 'g' },
+ { .name = "luatex_revision", .value = (void *) &lmt_version_state.revision, .type = 'g' },
+ { .name = "luatex_verbose", .value = (void *) &lmt_version_state.verbose, .type = 'c' },
+ { .name = "copyright", .value = (void *) &lmt_version_state.copyright, .type = 'c' },
+ { .name = "development_id", .value = (void *) &lmt_version_state.developmentid, .type = 'g' },
+ { .name = "format_id", .value = (void *) &lmt_version_state.formatid, .type = 'g' },
+ { .name = "used_compiler", .value = (void *) &lmt_version_state.compiler, .type = 'c' },
+ { .name = "run_state", .value = (void *) &lmt_main_state.run_state, .type = 'g' },
+ { .name = "permit_loadlib", .value = (void *) &lmt_engine_state.permit_loadlib, .type = 'b' },
+
+ { .name = NULL, .value = NULL, .type = 0 },
+};
+
+static struct statistic_entry statslib_entries_only[] = {
+ { .name = "filename", .value = (void *) &tex_current_input_file_name, .type = 'S' },
+ { .name = "banner", .value = (void *) &lmt_engine_state.luatex_banner, .type = 'c' },
+ { .name = "luatex_engine", .value = (void *) &lmt_engine_state.engine_name, .type = 'c' },
+ { .name = "luatex_version", .value = (void *) &lmt_version_state.version, .type = 'g' },
+ { .name = "luatex_revision", .value = (void *) &lmt_version_state.revision, .type = 'g' },
+ { .name = "luatex_verbose", .value = (void *) &lmt_version_state.verbose, .type = 'c' },
+ { .name = "copyright", .value = (void *) &lmt_version_state.copyright, .type = 'c' },
+ { .name = "development_id", .value = (void *) &lmt_version_state.developmentid, .type = 'g' },
+ { .name = "format_id", .value = (void *) &lmt_version_state.formatid, .type = 'g' },
+ { .name = "used_compiler", .value = (void *) &lmt_version_state.compiler, .type = 'c' },
+
+ { .name = NULL, .value = NULL, .type = 0 },
+};
+
+static int statslib_aux_getstats(lua_State *L)
+{
+ return statslib_aux_getstats_indeed(L, statslib_entries);
+}
+
+static int statslib_aux_getstats_only(lua_State *L)
+{
+ return statslib_aux_getstats_indeed(L, statslib_entries_only);
+}
+
+static int statslib_aux_statslist(lua_State *L, statistic_entry stats[])
+{
+ lua_createtable(L, 0, 60);
+ for (int i = 0; stats[i].name; i++) {
+ lua_pushstring(L, stats[i].name);
+ statslib_aux_getstat_indeed(L, stats, i);
+ lua_rawset(L, -3);
+ }
+ return 1;
+}
+
+static int statslib_statslist(lua_State *L)
+{
+ return statslib_aux_statslist(L, statslib_entries);
+}
+
+static int statslib_statslist_only(lua_State *L)
+{
+ return statslib_aux_statslist(L, statslib_entries_only);
+}
+
+static int statslib_resetmessages(lua_State *L)
+{
+ (void) (L);
+ lmt_memory_free(lmt_error_state.last_warning);
+ lmt_memory_free(lmt_error_state.last_warning_tag);
+ lmt_memory_free(lmt_error_state.last_error);
+ lmt_memory_free(lmt_error_state.last_lua_error);
+ lmt_error_state.last_warning = NULL;
+ lmt_error_state.last_warning_tag = NULL;
+ lmt_error_state.last_error = NULL;
+ lmt_error_state.last_lua_error = NULL;
+ return 0;
+}
+
+static const struct luaL_Reg statslib_function_list[] = {
+ { "list", statslib_statslist }, /* for old times sake */
+ { "getconstants", statslib_getconstants },
+ { "resetmessages", statslib_resetmessages },
+
+ { "gettexstate", statslib_texstate },
+ { "getluastate", statslib_luastate },
+ { "geterrorstate", statslib_errorstate },
+ { "getwarningstate", statslib_warningstate },
+ { "getreadstate", statslib_readstate },
+ { "getcallbackstate", statslib_callbackstate },
+
+ { "geterrorlinestate", statslib_errorlinestate },
+ { "gethalferrorlinestate", statslib_halferrorlinestate },
+ { "getexpandstate", statslib_expandstate },
+
+ { "getstringstate", statslib_stringstate },
+ { "getpoolstate", statslib_poolstate },
+ { "gethashstate", statslib_hashstate },
+ { "getlookupstate", statslib_lookupstate },
+ { "getnodestate", statslib_nodestate },
+ { "getextrastate", statslib_extrastate },
+ { "gettokenstate", statslib_tokenstate },
+ { "getinputstate", statslib_inputstate },
+ { "getfilestate", statslib_filestate },
+ { "getparameterstate", statslib_parameterstate },
+ { "getneststate", statslib_neststate },
+ { "getsavestate", statslib_savestate },
+ { "getbufferstate", statslib_bufferstate },
+ { "getfontstate", statslib_fontstate },
+ { "getlanguagestate", statslib_languagestate },
+ { "getmarkstate", statslib_markstate },
+ { "getinsertstate", statslib_insertstate },
+ { "getsparsestate", statslib_sparsestate },
+
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg statslib_function_list_only[] = {
+ { "list", statslib_statslist_only },
+ { NULL, NULL },
+};
+
+int luaopen_status(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, lmt_engine_state.lua_only ? statslib_function_list_only : statslib_function_list, 0);
+ luaL_newmetatable(L, STATS_METATABLE);
+ lua_pushstring(L, "__index");
+ lua_pushcfunction(L, lmt_engine_state.lua_only ? statslib_aux_getstats_only : statslib_aux_getstats);
+ lua_settable(L, -3);
+ lua_setmetatable(L, -2); /*tex meta to itself */
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmttexiolib.c b/source/luametatex/source/lua/lmttexiolib.c
new file mode 100644
index 000000000..f7f7751d8
--- /dev/null
+++ b/source/luametatex/source/lua/lmttexiolib.c
@@ -0,0 +1,307 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This is a small module that deals with logging. We inherit from \TEX\ the dualistic model
+ of console (terminal) and log file. One can write to one of them or both at the same time.
+ We also inherit most of the logic that deals with going to a new line but we don't have the
+ escaping with |^^| any longer: we live in \UNICODE\ times now. Because \TEX\ itself often
+ outputs single characters and/or small strings, the console actually can have some real
+ impact on performance: updating the display, rendering with complex fonts, intercepting
+ \ANSI\ control sequences, scrolling, etc.
+
+*/
+
+# include "luametatex.h"
+
+FILE *lmt_valid_file(lua_State *L) {
+ luaL_Stream *p = (luaL_Stream *) lua_touserdata(L, 1);
+ if (p && lua_getmetatable(L, 1)) {
+ luaL_getmetatable(L, LUA_FILEHANDLE);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ return (p && (p)->closef) ? p->f : NULL;
+ }
+ return NULL;
+}
+
+typedef void (*texio_printer) (const char *);
+
+inline static int texiolib_aux_get_selector_value(lua_State *L, int i, int *l, int dflt)
+{
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, i);
+ if (lua_key_eq(s, logfile)) {
+ *l = logfile_selector_code;
+ } else if (lua_key_eq(s, terminal)) {
+ *l = terminal_selector_code;
+ } else if (lua_key_eq(s, terminal_and_logfile)) {
+ *l = terminal_and_logfile_selector_code;
+ } else {
+ *l = dflt;
+ }
+ return 1;
+ }
+ case LUA_TNUMBER:
+ {
+ int n = lmt_tointeger(L, i);
+ *l = n >= terminal_selector_code && n <= terminal_and_logfile_selector_code ? n : dflt;
+ return 1;
+ }
+ default:
+ return luaL_error(L, "(first) argument is not 'terminal_and_logfile', 'terminal' or 'logfile'");
+ }
+}
+
+static void texiolib_aux_print(lua_State *L, int n, texio_printer printfunction, const char *dflt)
+{
+ int i = 1;
+ int saved_selector = lmt_print_state.selector;
+ if (n > 1 && texiolib_aux_get_selector_value(L, i, &lmt_print_state.selector, terminal_selector_code)) {
+ i++;
+ }
+ switch (lmt_print_state.selector) {
+ case terminal_and_logfile_selector_code:
+ case logfile_selector_code:
+ case terminal_selector_code:
+ if (i <= n) {
+ do {
+ switch (lua_type(L, i)) {
+ case LUA_TNIL:
+ break;
+ case LUA_TBOOLEAN:
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ printfunction(lua_tostring(L, i));
+ break;
+ default:
+ luaL_error(L, "argument is not a string, number or boolean");
+ }
+ i++;
+ } while (i <= n);
+ } else if (dflt) {
+ printfunction(dflt);
+ }
+ break;
+ }
+ lmt_print_state.selector = saved_selector;
+}
+
+static void texiolib_aux_print_selector(lua_State *L, int n, texio_printer printfunction, const char *dflt)
+{
+ int saved_selector = lmt_print_state.selector;
+ texiolib_aux_get_selector_value(L, 1, &lmt_print_state.selector, no_print_selector_code);
+ switch (lmt_print_state.selector) {
+ case terminal_and_logfile_selector_code:
+ case logfile_selector_code:
+ case terminal_selector_code:
+ {
+ if (n > 1) {
+ for (int i = 2; i <= n; i++) {
+ switch (lua_type(L, i)) {
+ case LUA_TNIL:
+ break;
+ case LUA_TBOOLEAN:
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ printfunction(lua_tostring(L, i));
+ break;
+ default:
+ luaL_error(L, "argument is not a string, number or boolean");
+ }
+ };
+ } else if (dflt) {
+ printfunction(dflt);
+ }
+ break;
+ }
+ }
+ lmt_print_state.selector = saved_selector;
+}
+
+static void texiolib_aux_print_stdout(lua_State *L, const char *extra)
+{
+ int i = 1;
+ int l = terminal_and_logfile_selector_code;
+ int n = lua_gettop(L);
+ if (n > 1 && texiolib_aux_get_selector_value(L, i, &l, terminal_selector_code)) {
+ i++;
+ }
+ for (; i <= n; i++) {
+ if (lua_isstring(L, i)) { /* or number */
+ const char *s = lua_tostring(L, i);
+ if (l == terminal_and_logfile_selector_code || l == terminal_selector_code) {
+ fputs(extra, stdout);
+ fputs(s, stdout);
+ }
+ if (l == terminal_and_logfile_selector_code || l == logfile_selector_code) {
+ if (lmt_print_state.loggable_info) {
+ char *v = (char*) lmt_memory_malloc(strlen(lmt_print_state.loggable_info) + strlen(extra) + strlen(s) + 1);
+ if (v) {
+ sprintf(v, "%s%s%s", lmt_print_state.loggable_info, extra, s);
+ }
+ lmt_memory_free(lmt_print_state.loggable_info);
+ lmt_print_state.loggable_info = v;
+ } else {
+ lmt_print_state.loggable_info = lmt_memory_strdup(s);
+ }
+ }
+ }
+ }
+}
+
+static void texiolib_aux_print_nlp_str(const char *s)
+{
+ tex_print_nlp();
+ tex_print_str(s);
+}
+
+static int texiolib_write(lua_State *L)
+{
+ if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
+ texiolib_aux_print_stdout(L, "");
+ } else {
+ int n = lua_gettop(L);
+ if (n > 0) {
+ texiolib_aux_print(L, n, tex_print_str, NULL);
+ } else {
+ /*tex We silently ignore bogus calls. */
+ }
+ }
+ return 0;
+}
+
+
+static int texiolib_write_nl(lua_State *L)
+{
+ if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
+ texiolib_aux_print_stdout(L, "\n");
+ } else {
+ int n = lua_gettop(L);
+ if (n > 0) {
+ texiolib_aux_print(L, n, texiolib_aux_print_nlp_str, "\n");
+ } else {
+ /*tex We silently ignore bogus calls. */
+ }
+ }
+ return 0;
+}
+
+static int texiolib_write_selector(lua_State *L)
+{
+ if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
+ texiolib_aux_print_stdout(L, "");
+ } else {
+ int n = lua_gettop(L);
+ if (n > 1) {
+ texiolib_aux_print_selector(L, n, tex_print_str, NULL);
+ } else {
+ /*tex We silently ignore bogus calls. */
+ }
+ }
+ return 0;
+}
+
+
+static int texiolib_write_selector_nl(lua_State *L)
+{
+ if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
+ texiolib_aux_print_stdout(L, "\n");
+ } else {
+ int n = lua_gettop(L);
+ if (n > 1) {
+ texiolib_aux_print_selector(L, n, texiolib_aux_print_nlp_str, "");
+ } else {
+ /*tex We silently ignore bogus calls. */
+ }
+ }
+ return 0;
+}
+
+static int texiolib_write_selector_lf(lua_State *L)
+{
+ if (lmt_main_state.ready_already == output_disabled_state || ! lmt_fileio_state.job_name) {
+ texiolib_aux_print_stdout(L, "\n");
+ } else {
+ int n = lua_gettop(L);
+ if (n >= 1) {
+ texiolib_aux_print_selector(L, n, texiolib_aux_print_nlp_str, "");
+ } else {
+ /*tex We silently ignore bogus calls. */
+ }
+ }
+ return 0;
+}
+
+/*tex At the point this function is called, the selector is log_only. */
+
+static int texiolib_closeinput(lua_State *L)
+{
+ (void) (L);
+ if (lmt_input_state.cur_input.index > 0) {
+ tex_end_token_list();
+ tex_end_file_reading();
+ }
+ return 0 ;
+}
+
+/*tex
+ This is a private hack, handy for testing runtime math font patches in lfg files with a bit of
+ low level tracing. Setting the logfile is already handles by a callback so we don't support
+ string argument here because we'd end up in that callback which then returns the same logfile
+ name as we already had.
+*/
+
+static int texiolib_setlogfile(lua_State *L)
+{
+ FILE *f = lmt_valid_file(L);
+ if (f) {
+ /* If not writeable then all goes into the void. */
+ if (! lmt_print_state.logfile) {
+ lmt_print_state.saved_logfile = lmt_print_state.logfile;
+ lmt_print_state.saved_logfile_offset = lmt_print_state.logfile_offset;
+
+ }
+ lmt_print_state.logfile = f;
+ lmt_print_state.logfile_offset = 0;
+ } else if (lmt_print_state.logfile) {
+ lmt_print_state.logfile = lmt_print_state.saved_logfile;
+ lmt_print_state.logfile_offset = lmt_print_state.saved_logfile_offset;
+ }
+ return 0;
+}
+
+static const struct luaL_Reg texiolib_function_list[] = {
+ { "write", texiolib_write },
+ { "writenl", texiolib_write_nl },
+ { "write_nl", texiolib_write_nl }, /* depricated */
+ { "writeselector", texiolib_write_selector },
+ { "writeselectornl", texiolib_write_selector_nl },
+ { "writeselectorlf", texiolib_write_selector_lf },
+ { "closeinput", texiolib_closeinput },
+ { "setlogfile", texiolib_setlogfile },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg texiolib_function_list_only[] = {
+ { "write", texiolib_write },
+ { "writenl", texiolib_write_nl },
+ { "write_nl", texiolib_write_nl }, /* depricated */
+ { "writeselector", texiolib_write_selector },
+ { "writeselectornl", texiolib_write_selector_nl },
+ { "writeselectorlf", texiolib_write_selector_lf },
+ { NULL, NULL },
+};
+
+int luaopen_texio(lua_State *L)
+{
+ lua_newtable(L);
+ luaL_setfuncs(L, lmt_engine_state.lua_only ? texiolib_function_list_only : texiolib_function_list, 0);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmttexiolib.h b/source/luametatex/source/lua/lmttexiolib.h
new file mode 100644
index 000000000..e690befa7
--- /dev/null
+++ b/source/luametatex/source/lua/lmttexiolib.h
@@ -0,0 +1,13 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LTEXIOLIB_H
+# define LMT_LTEXIOLIB_H
+
+# include <stdio.h>
+# include <lua.h>
+
+extern FILE *lmt_valid_file (lua_State *L);
+
+# endif
diff --git a/source/luametatex/source/lua/lmttexlib.c b/source/luametatex/source/lua/lmttexlib.c
new file mode 100644
index 000000000..12510206a
--- /dev/null
+++ b/source/luametatex/source/lua/lmttexlib.c
@@ -0,0 +1,5580 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*
+
+ This module deals with access to some if the internal quanities of \TEX, like registers,
+ internal variables and all kind of lists. Because we provide access by name and/or number
+ (index) there is quite a bit of code here, and sometimes if can look a bit confusing.
+
+ The code here differs from \LUATEX\ in the sense that there are some more checks, a bit
+ more abstraction, more access and better performance. What we see here is the result of
+ years of experimenting and usage in \CONTEXT.
+
+ A remark about some of the special node lists that one can query: because some start with
+ a so called |temp| node, we have to set the |prev| link to |nil| because otherwise at the
+ \LUA\ end we expose that |temp| node and users are not suposed to touch them! In the setter
+ no |prev| link is set so we can presume that it's not used later on anyway; this is because
+ original \TEX\ has no |prev| links.
+
+ There is still room for improvement but I'll deal with that when I have a reason (read: when
+ I need something).
+
+*/
+
+# include "luametatex.h"
+
+/*tex
+ Due to the nature of the accessors, this is the module with most metatables. However, we
+ provide getters and setters too. Users can choose what they like most.
+*/
+
+# define TEX_METATABLE_ATTRIBUTE "tex.attribute"
+# define TEX_METATABLE_SKIP "tex.skip"
+# define TEX_METATABLE_GLUE "tex.glue"
+# define TEX_METATABLE_MUSKIP "tex.muskip"
+# define TEX_METATABLE_MUGLUE "tex.muglue"
+# define TEX_METATABLE_DIMEN "tex.dimen"
+# define TEX_METATABLE_COUNT "tex.count"
+# define TEX_METATABLE_TOKS "tex.toks"
+# define TEX_METATABLE_BOX "tex.box"
+# define TEX_METATABLE_SFCODE "tex.sfcode"
+# define TEX_METATABLE_LCCODE "tex.lccode"
+# define TEX_METATABLE_UCCODE "tex.uccode"
+# define TEX_METATABLE_HCCODE "tex.hccode"
+# define TEX_METATABLE_HMCODE "tex.hmcode"
+# define TEX_METATABLE_CATCODE "tex.catcode"
+# define TEX_METATABLE_MATHCODE "tex.mathcode"
+# define TEX_METATABLE_DELCODE "tex.delcode"
+# define TEX_METATABLE_LISTS "tex.lists"
+# define TEX_METATABLE_NEST "tex.nest"
+
+# define TEX_METATABLE_TEX "tex.tex"
+
+# define TEX_NEST_INSTANCE "tex.nest.instance"
+
+/*tex Let's share these. */
+
+static void texlib_aux_show_box_index_error(lua_State *L)
+{
+ luaL_error(L, "invalid index passed, range 0.." LMT_TOSTRING(max_box_register_index) " or name expected");
+}
+
+static void texlib_aux_show_character_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid character value %d passed, range 0.." LMT_TOSTRING(max_character_code), i);
+}
+
+static void texlib_aux_show_catcode_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid catcode %d passed, range 0.." LMT_TOSTRING(max_category_code), i);
+}
+
+static void texlib_aux_show_family_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid family %d passed, range 0.." LMT_TOSTRING(max_math_family_index), i);
+}
+
+static void texlib_aux_show_class_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid class %d passed, range 0.." LMT_TOSTRING(max_math_class_code), i);
+}
+
+static void texlib_aux_show_half_error(lua_State *L, int i)
+{
+ luaL_error(L, "invalid value %d passed, range 0.." LMT_TOSTRING(max_half_value), i);
+}
+
+/*tex
+
+ The rope model dates from the time that we had multiple \LUA\ instances so probably we can
+ simplify it a bit. On the other hand, it works, and also is upgraded for nodes, tokens and some
+ caching, so there is no real need to change anything now. A complication is anyway that input
+ states can nest and any change can mess it up.
+
+ We use a flag to indicate the kind of data that we are dealing with:
+
+ \starttabulate
+ \NC 0 \NC string \NC \NR
+ \NC 1 \NC char \NC \NR
+ \NC 2 \NC token \NC \NR
+ \NC 3 \NC node \NC \NR
+ \stoptabulate
+
+ By treating simple \ASCII\ characters special we prevent mallocs. We also pack small strings if
+ only because we have the room available anyway (due to padding).
+
+ For quite a while we used to have this:
+
+ \starttyping
+ typedef struct spindle_char {
+ unsigned char c1, c2, c3, c4;
+ } spindle_char;
+
+ typedef union spindle_data {
+ spindle_char c;
+ halfword h;
+ } spindle_data;
+ \stopttyping
+
+ The spindle and rope terminology was introduced by Taco early in the development of \LUATEX,
+ but in the meantime the datastructures have been adapted to deal with tokens and nodes. There
+ are also quite some optimizations in performance and memort usage (e.g. for small strings and
+ single characters).
+
+*/
+
+# define FULL_LINE 0
+# define PARTIAL_LINE 1
+# define PACKED_SIZE 8
+# define INITIAL_SIZE 32
+# define MAX_ROPE_CACHE 5000
+
+typedef union spindle_data {
+ unsigned char c[PACKED_SIZE];
+ halfword h;
+ char *t;
+} spindle_data;
+
+typedef struct spindle_rope {
+// char *text;
+ void *next;
+ union {
+ unsigned int tsize;
+ halfword tail;
+ };
+ unsigned char kind;
+ unsigned char partial;
+ short cattable;
+ spindle_data data;
+ /* alignment, not needed when we have c[PACKED_SIZE] */
+ /* int padding; */
+} spindle_rope;
+
+typedef struct spindle {
+ spindle_rope *head;
+ spindle_rope *tail;
+ int complete;
+ /* alignment */
+ int padding;
+} spindle;
+
+typedef struct spindle_state_info {
+ int spindle_size;
+ int spindle_index;
+ spindle *spindles;
+ spindle_rope *rope_cache;
+ int rope_count;
+ /* alignment */
+ int padding;
+} spindle_state_info ;
+
+static spindle_state_info lmt_spindle_state = {
+ .spindle_size = 0,
+ .spindle_index = 0,
+ .spindles = NULL,
+ .rope_cache = NULL,
+ .rope_count = 0,
+ .padding = 0
+};
+
+# define write_spindle lmt_spindle_state.spindles[lmt_spindle_state.spindle_index]
+# define read_spindle lmt_spindle_state.spindles[lmt_spindle_state.spindle_index - 1]
+
+inline static void texlib_aux_reset_spindle(int i)
+{
+ lmt_spindle_state.spindles[i].head = NULL;
+ lmt_spindle_state.spindles[i].tail = NULL;
+ lmt_spindle_state.spindles[i].complete = 0;
+}
+
+/*
+
+ Each rope takes 48 bytes. So, caching some 100 K ropes is not really a problem. In practice we
+ seldom reach that number anyway.
+
+*/
+
+inline static spindle_rope *texlib_aux_new_rope(void)
+{
+ spindle_rope *r;
+ if (lmt_spindle_state.rope_cache) {
+ r = lmt_spindle_state.rope_cache;
+ lmt_spindle_state.rope_cache = r->next;
+ } else {
+ r = (spindle_rope *) lmt_memory_malloc(sizeof(spindle_rope));
+ ++lmt_spindle_state.rope_count;
+ if (r) {
+ r->next = NULL;
+ } else {
+ tex_overflow_error("spindle", sizeof(spindle_rope));
+ }
+ }
+ return r;
+}
+
+inline static void texlib_aux_dispose_rope(spindle_rope *r)
+{
+ if (r) {
+ if (lmt_spindle_state.rope_count > MAX_ROPE_CACHE) {
+ lmt_memory_free(r);
+ --lmt_spindle_state.rope_count;
+ } else {
+ r->next = lmt_spindle_state.rope_cache;
+ lmt_spindle_state.rope_cache = r;
+ }
+ }
+}
+
+static void texlib_aux_initialize(void)
+{
+ lmt_spindle_state.spindles = lmt_memory_malloc(INITIAL_SIZE * sizeof(spindle));
+ if (lmt_spindle_state.spindles) {
+ for (int i = 0; i < INITIAL_SIZE; i++) {
+ texlib_aux_reset_spindle(i);
+ }
+ lmt_spindle_state.spindle_index = 0;
+ lmt_spindle_state.spindle_size = INITIAL_SIZE;
+ } else {
+ tex_overflow_error("spindle", sizeof(spindle));
+ }
+}
+
+/*tex
+ We could convert strings into tokenlists here but conceptually the split is cleaner.
+*/
+
+static int texlib_aux_store(lua_State *L, int i, int partial, int cattable)
+{
+ size_t tsize = 0;
+ spindle_rope *rn = NULL;
+ unsigned char kind = unset_lua_input;
+ spindle_data data = { .h = 0 };
+ switch (lua_type(L, i)) {
+ case LUA_TNUMBER:
+ case LUA_TSTRING:
+ {
+ const char *sttemp = lua_tolstring(L, i, &tsize);
+ if (! partial) {
+ while (tsize > 0 && sttemp[tsize-1] == ' ') {
+ tsize--;
+ }
+ }
+ if (tsize > PACKED_SIZE) {
+ data.t = lmt_memory_malloc(tsize + 1);
+ kind = string_lua_input;
+ if (data.t) {
+ memcpy(data.t, sttemp, (tsize + 1));
+ break;
+ } else {
+ return 0;
+ }
+ } else {
+ /*tex
+ We could append to a previous but partial interferes and in practice it then
+ never can be done.
+ */
+ for (unsigned i = 0; i < tsize; i++) {
+ /*tex When we end up here we often don't have that many bytes. */
+ data.c[i] = (unsigned char) sttemp[i];
+ }
+ kind = packed_lua_input;
+ }
+ }
+ break;
+ case LUA_TUSERDATA:
+ {
+ void *p = lua_touserdata(L, i);
+ if (p && lua_getmetatable(L, i)) {
+ lua_get_metatablelua(token_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ halfword token = (*((lua_token *) p)).token;
+ if (token_link(token)) {
+ /*tex
+ We cannnot pass a list unless we copy it, alternatively we can bump the ref count
+ but a quick test didn't work out that well.
+ */
+ token = token_link(token);
+ if (token) {
+ /*tex
+ We're now past the ref count head. Like below we could actually append to a
+ current rope but so far we seldom end up in here. Maybe I'll do add that later.
+ */
+ halfword t = null;
+ kind = token_list_lua_input;
+ data.h = tex_copy_token_list(token, &t);
+ tsize = t;
+ } else {
+ lua_pop(L, 2);
+ return 0;
+ }
+ } else {
+ /*tex
+ This is a little (mostly memory) optimization. We use token list instead of adding
+ single token ropes. That way we also need to push less back into the input. We
+ check for partial. This optimization is experimental and might go.
+ */
+ if (write_spindle.tail && write_spindle.tail->partial && partial == write_spindle.tail->partial) {
+ switch (write_spindle.tail->kind) {
+ case token_lua_input:
+ /*tex
+ Okay, we now allocate a token but still pushing into the input later has
+ less (nesting) overhead then because we process a sequence.
+ */
+ write_spindle.tail->kind = token_list_lua_input;
+ write_spindle.tail->data.h = tex_store_new_token(null, write_spindle.tail->data.h);
+ write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->data.h, token_info(token));
+ break;
+ case token_list_lua_input:
+ /*tex
+ Normally we have short lists but it still pays off to store the tail
+ in |tsize| instead of locating the tail each time.
+ */
+ write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->tail, token_info(token));
+ break;
+ default:
+ goto SINGLE;
+ }
+ lmt_token_state.luacstrings++; /* already set */
+ write_spindle.complete = 0; /* already set */
+ lua_pop(L, 2);
+ return 1;
+ }
+ /*tex The non-optimized case: */
+ SINGLE:
+ kind = token_lua_input;
+ data.h = token_info(token);
+ }
+ lua_pop(L, 2);
+ } else {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -3)) {
+ kind = node_lua_input;
+ data.h = *((halfword *) p);
+ lua_pop(L, 3);
+ } else {
+ lua_pop(L, 3);
+ return 0;
+ }
+ }
+ } else {
+ return 0;
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+ lmt_token_state.luacstrings++;
+ rn = texlib_aux_new_rope();
+ /* set */
+ rn->tsize = (unsigned) tsize;
+ rn->next = NULL;
+ rn->kind = kind;
+ rn->partial = (unsigned char) partial;
+ rn->cattable = (short) cattable;
+ rn->data = data;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ return 1;
+}
+
+static void texlib_aux_store_token(halfword token, int partial, int cattable)
+{
+ spindle_rope *rn = texlib_aux_new_rope();
+ /* set */
+ rn->tsize = 0;
+ rn->next = NULL;
+ rn->kind = token_lua_input;
+ rn->partial = (unsigned char) partial;
+ rn->cattable = (short) cattable;
+ rn->data.h = token;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ lmt_token_state.luacstrings++;
+}
+
+static void lmx_aux_store_string(char *str, int len, int cattable)
+{
+ spindle_rope *rn = texlib_aux_new_rope();
+ rn->data.h = 0; /* wipes */
+ if (len > PACKED_SIZE) {
+ rn->data.t = lmt_memory_malloc((size_t) len + 1);
+ if (rn->data.t) {
+ memcpy(rn->data.t, str, (size_t) len + 1);
+ } else {
+ len = 0;
+ }
+ rn->kind = string_lua_input;
+ } else {
+ for (int i = 0; i < len; i++) {
+ /* when we end up here we often don't have that many bytes */
+ rn->data.c[i] = (unsigned char) str[i];
+ }
+ rn->kind = packed_lua_input;
+ }
+ /* set */
+ rn->tsize = (unsigned) len;
+ rn->next = NULL;
+ rn->partial = FULL_LINE;
+ rn->cattable = (unsigned char) cattable;
+ /* add */
+ if (write_spindle.head) {
+ write_spindle.tail->next = rn;
+ } else {
+ write_spindle.head = rn;
+ }
+ write_spindle.tail = rn;
+ write_spindle.complete = 0;
+ lmt_token_state.luacstrings++;
+}
+
+static int texlib_aux_cprint(lua_State *L, int partial, int cattable, int startstrings)
+{
+ int n = lua_gettop(L);
+ int t = lua_type(L, startstrings);
+ if (n > startstrings && cattable != no_catcode_table_preset && t == LUA_TNUMBER) {
+ cattable = lmt_tointeger(L, startstrings);
+ ++startstrings;
+ if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
+ cattable = default_catcode_table_preset;
+ }
+ t = lua_type(L, startstrings);
+ }
+ if (t == LUA_TTABLE) {
+ for (int i = 1;; i++) {
+ lua_rawgeti(L, startstrings, i);
+ if (texlib_aux_store(L, -1, partial, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ } else {
+ for (int i = startstrings; i <= n; i++) {
+ texlib_aux_store(L, i, partial, cattable);
+ }
+ }
+ return 0;
+}
+
+/*tex
+ We now feed back to be tokenized input from the \TEX\ end into the same handler as we use for
+ \LUA. It saves code but more important is that we no longer need the pseudo files and lines
+ that are kind of inefficient and depend on variable nodes.
+*/
+
+void lmt_cstring_store(char *str, int len, int cattable)
+{
+ lmx_aux_store_string(str, len, cattable);
+}
+
+void lmt_tstring_store(strnumber s, int cattable)
+{
+ lmx_aux_store_string((char *) str_string(s), (int) str_length(s), cattable);
+}
+
+/*tex
+ This is a bit of a dirty trick, needed for an experiment and it's fast enough for our purpose.
+*/
+
+void lmt_cstring_print(int cattable, const char *s, int ispartial)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = lua_gettop(L);
+ lua_settop(L, 0);
+ lua_pushinteger(L, cattable);
+ lua_pushstring(L, s);
+ texlib_aux_cprint(L, ispartial ? PARTIAL_LINE : FULL_LINE, default_catcode_table_preset, 1);
+ lua_settop(L, top);
+}
+
+/* lua.write */
+
+static int texlib_write(lua_State *L)
+{
+ return texlib_aux_cprint(L, FULL_LINE, no_catcode_table_preset, 1);
+}
+
+/* lua.print */
+
+static int texlib_print(lua_State *L)
+{
+ return texlib_aux_cprint(L, FULL_LINE, default_catcode_table_preset, 1);
+}
+
+/* lua.sprint */
+
+static int texlib_sprint(lua_State *L)
+{
+ return texlib_aux_cprint(L, PARTIAL_LINE, default_catcode_table_preset, 1);
+}
+
+static int texlib_mprint(lua_State *L)
+{
+ int ini = 1;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via (run) macro");
+ }
+ texlib_aux_store_token(token_val(end_local_cmd, 0), PARTIAL_LINE, default_catcode_table_preset);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ int cs = tex_string_locate(name, lname, 0);
+ int cmd = eq_type(cs);
+ if (is_call_cmd(cmd)) {
+ texlib_aux_store_token(cs_token_flag + cs, PARTIAL_LINE, default_catcode_table_preset);
+ ++ini;
+ } else {
+ tex_local_control_message("invalid (mprint) macro");
+ }
+ }
+ if (lua_gettop(L) >= ini) {
+ texlib_aux_cprint(L, PARTIAL_LINE, default_catcode_table_preset, ini);
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via mprint");
+ }
+ tex_local_control(1);
+ return 0;
+}
+
+/* we default to obeymode */
+
+static int texlib_pushlocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("pushing local control");
+ }
+ texlib_aux_store_token(token_val(end_local_cmd, 0), PARTIAL_LINE, default_catcode_table_preset);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ return 0;
+}
+
+static int texlib_poplocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering local control via pop");
+ }
+ tex_local_control(1);
+ return 0;
+}
+
+/* lua.cprint */
+
+static int texlib_cprint(lua_State *L)
+{
+ /*tex
+ We map a catcode to a pseudo cattable. So a negative value is a specific catcode with offset 1.
+ */
+ int cattable = lmt_tointeger(L, 1);
+ if (cattable < 0 || cattable > 15) {
+ cattable = - 12 - 0xFF ;
+ } else {
+ cattable = - cattable - 0xFF;
+ }
+ if (lua_type(L, 2) == LUA_TTABLE) {
+ for (int i = 1; ; i++) {
+ lua_rawgeti(L, 2, i);
+ if (texlib_aux_store(L, -1, PARTIAL_LINE, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ } else {
+ int n = lua_gettop(L);
+ for (int i = 2; i <= n; i++) {
+ texlib_aux_store(L, i, PARTIAL_LINE, cattable);
+ }
+ }
+ return 0;
+}
+
+/* lua.tprint */
+
+static int texlib_tprint(lua_State *L)
+{
+ int n = lua_gettop(L);
+ for (int i = 1; i <= n; i++) {
+ int cattable = default_catcode_table_preset;
+ int startstrings = 1;
+ if (lua_type(L, i) != LUA_TTABLE) {
+ luaL_error(L, "no string to print");
+ }
+ lua_pushvalue(L, i); /* the table */
+ lua_pushinteger(L, 1);
+ lua_gettable(L, -2);
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ cattable = lmt_tointeger(L, -1);
+ startstrings = 2;
+ if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
+ cattable = default_catcode_table_preset;
+ }
+ }
+ lua_pop(L, 1);
+ for (int j = startstrings; ; j++) {
+ lua_pushinteger(L, j);
+ lua_gettable(L, -2);
+ if (texlib_aux_store(L, -1, PARTIAL_LINE, cattable)) {
+ lua_pop(L, 1);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ }
+ lua_pop(L, 1); /* the table */
+ }
+ return 0;
+}
+
+static int texlib_isprintable(lua_State* L)
+{
+ halfword okay = 0;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING :
+ okay = 1;
+ break;
+ case LUA_TUSERDATA :
+ {
+ if (lua_getmetatable(L, 1)) {
+ lua_get_metatablelua(token_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ okay = 1;
+ // lua_pop(L, 2);
+ } else {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -3)) {
+ okay = 1;
+ }
+ // lua_pop(L, 3);
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, okay);
+ return 1;
+}
+
+/*tex We actually don't need to copy and could read from the string. */
+
+int lmt_cstring_input(halfword *n, int *cattable, int *partial, int *finalline)
+{
+ spindle_rope *t = read_spindle.head;
+ int ret = eof_tex_input ;
+ if (! read_spindle.complete) {
+ read_spindle.complete = 1;
+ read_spindle.tail = NULL;
+ }
+ if (t) {
+ switch (t->kind) {
+ case string_lua_input:
+ {
+ if (t->data.t) {
+ /*tex put that thing in the buffer */
+ int strsize = (int) t->tsize;
+ int newlast = lmt_fileio_state.io_first + strsize;
+ lmt_fileio_state.io_last = lmt_fileio_state.io_first;
+ if (tex_room_in_buffer(newlast)) {
+ memcpy(&lmt_fileio_state.io_buffer[lmt_fileio_state.io_last], &t->data.t[0], sizeof(unsigned char) * strsize);
+ lmt_fileio_state.io_last = newlast;
+ lmt_memory_free(t->data.t);
+ t->data.t = NULL;
+ } else {
+ return ret;
+ }
+ }
+ *cattable = t->cattable;
+ *partial = t->partial;
+ *finalline = (t->next == NULL);
+ ret = string_tex_input;
+ break;
+ }
+ case packed_lua_input:
+ {
+ unsigned strsize = t->tsize;
+ int newlast = lmt_fileio_state.io_first + strsize;
+ lmt_fileio_state.io_last = lmt_fileio_state.io_first;
+ if (tex_room_in_buffer(newlast)) {
+ for (unsigned i = 0; i < strsize; i++) {
+ /* when we end up here we often don't have that many bytes */
+ lmt_fileio_state.io_buffer[lmt_fileio_state.io_last + i] = t->data.c[i];
+ }
+ lmt_fileio_state.io_last = newlast;
+ *cattable = t->cattable;
+ *partial = t->partial;
+ *finalline = (t->next == NULL);
+ ret = string_tex_input;
+ } else {
+ return ret;
+ }
+ break;
+ }
+ case token_lua_input:
+ {
+ *n = t->data.h;
+ ret = token_tex_input;
+ break;
+ }
+ case token_list_lua_input:
+ {
+ *n = t->data.h;
+ ret = token_list_tex_input;
+ break;
+ }
+ case node_lua_input:
+ {
+ *n = t->data.h;
+ ret = node_tex_input;
+ break;
+ }
+ }
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = t;
+ read_spindle.head = t->next;
+ } else {
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = NULL;
+ }
+ return ret;
+}
+
+/*tex Open for reading, and make a new one for writing. */
+
+void lmt_cstring_start(void)
+{
+ lmt_spindle_state.spindle_index++;
+ if (lmt_spindle_state.spindle_size == lmt_spindle_state.spindle_index) {
+ int size = (lmt_spindle_state.spindle_size + 1) * sizeof(spindle);
+ spindle *spindles = lmt_memory_realloc(lmt_spindle_state.spindles, (size_t) size);
+ if (spindles) {
+ lmt_spindle_state.spindles = spindles;
+ texlib_aux_reset_spindle(lmt_spindle_state.spindle_index);
+ lmt_spindle_state.spindle_size++;
+ } else {
+ tex_overflow_error("spindle", size);
+ }
+ }
+}
+
+/*tex Close for reading. */
+
+void lmt_cstring_close(void)
+{
+ spindle_rope *t;
+ spindle_rope *next = read_spindle.head;
+ while (next) {
+ if (next->kind == string_tex_input && next->data.t) {
+ lmt_memory_free(next->data.t);
+ next->data.t = NULL;
+ }
+ t = next;
+ next = next->next;
+ if (t == read_spindle.tail) {
+ read_spindle.tail = NULL;
+ }
+ texlib_aux_dispose_rope(t);
+ }
+ read_spindle.head = NULL;
+ texlib_aux_dispose_rope(read_spindle.tail);
+ read_spindle.tail = NULL;
+ read_spindle.complete = 0;
+ lmt_spindle_state.spindle_index--;
+}
+
+/*tex
+ The original was close to the \TEX\ original (even using |cur_val|) but there is no need to have
+ that all-in-one loop with radix magic.
+*/
+
+static const char *texlib_aux_scan_integer_part(lua_State *L, const char *ss, int *ret, int *radix_ret)
+{
+ int negative = 0; /*tex should the answer be negated? */
+ int vacuous = 1; /*tex have no digits appeared? */
+ int overflow = 0;
+ int c = 0; /*tex the current character */
+ const char *s = ss; /*tex where we stopped in the string |ss| */
+ long long result = 0; /*tex return value */
+ while (1) {
+ c = *s++;
+ switch (c) {
+ case ' ':
+ case '+':
+ break;
+ case '-':
+ negative = ! negative;
+ break;
+ case '\'':
+ {
+ int d;
+ *radix_ret = 8;
+ c = *s++;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 7)) {
+ d = c - '0';
+ } else {
+ break;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 8 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ case '"':
+ {
+ int d;
+ *radix_ret = 16;
+ c = *s++;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 9)) {
+ d = c - '0';
+ } else if ((c <= 'A' + 5) && (c >= 'A')) {
+ d = c - 'A' + 10;
+ } else if ((c <= 'a' + 5) && (c >= 'a')) {
+ /*tex Actually \TEX\ only handles uppercase. */
+ d = c - 'a' + 10;
+ } else {
+ goto DONE;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 16 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ default:
+ {
+ int d;
+ *radix_ret = 10;
+ while (c) {
+ if ((c >= '0') && (c <= '0' + 9)) {
+ d = c - '0';
+ } else {
+ goto DONE;
+ }
+ if (! overflow) {
+ vacuous = 0;
+ result = result * 10 + d;
+ if (result > max_integer) {
+ overflow = 1;
+ }
+ }
+ c = *s++;
+ }
+ goto DONE;
+ }
+ }
+ }
+ DONE:
+ if (overflow) {
+ luaL_error(L, "number too big");
+ result = infinity;
+ } else if (vacuous) {
+ luaL_error(L, "missing number, treated as zero") ;
+ }
+ if (negative) {
+ result = -result;
+ }
+ *ret = (int) result;
+ if (c != ' ' && s > ss) {
+ s--;
+ }
+ return s;
+}
+
+/*tex
+ This sets |cur_val| to a dimension. We can clean this up a bit like the normal dimen scanner,
+ but it's seldom called. Scanning is like in \TEX, with gobbling spaces and such. When no unit
+ is given we assume points. When nothing is given we assume zero. Trailing crap is just ignored.
+*/
+
+static const char *texlib_aux_scan_dimen_part(lua_State * L, const char *ss, int *ret)
+{
+ int negative = 0; /*tex should the answer be negated? */
+ int fraction = 0; /*tex numerator of a fraction whose denominator is $2^{16}$ */
+ int numerator;
+ int denominator;
+ scaled special; /*tex an internal dimension */
+ int result = 0;
+ int radix = 0; /*tex the current radix */
+ int remainder = 0; /*tex the to be remainder */
+ int saved_error = lmt_scanner_state.arithmic_error; /*tex to save |arith_error| */
+ const char *s = NULL;
+ if (ss && (*ss == '.' || *ss == ',')) {
+ s = ss;
+ goto FRACTION;
+ } else {
+ s = texlib_aux_scan_integer_part(L, ss, &result, &radix);
+ }
+ if (! (char) *s) {
+ /* error, no unit, assume scaled points */
+ goto ATTACH_FRACTION;
+ }
+ if (result < 0) {
+ negative = ! negative;
+ result = -result;
+ }
+ FRACTION:
+ if ((radix == 0 || radix == 10) && (*s == '.' || *s == ',')) {
+ unsigned k = 0;
+ unsigned char digits[18];
+ s++;
+ while (1) {
+ int c = *s++;
+ if ((c > '0' + 9) || (c < '0')) {
+ break;
+ } else if (k < 17) {
+ digits[k++] = (unsigned char) c - '0';
+ }
+ }
+ fraction = tex_round_decimals_digits(digits, k);
+ if (*s != ' ') {
+ --s;
+ }
+ }
+ /* the unit can have spaces in front */
+/*UNIT: */
+ while ((char) *s == ' ') {
+ s++;
+ }
+ /* We dropped the |nd| and |nc| units as well as the |true| prefix. */
+ if (! (char) *s) {
+ goto ATTACH_FRACTION;
+ } else if (strncmp(s, "pt", 2) == 0) {
+ s += 2;
+ goto ATTACH_FRACTION;
+ } else if (strncmp(s, "mm", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 2540;
+ goto CONVERSION;
+ } else if (strncmp(s, "cm", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 254;
+ goto CONVERSION;
+ } else if (strncmp(s, "sp", 2) == 0) {
+ s += 2;
+ goto DONE;
+ } else if (strncmp(s, "bp", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 7200;
+ goto CONVERSION;
+ } else if (strncmp(s, "in", 2) == 0) {
+ s += 2;
+ numerator = 7227;
+ denominator = 100;
+ goto CONVERSION;
+ } else if (strncmp(s, "dd", 2) == 0) {
+ s += 2;
+ numerator = 1238;
+ denominator = 1157;
+ goto CONVERSION;
+ } else if (strncmp(s, "cc", 2) == 0) {
+ s += 2;
+ numerator = 14856;
+ denominator = 1157;
+ goto CONVERSION;
+ } else if (strncmp(s, "pc", 2) == 0) {
+ s += 2;
+ numerator = 12;
+ denominator = 1;
+ goto CONVERSION;
+ } else if (strncmp(s, "dk", 2) == 0) {
+ s += 2;
+ numerator = 49838;
+ denominator = 7739;
+ goto CONVERSION;
+ } else if (strncmp(s, "em", 2) == 0) {
+ s += 2;
+ special = tex_get_font_em_width(cur_font_par);
+ goto SPECIAL;
+ } else if (strncmp(s, "ex", 2) == 0) {
+ s += 2;
+ special = tex_get_font_ex_height(cur_font_par);
+ goto SPECIAL;
+ } else if (strncmp(s, "px", 2) == 0) {
+ s += 2;
+ special = px_dimen_par;
+ goto SPECIAL;
+ } else if (strncmp(s, "mu", 2) == 0) {
+ s += 2;
+ goto ATTACH_FRACTION;
+ /* } else if (strncmp(s, "true", 4) == 0) { */
+ /* s += 4; */
+ /* goto UNIT; */
+ } else {
+ /* luaL_error(L, "illegal unit of measure (pt inserted)"); */
+ goto ATTACH_FRACTION;
+ }
+ SPECIAL:
+ result = tex_nx_plus_y(result, special, tex_xn_over_d_r(special, fraction, 0200000, &remainder));
+ goto DONE;
+ CONVERSION:
+ result = tex_xn_over_d_r(result, numerator, denominator, &remainder);
+ fraction = (numerator * fraction + 0200000 * remainder) / denominator;
+ result = result + (fraction / 0200000);
+ fraction = fraction % 0200000;
+ ATTACH_FRACTION:
+ if (result >= 040000) {
+ lmt_scanner_state.arithmic_error = 1;
+ } else {
+ result = result * 65536 + fraction;
+ }
+ DONE:
+ if (lmt_scanner_state.arithmic_error || (abs(result) >= 010000000000)) {
+ result = max_dimen;
+ luaL_error(L, "dimension too large");
+ }
+ *ret = negative ? - result : result;
+ lmt_scanner_state.arithmic_error = saved_error;
+ /* only when we want to report junk */
+ while ((char) *s == ' ') {
+ s++;
+ }
+ return s;
+}
+
+static int texlib_aux_dimen_to_number(lua_State *L, const char *s)
+{
+ int result = 0;
+ const char *d = texlib_aux_scan_dimen_part(L, s, &result);
+ if (*d) {
+ return luaL_error(L, "conversion failed (trailing junk?)");
+ } else {
+ return result;
+ }
+}
+
+static int texlib_aux_integer_to_number(lua_State *L, const char *s)
+{
+ int result = 0;
+ int radix = 10;
+ const char *d = texlib_aux_scan_integer_part(L, s, &result, &radix);
+ if (*d) {
+ return luaL_error(L, "conversion failed (trailing junk?)");
+ } else {
+ return result;
+ }
+}
+
+static int texlib_toscaled(lua_State *L)
+{
+ int sp;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ sp = lmt_toroundnumber(L, 1);
+ break;
+ case LUA_TSTRING:
+ sp = texlib_aux_dimen_to_number(L, lua_tostring(L, 1));
+ break;
+ default:
+ return luaL_error(L, "string or a number expected");
+ }
+ lua_pushinteger(L, sp);
+ return 1;
+}
+
+static int texlib_tonumber(lua_State *L)
+{
+ int i;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ i = lmt_toroundnumber(L, 1);
+ break;
+ case LUA_TSTRING:
+ i = texlib_aux_integer_to_number(L, lua_tostring(L, 1));
+ break;
+ default:
+ return luaL_error(L, "string or a number expected");
+ }
+ lua_pushinteger(L, i);
+ return 1;
+}
+
+static int texlib_error(lua_State *L)
+{
+ const char *error = luaL_checkstring(L, 1);
+ const char *help = lua_type(L, 2) == LUA_TSTRING ? luaL_checkstring(L, 2) : NULL;
+ tex_handle_error(normal_error_type, error, help);
+ return 0;
+}
+
+/*tex
+
+ The lua interface needs some extra functions. The functions themselves are quite boring, but they
+ are handy because otherwise this internal stuff has to be accessed from \CCODE\ directly, where
+ lots of the defines are not available.
+
+*/
+
+inline static int texlib_aux_valid_register_index(lua_State *L, int slot, int cmd, int base, int max)
+{
+ int index = -1;
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *str = lua_tolstring(L, 1, &len);
+ int cs = tex_string_locate(str, len, 0);
+ if (eq_type(cs) == cmd) {
+ index = eq_value(cs) - base;
+ }
+ }
+ break;
+ case LUA_TNUMBER:
+ index = lmt_tointeger(L, slot);
+ break;
+ default:
+ luaL_error(L, "string or a number expected");
+ break;
+ }
+ if (index >= 0 && index <= max) {
+ return index;
+ } else {
+ return -1;
+ }
+}
+
+static int texlib_get_register_index(lua_State *L)
+{
+ size_t len;
+ const char *str = lua_tolstring(L, 1, &len);
+ int cs = tex_string_locate(str, len, 0);
+ int index = -1;
+ switch (eq_type(cs)) {
+ case register_toks_cmd : index = eq_value(cs) - register_toks_base; break;
+ case register_int_cmd : index = eq_value(cs) - register_int_base; break;
+ case register_attribute_cmd : index = eq_value(cs) - register_attribute_base; break;
+ case register_dimen_cmd : index = eq_value(cs) - register_dimen_base; break;
+ case register_glue_cmd : index = eq_value(cs) - register_glue_base; break;
+ case register_mu_glue_cmd : index = eq_value(cs) - register_mu_glue_base; break;
+ }
+ if (index >= 0) {
+ lua_pushinteger(L, index);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+inline static int texlib_aux_checked_register(lua_State *L, int cmd, int base, int max)
+{
+ int index = texlib_aux_valid_register_index(L, 1, cmd, base, max);
+ if (index >= 0) {
+ lua_pushinteger(L, index);
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+typedef void (*setfunc) (int, halfword, int, int);
+typedef halfword (*getfunc) (int, int);
+
+int lmt_check_for_flags(lua_State *L, int slot, int *flags, int prefixes, int numeric)
+{
+ if (global_defs_par) {
+ *flags = add_global_flag(*flags);
+ }
+ if (prefixes) {
+ while (1) {
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ const char *str = lua_tostring(L, slot);
+ if (! str || lua_key_eq(str, macro)) {
+ /*tex For practical reasons we skip empty strings. */
+ slot += 1;
+ } else if (lua_key_eq(str, global)) {
+ slot += 1;
+ *flags = add_global_flag(*flags);
+ } else if (lua_key_eq(str, frozen)) {
+ slot += 1;
+ *flags = add_frozen_flag(*flags);
+ } else if (lua_key_eq(str, permanent)) {
+ slot += 1;
+ *flags = add_permanent_flag(*flags);
+ } else if (lua_key_eq(str, protected)) {
+ slot += 1;
+ *flags = add_protected_flag(*flags);
+ } else if (lua_key_eq(str, untraced)) {
+ slot += 1;
+ *flags = add_untraced_flag(*flags);
+ } else if (lua_key_eq(str, immutable)) {
+ slot += 1;
+ *flags = add_immutable_flag(*flags);
+ } else if (lua_key_eq(str, overloaded)) {
+ slot += 1;
+ *flags = add_overloaded_flag(*flags);
+ } else if (lua_key_eq(str, value)) {
+ slot += 1;
+ *flags = add_value_flag(*flags);
+ } else if (lua_key_eq(str, conditional) || lua_key_eq(str, condition)) {
+ /* condition will go, conditional stays */
+ slot += 1;
+ *flags = add_conditional_flag(*flags);
+ } else {
+ /*tex When we have this at the start we now can have a csname. */
+ return slot;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ if (numeric) {
+ *flags |= lua_tointeger(L, slot);
+ slot += 1;
+ break;
+ } else {
+ return slot;
+ }
+ case LUA_TNIL:
+ /*tex This is quite convenient if we use some composer. */
+ slot += 1;
+ break;
+ default:
+ return slot;
+ }
+ }
+ }
+ return slot;
+}
+
+int lmt_check_for_level(lua_State *L, int slot, quarterword *level, quarterword defaultlevel)
+{
+ if (lua_type(L, slot) == LUA_TSTRING) {
+ const char *str = lua_tostring(L, slot);
+ *level = lua_key_eq(str, global) ? level_one : defaultlevel;
+ ++slot;
+ } else {
+ *level = defaultlevel;
+ }
+ return slot;
+}
+
+/* -1=noindex, 0=register 1=internal */
+
+static int texlib_aux_check_for_index(
+ lua_State *L,
+ int slot,
+ const char *what,
+ int *index,
+ int internal_cmd,
+ int register_cmd,
+ int internal_base,
+ int register_base,
+ int max_index
+) {
+ *index = -1;
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ size_t len;
+ const char *str = lua_tolstring(L, slot, &len);
+ int cs = tex_string_locate(str, len, 0);
+ if (eq_type(cs) == internal_cmd) {
+ *index = eq_value(cs) - internal_base;
+ return 1;
+ } else if (eq_type(cs) == register_cmd) {
+ *index = eq_value(cs) - register_base;
+ return 0;
+ } else {
+ luaL_error(L, "incorrect %s name", what);
+ return -1;
+ }
+ }
+ case LUA_TNUMBER:
+ *index = lmt_tointeger(L, slot);
+ if (*index >= 0 && *index <= max_index) {
+ return 0;
+ } else {
+ return -1;
+ }
+ default:
+ luaL_error(L, "%s name or valid index expected", what);
+ return -1;
+ }
+}
+
+static int texlib_get(lua_State *L);
+
+/*tex
+
+ We intercept the first string and when it is |global| then we check the second one which can
+ also be a string. It is unlikely that one will use |\global| as register name so we don't need
+ to check for the number of further arguments. This permits to treat lack of them as a reset.
+
+*/
+
+static int texlib_isdimen(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_dimen_cmd, register_dimen_base, max_dimen_register_index);
+}
+
+/* [global] name|index integer|dimension|false|nil */
+
+static int texlib_setdimen(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "dimen", &index, internal_dimen_cmd, register_dimen_cmd, internal_dimen_base, register_dimen_base, max_dimen_register_index);
+ if (state >= 0) {
+ halfword value = 0;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ value = lmt_toroundnumber(L, slot++);
+ break;
+ case LUA_TSTRING:
+ value = texlib_aux_dimen_to_number(L, lua_tostring(L, slot++));
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, slot++)) {
+ /*tex The value |true| makes no sense. */
+ return 0;
+ }
+ break;
+ case LUA_TNONE:
+ case LUA_TNIL:
+ break;
+ default:
+ luaL_error(L, "unsupported dimen value type");
+ break;
+ }
+ tex_set_tex_dimen_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_dimen_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getdimen(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "dimen", &index, internal_dimen_cmd, register_dimen_cmd, internal_dimen_base, register_dimen_base, max_dimen_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_dimen_register(index, state) : 0);
+ return 1;
+}
+
+// static halfword texlib_aux_make_glue(lua_State *L, int top, int slot)
+// {
+// halfword value = copy_node(zero_glue);
+// if (++slot <= top) {
+// glue_amount(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_stretch(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_shrink(value) = lmt_toroundnumber(L, slot);
+// if (++slot <= top) {
+// glue_stretch_order(value) = lmt_tohalfword(L, slot);
+// if (++slot <= top) {
+// glue_shrink_order(value) = lmt_tohalfword(L, slot);
+// }
+// }
+// }
+// }
+// }
+// return value;
+// }
+
+static halfword texlib_aux_make_glue(lua_State *L, int top, int slot)
+{
+ halfword value = tex_copy_node(zero_glue);
+ if (slot <= top) {
+ glue_amount(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_shrink(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch_order(value) = lmt_tohalfword(L, slot++);
+ if (slot <= top) {
+ glue_shrink_order(value) = lmt_tohalfword(L, slot++);
+ }
+ }
+ }
+ }
+ }
+ return value;
+}
+
+inline static int texlib_aux_push_glue(lua_State* L, halfword g)
+{
+ if (g) {
+ lua_pushinteger(L, glue_amount(g));
+ lua_pushinteger(L, glue_stretch(g));
+ lua_pushinteger(L, glue_shrink(g));
+ lua_pushinteger(L, glue_stretch_order(g));
+ lua_pushinteger(L, glue_shrink_order(g));
+ } else {
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ }
+ return 5;
+}
+
+static halfword texlib_aux_get_glue_spec(lua_State *L, int slot)
+{
+ halfword value = null;
+ switch (lua_type(L, slot + 1)) {
+ case LUA_TBOOLEAN:
+ // if (lua_toboolean(L, slot + 1)) {
+ // /*tex The value |true| makes no sense. */
+ // }
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ value = lmt_check_isnode(L, slot + 1);
+ if (node_type(value) != glue_spec_node) {
+ value = null;
+ luaL_error(L, "glue_spec expected");
+ }
+ }
+ return value;
+}
+
+static int texlib_isskip(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index);
+}
+
+/* [global] name|index gluespec|false|nil */
+
+static int texlib_setskip(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ if (state >= 0) {
+ halfword value = texlib_aux_get_glue_spec(L, slot++);
+ tex_set_tex_skip_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_glue_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getskip(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_skip_register(index, state) : null;
+ lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
+ return 1;
+}
+
+static int texlib_isglue(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index);
+}
+
+/* [global] slot [width] [stretch] [shrink] [stretch_order] [shrink_order] */
+
+static int texlib_setglue(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ if (state >= 0) {
+ tex_set_tex_skip_register(index, texlib_aux_make_glue(L, lua_gettop(L), slot), flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getglue(lua_State *L)
+{
+ int index;
+ int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
+ int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_skip_register(index, state) : null;
+ if (! value) {
+ lua_pushinteger(L, 0);
+ if (all) {
+ /* save the trouble of testing p/m */
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, 0);
+ return 3;
+ } else {
+ return 1;
+ }
+ } else if (all) {
+ return texlib_aux_push_glue(L, value);
+ } else {
+ /* false */
+ lua_pushinteger(L, value ? glue_amount(value) : 0);
+ return 1;
+ }
+}
+
+static int texlib_ismuskip(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_mu_glue_cmd, register_mu_glue_base, max_mu_glue_register_index);
+}
+
+static int texlib_setmuskip(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ tex_set_tex_mu_skip_register(index, texlib_aux_get_glue_spec(L, slot), flags, state);
+ return 0;
+}
+
+static int texlib_getmuskip(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_mu_skip_register(index, state) : null;
+ lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
+ return 1;
+}
+
+static int texlib_ismuglue(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_mu_glue_cmd, register_mu_glue_base, max_mu_glue_register_index);
+}
+
+static int texlib_setmuglue(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = texlib_aux_make_glue(L, lua_gettop(L), slot);
+ if (state >= 0) {
+ tex_set_tex_mu_skip_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getmuglue(lua_State *L)
+{
+ int index;
+ int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
+ int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_mu_glue_cmd, register_mu_glue_cmd, internal_mu_glue_base, register_mu_glue_base, max_mu_glue_register_index);
+ halfword value = state >= 0 ? tex_get_tex_mu_skip_register(index, state) : null;
+ if (! value) {
+ lua_pushinteger(L, 0);
+ return 1;
+ } else if (all) {
+ return texlib_aux_push_glue(L, value);
+ } else {
+ /* false */
+ lua_pushinteger(L, value ? glue_amount(value) : 0);
+ return 1;
+ }
+}
+
+static int texlib_iscount(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_int_cmd, register_int_base, max_int_register_index);
+}
+
+static int texlib_setcount(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "count", &index, internal_int_cmd, register_int_cmd, internal_int_base, register_int_base, max_int_register_index);
+ if (state >= 0) {
+ halfword value = lmt_optinteger(L, slot++, 0);
+ tex_set_tex_count_register(index, value, flags, state);
+ if (state == 1 && lua_toboolean(L, slot)) {
+ tex_update_par_par(internal_int_cmd, index);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getcount(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "count", &index, internal_int_cmd, register_int_cmd, internal_int_base, register_int_base, max_int_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_count_register(index, state) : 0);
+ return 1;
+}
+
+static int texlib_isattribute(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_attribute_cmd, register_attribute_base, max_attribute_register_index);
+}
+
+/*tex there are no system set attributes so this is a bit overkill */
+
+static int texlib_setattribute(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index);
+ if (state >= 0) {
+ halfword value = lmt_optinteger(L, slot++, unused_attribute_value);
+ tex_set_tex_attribute_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_getattribute(lua_State *L)
+{
+ int index;
+ int state = texlib_aux_check_for_index(L, 1, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index);
+ lua_pushinteger(L, state >= 0 ? tex_get_tex_attribute_register(index, state) : 0);
+ return 1;
+}
+
+/*tex todo: we can avoid memcpy as there is no need to go through the pool */
+
+/* use string_to_toks */
+
+static int texlib_istoks(lua_State *L)
+{
+ return texlib_aux_checked_register(L, register_toks_cmd, register_toks_base, max_toks_register_index);
+}
+
+/* [global] name|integer string|nil */
+
+static int texlib_settoks(lua_State *L)
+{
+ int flags = 0;
+ int index = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index);
+ if (state >= 0) {
+ lstring value = { .c = NULL, .l = 0 };
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ value.c = lua_tolstring(L, slot, &value.l);
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ return luaL_error(L, "string or nil expected");
+ }
+ tex_set_tex_toks_register(index, value, flags, state);
+ }
+ return 0;
+}
+
+/* [global] name|index catcode string */
+
+static int texlib_scantoks(lua_State *L) // TODO
+{
+ int index = 0;
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index);
+ if (state >= 0) {
+ lstring value = { .c = NULL, .l = 0 };
+ int cattable = lmt_checkinteger(L, slot++);
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ value.c = lua_tolstring(L, slot, &value.l);
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ return luaL_error(L, "string or nil expected");
+ }
+ tex_scan_tex_toks_register(index, cattable, value, flags, state);
+ }
+ return 0;
+}
+
+static int texlib_gettoks(lua_State *L)
+{
+ int index;
+ int slot = 1;
+ int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base, max_toks_register_index);
+ if (state >= 0) {
+ if (lua_toboolean(L, slot)) {
+ lmt_token_register_to_lua(L, state ? toks_parameter(index) : toks_register(index));
+ } else {
+ strnumber value = tex_get_tex_toks_register(index, state);
+ char *s = tex_makecstring(value);
+ lua_pushstring(L, s);
+ lmt_memory_free(s);
+ tex_flush_str(value);
+ }
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+ return 1;
+}
+
+static int texlib_getmark(lua_State *L)
+{
+ if (lua_gettop(L) == 0) {
+ lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
+ return 1;
+ } else if (lua_type(L, 1) == LUA_TSTRING) {
+ int mrk = -1;
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ mrk = top_marks_code;
+ } else if (lua_key_eq(s, first)) {
+ mrk = first_marks_code;
+ } else if (lua_key_eq(s, bottom)) {
+ mrk = bot_marks_code;
+ } else if (lua_key_eq(s, splitfirst)) {
+ mrk = split_first_marks_code;
+ } else if (lua_key_eq(s, splitbottom)) {
+ mrk = split_bot_marks_code;
+ } else if (lua_key_eq(s, current)) {
+ mrk = current_marks_code;
+ }
+ if (mrk >= 0) {
+ int num = lmt_optinteger(L, 2, 0);
+ if (num >= 0 && num <= lmt_mark_state.mark_data.ptr) {
+ halfword ptr = tex_get_some_mark(mrk, num);
+ if (ptr) {
+ char *str = tex_tokenlist_to_tstring(ptr, 1, NULL, 0, 0, 0);
+ if (str) {
+ lua_pushstring(L, str);
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+ }
+ } else {
+ luaL_error(L, "valid mark class expected");
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+int lmt_get_box_id(lua_State *L, int i, int report)
+{
+ int index = -1;
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ {
+ size_t k = 0;
+ const char *s = lua_tolstring(L, i, &k);
+ int cs = tex_string_locate(s, k, 0);
+ int cmd = eq_type(cs);
+ switch (cmd) {
+ case char_given_cmd:
+ // case math_char_given_cmd:
+ case integer_cmd:
+ index = eq_value(cs);
+ break;
+ case register_int_cmd:
+ index = register_int_number(eq_value(cs));
+ break;
+ default:
+ /* we don't accept other commands as it makes no sense */
+ break;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ index = lmt_tointeger(L, i);
+ default:
+ break;
+ }
+ if (index >= 0 && index <= max_box_register_index) {
+ return index;
+ } else {
+ if (report) {
+ luaL_error(L, "string or a number within range expected");
+ }
+ return -1;
+ }
+}
+
+static int texlib_getbox(lua_State *L)
+{
+ halfword index = lmt_get_box_id(L, 1, 1);
+ lmt_node_list_to_lua(L, index >= 0 ? tex_get_tex_box_register(index, 0) : null);
+ return 1;
+}
+
+static int texlib_splitbox(lua_State *L)
+{
+ int index = lmt_get_box_id(L, 1, 1);
+ if (index >= 0) {
+ if (lua_isnumber(L, 2)) {
+ int m = packing_additional;
+ switch (lua_type(L, 3)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 3);
+ if (lua_key_eq(s, exactly)) {
+ m = packing_exactly;
+ } else if (lua_key_eq(s, additional)) {
+ m = packing_additional;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ m = lmt_tointeger(L, 3);
+ if (m != packing_exactly && m != packing_additional) {
+ m = packing_exactly;
+ luaL_error(L, "wrong mode in splitbox");
+ }
+ break;
+ }
+ }
+ lmt_node_list_to_lua(L, tex_vsplit(index, lmt_toroundnumber(L, 2), m));
+ } else {
+ /* maybe a warning */
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* todo */
+
+static int texlib_isbox(lua_State *L)
+{
+ lua_pushboolean(L, lmt_get_box_id(L, 1, 0) >= 0);
+ return 1;
+}
+
+static int texlib_setbox(lua_State *L)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int index = lmt_get_box_id(L, slot++, 1);
+ if (index >= 0) {
+ int n = null;
+ switch (lua_type(L, slot)) {
+ case LUA_TBOOLEAN:
+ n = lua_toboolean(L, slot);
+ if (n) {
+ return 0;
+ } else {
+ n = null;
+ }
+ break;
+ case LUA_TNIL:
+ case LUA_TNONE:
+ break;
+ default:
+ n = lmt_node_list_from_lua(L, slot);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ break;
+ default:
+ return luaL_error(L, "invalid node type %s passed", get_node_name(node_type(n)));
+ }
+ }
+ break;
+ }
+ tex_set_tex_box_register(index, n, flags, 0);
+ }
+ return 0;
+}
+
+/* [global] index first second */
+
+static int texlib_setlccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch1 = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch1)) {
+ halfword ch2 = lmt_checkhalfword(L, slot++);
+ if (character_in_range(ch2)) {
+ tex_set_lc_code(ch1, ch2, level);
+ if (slot <= top) {
+ halfword ch3 = lmt_checkhalfword(L, slot);
+ if (character_in_range(ch3)) {
+ tex_set_uc_code(ch1, ch3, level);
+ } else {
+ texlib_aux_show_character_error(L, ch3);
+ }
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch2);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch1);
+ }
+ }
+ return 0;
+}
+
+static int texlib_setuccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch1 = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch1)) {
+ halfword ch2 = lmt_checkhalfword(L, slot++);
+ if (character_in_range(ch2)) {
+ tex_set_uc_code(ch1, ch2, level);
+ if (slot <= top) {
+ halfword ch3 = lmt_checkhalfword(L, slot);
+ if (character_in_range(ch3)) {
+ tex_set_lc_code(ch1, ch3, level);
+ } else {
+ texlib_aux_show_character_error(L, ch3);
+ }
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch2);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch1);
+ }
+ }
+ return 0;
+}
+
+static int texlib_setsfcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (half_in_range(val)) {
+ tex_set_sf_code(ch, val, level);
+ } else {
+ texlib_aux_show_half_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_sethccode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (half_in_range(val)) {
+ tex_set_hc_code(ch, val, level);
+ } else {
+ texlib_aux_show_half_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_sethmcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ tex_set_hm_code(ch, val, level);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+static int texlib_getlccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_lc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_getuccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_uc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_getsfcode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_sf_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_gethccode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_hc_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+static int texlib_gethmcode(lua_State *L)
+{
+ int ch = lmt_checkinteger(L, 1);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_hm_code(ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+/* [global] [cattable] code value */
+
+static int texlib_setcatcode(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 2) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int cattable = ((top - slot + 1) >= 3) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword val = lmt_checkhalfword(L, slot);
+ if (catcode_in_range(val)) {
+ tex_set_cat_code(cattable, ch, val, level);
+ } else {
+ texlib_aux_show_catcode_error(L, val);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ }
+ return 0;
+}
+
+/* [cattable] code */
+
+static int texlib_getcatcode(lua_State *L)
+{
+ int slot = 1;
+ int cattable = (lua_gettop(L) > 1) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
+ int ch = lmt_checkinteger(L, slot);
+ if (character_in_range(ch)) {
+ lua_pushinteger(L, tex_get_cat_code(cattable, ch));
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ lua_pushinteger(L, 12); /* other */
+ }
+ return 1;
+}
+
+/*
+ [global] code { c f ch }
+ [global] code c f ch (a bit easier on memory, counterpart of getter)
+*/
+
+static int texlib_setmathcode(lua_State *L)
+{
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword cval, fval, chval;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ cval = lmt_checkhalfword(L, slot++);
+ fval = lmt_checkhalfword(L, slot++);
+ chval = lmt_checkhalfword(L, slot);
+ break;
+ case LUA_TTABLE:
+ lua_rawgeti(L, slot, 1);
+ cval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 2);
+ fval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 3);
+ chval = lmt_checkhalfword(L, -1);
+ lua_pop(L, 3);
+ break;
+ default:
+ return luaL_error(L, "number of table expected");
+ }
+ if (class_in_range(cval)) {
+ if (family_in_range(fval)) {
+ if (character_in_range(chval)) {
+ mathcodeval m;
+ m.character_value = chval;
+ m.class_value = (short) cval;
+ m.family_value = (short) fval;
+ tex_set_math_code(ch, m, (quarterword) (level));
+ } else {
+ texlib_aux_show_character_error(L, chval);
+ }
+ } else {
+ texlib_aux_show_family_error(L, fval);
+ }
+ } else {
+ texlib_aux_show_class_error(L, cval);
+ }
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+return 0;
+}
+
+static int texlib_getmathcode(lua_State* L)
+{
+ mathcodeval mval = { 0, 0, 0 };
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ mval = tex_get_math_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ lua_createtable(L, 3, 0);
+ lua_pushinteger(L, mval.class_value);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, mval.family_value);
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, mval.character_value);
+ lua_rawseti(L, -2, 3);
+ return 1;
+}
+
+static int texlib_getmathcodes(lua_State* L)
+{
+ mathcodeval mval = { 0, 0, 0 };
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ mval = tex_get_math_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ lua_pushinteger(L, mval.class_value);
+ lua_pushinteger(L, mval.family_value);
+ lua_pushinteger(L, mval.character_value);
+ return 3;
+}
+
+/*
+ [global] code { c f ch }
+ [global] code c f ch (a bit easier on memory, counterpart of getter)
+*/
+
+static int texlib_setdelcode(lua_State* L)
+{
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ /* todo: when no integer than do a reset */
+ int ch = lmt_checkinteger(L, slot++);
+ if (character_in_range(ch)) {
+ halfword sfval, scval, lfval, lcval;
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ sfval = lmt_checkhalfword(L, slot++);
+ scval = lmt_checkhalfword(L, slot++);
+ lfval = lmt_checkhalfword(L, slot++);
+ lcval = lmt_checkhalfword(L, slot);
+ break;
+ case LUA_TTABLE:
+ lua_rawgeti(L, slot, 1);
+ sfval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 2);
+ scval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 3);
+ lfval = lmt_checkhalfword(L, -1);
+ lua_rawgeti(L, slot, 4);
+ lcval = lmt_checkhalfword(L, -1);
+ lua_pop(L, 4);
+ break;
+ default:
+ return luaL_error(L, "number of table expected");
+ }
+ if (family_in_range(sfval)) {
+ if (character_in_range(scval)) {
+ if (family_in_range(lfval)) {
+ if (character_in_range(lcval)) {
+ delcodeval d;
+ d.small.class_value = 0;
+ d.small.family_value = (short) sfval;
+ d.small.character_value = scval;
+ d.large.class_value = 0;
+ d.large.family_value = (short) lfval;
+ d.large.character_value = lcval;
+ tex_set_del_code(ch, d, (quarterword) (level));
+ }
+ else {
+ texlib_aux_show_character_error(L, lcval);
+ }
+ }
+ else {
+ texlib_aux_show_family_error(L, lfval);
+ }
+ }
+ else {
+ texlib_aux_show_character_error(L, scval);
+ }
+ }
+ else {
+ texlib_aux_show_family_error(L, sfval);
+ }
+ }
+ else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ return 0;
+}
+
+static int texlib_getdelcode(lua_State* L)
+{
+ delcodeval dval = tex_no_del_code();
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ dval = tex_get_del_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ if (tex_has_del_code(dval)) {
+ lua_createtable(L, 4, 0);
+ lua_pushinteger(L, dval.small.family_value);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, dval.small.character_value);
+ lua_rawseti(L, -2, 2);
+ lua_pushinteger(L, dval.large.family_value);
+ lua_rawseti(L, -2, 3);
+ lua_pushinteger(L, dval.large.character_value);
+ lua_rawseti(L, -2, 4);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_getdelcodes(lua_State* L)
+{
+ delcodeval dval = tex_no_del_code();
+ int ch = lmt_checkinteger(L, -1);
+ if (character_in_range(ch)) {
+ dval = tex_get_del_code(ch);
+ } else {
+ texlib_aux_show_character_error(L, ch);
+ }
+ if (tex_has_del_code(dval)) {
+ lua_pushinteger(L, dval.small.family_value);
+ lua_pushinteger(L, dval.small.character_value);
+ lua_pushinteger(L, dval.large.family_value);
+ lua_pushinteger(L, dval.large.character_value);
+ } else {
+ lua_pushnil(L);
+ }
+ return 4;
+}
+
+static halfword texlib_aux_getdimension(lua_State* L, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ return lmt_toroundnumber(L, index);
+ case LUA_TSTRING:
+ return texlib_aux_dimen_to_number(L, lua_tostring(L, index));
+ default:
+ luaL_error(L, "string or number expected (dimension)");
+ return 0;
+ }
+}
+
+static halfword texlib_aux_getinteger(lua_State* L, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ return lmt_toroundnumber(L, index);
+ default:
+ luaL_error(L, "number expected (integer)");
+ return 0;
+ }
+}
+
+static halfword texlib_toparshape(lua_State *L, int i)
+{
+ if (lua_type(L, i) == LUA_TTABLE) {
+ halfword n = (halfword) luaL_len(L, i);
+ if (n > 0) {
+ halfword p = tex_new_specification_node(n, par_shape_code, 0); /* todo: repeat but then not top based */
+ lua_push_key(repeat);
+ if (lua_rawget(L, -2) == LUA_TBOOLEAN && lua_toboolean(L, -1)) {
+ tex_set_specification_option(p, specification_option_repeat);
+ }
+ lua_pop(L, 1);
+ /* fill |p| */
+ for (int j = 1; j <= n; j++) {
+ halfword indent = 0;
+ halfword width = 0;
+ if (lua_rawgeti(L, i, j) == LUA_TTABLE) {
+ if (lua_rawgeti(L, -1, 1) == LUA_TNUMBER) {
+ indent = lmt_toroundnumber(L, -1);
+ if (lua_rawgeti(L, -2, 2) == LUA_TNUMBER) {
+ width = lmt_toroundnumber(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ }
+ lua_pop(L, 1);
+ tex_set_specification_indent(p, j, indent);
+ tex_set_specification_width(p, j, width);
+ }
+ return p;
+ }
+ }
+ return null;
+}
+
+static int texlib_shiftparshape(lua_State *L)
+{
+ if (par_shape_par) {
+ tex_shift_specification_list(par_shape_par, lmt_tointeger(L, 1), lua_toboolean(L, 2));
+ }
+ return 0;
+}
+
+static int texlib_snapshotpar(lua_State *L)
+{
+ halfword par = tex_find_par_par(cur_list.head);
+ if (par) {
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ tex_snapshot_par(par, lmt_tointeger(L, 1));
+ }
+ lua_pushinteger(L, par_state(par));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int texlib_getparstate(lua_State *L)
+{
+ lua_createtable(L, 0, 7);
+ lua_push_integer_at_key(L, hsize, hsize_par);
+ lua_push_integer_at_key(L, leftskip, left_skip_par ? glue_amount(left_skip_par) : 0);
+ lua_push_integer_at_key(L, rightskip, right_skip_par ? glue_amount(right_skip_par) : 0);
+ lua_push_integer_at_key(L, hangindent, hang_indent_par);
+ lua_push_integer_at_key(L, hangafter, hang_after_par);
+ lua_push_integer_at_key(L, parindent, par_indent_par);
+ lua_push_specification_at_key(L, parshape, par_shape_par);
+ return 1;
+}
+
+static int texlib_set_item(lua_State* L, int index, int prefixes)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, index, &flags, prefixes, 0);
+ size_t sl;
+ const char *st = lua_tolstring(L, slot++, &sl);
+ if (sl > 0) {
+ int cs = tex_string_locate(st, sl, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = eq_type(cs);
+ switch (cmd) {
+ case internal_int_cmd:
+ case register_int_cmd: /* ? */
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int n = lmt_tointeger(L, slot++);
+ if (cmd == register_int_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_int_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ break;
+ }
+ default:
+ luaL_error(L, "number expected");
+ break;
+ }
+ return 1;
+ case internal_dimen_cmd:
+ case register_dimen_cmd:
+ {
+ halfword n = texlib_aux_getdimension(L, slot);
+ if (cmd == register_dimen_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_dimen_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ return 1;
+ }
+ case internal_glue_cmd:
+ case register_glue_cmd:
+ switch (lua_type(L, slot)) {
+ case LUA_TNUMBER:
+ {
+ int top = lua_gettop(L);
+ halfword value = tex_copy_node(zero_glue);
+ glue_amount(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_shrink(value) = lmt_toroundnumber(L, slot++);
+ if (slot <= top) {
+ glue_stretch_order(value) = lmt_tohalfword(L, slot++);
+ if (slot <= top) {
+ glue_shrink_order(value) = lmt_tohalfword(L, slot);
+ }
+ }
+ }
+ }
+ if (cmd == register_glue_cmd) {
+ tex_word_define(flags, eq_value(cs), value);
+ } else {
+ tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), value);
+ }
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ halfword n = lmt_check_isnode(L, slot);
+ if (node_type(n) == glue_spec_node) {
+ if (cmd == register_glue_cmd) {
+ tex_word_define(flags, eq_value(cs), n);
+ } else {
+ tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
+ }
+ } else {
+ luaL_error(L, "gluespec node expected");
+ }
+ break;
+ }
+ default:
+ luaL_error(L, "number or node expected");
+ break;
+ }
+ return 1;
+ case internal_toks_cmd:
+ case register_toks_cmd:
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ int t = lmt_token_list_from_lua(L, slot);
+ // define(flags, eq_value(cs), call_cmd, t); /* was call_cmd */
+ tex_define(flags, eq_value(cs), cmd == internal_toks_cmd ? internal_toks_reference_cmd : register_toks_reference_cmd, t); /* eq_value(cs) and not cs ? */
+ break;
+ }
+ default:
+ luaL_error(L, "string expected");
+ break;
+ }
+ return 1;
+ case set_page_property_cmd:
+ /*tex This could be |set_page_property_value| instead. */
+ switch (eq_value(cs)) {
+ // case page_goal_code:
+ // case page_total_code:
+ // case page_vsize_code:
+ case page_depth_code:
+ lmt_page_builder_state.depth = texlib_aux_getdimension(L, slot);
+ break;
+ // case page_stretch_code:
+ // case page_filstretch_code:
+ // case page_fillstretch_code:
+ // case page_filllstretch_code:
+ // case page_shrink_code:
+ case insert_storing_code:
+ lmt_insert_state.storing = texlib_aux_getinteger(L, slot);
+ break;
+ // case dead_cycles_code:
+ // case insert_penalties_code:
+ // case interaction_mode_code:
+ default:
+ return 0;
+ }
+ case set_auxiliary_cmd:
+ /*tex This could be |set_aux_value| instead. */
+ switch (eq_value(cs)) {
+ case space_factor_code:
+ cur_list.space_factor = texlib_aux_getinteger(L, slot);
+ return 1;
+ case prev_depth_code:
+ cur_list.prev_depth = texlib_aux_getdimension(L, slot);
+ return 1;
+ case prev_graf_code:
+ cur_list.prev_graf = texlib_aux_getinteger(L, slot);
+ return 1;
+ default:
+ return 0;
+ }
+ case set_box_property_cmd:
+ /*tex This could be |set_box_property_value| instead. */
+ return 0;
+ case set_specification_cmd:
+ {
+ int chr = internal_specification_number(eq_value(cs));
+ switch (chr) {
+ case par_shape_code:
+ {
+ halfword p = texlib_toparshape(L, slot);
+ tex_define(flags, eq_value(cs), specification_reference_cmd, p);
+ // lua_toboolean(L, slot + 1) ? add_frozen_flag(flags) : flags
+ if (is_frozen(flags) && cur_mode == hmode) {
+ tex_update_par_par(specification_reference_cmd, chr);
+ }
+ break;
+ }
+ }
+ return 0;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_set(lua_State *L)
+{
+ texlib_set_item(L, 1, 1);
+ return 0;
+}
+
+static int texlib_newindex(lua_State *L)
+{
+ if (! texlib_set_item(L, 2, 0)) {
+ lua_rawset(L, 1);
+ }
+ return 0;
+}
+
+static int texlib_aux_convert(lua_State *L, int cur_code)
+{
+ int i = -1;
+ char *str = NULL;
+ switch (cur_code) {
+ /* ignored (yet) */
+ case insert_progress_code: /* arg <register int> */
+ case lua_code: /* arg complex */
+ case lua_escape_string_code: /* arg token list */
+ case string_code: /* arg token */
+ case cs_string_code: /* arg token */
+ case detokenized_code: /* arg token */
+ case meaning_code: /* arg token */
+ case to_mathstyle_code:
+ break;
+ /* the next fall through, and come from 'official' indices! */
+ case font_name_code: /* arg fontid */
+ case font_specification_code: /* arg fontid */
+ case font_identifier_code: /* arg fontid */
+ case number_code: /* arg int */
+ case to_integer_code: /* arg int */
+ case to_hexadecimal_code: /* arg int */
+ case to_scaled_code: /* arg int */
+ case to_sparse_scaled_code: /* arg int */
+ case to_dimension_code: /* arg int */
+ case to_sparse_dimension_code: /* arg int */
+ case roman_numeral_code: /* arg int */
+ if (lua_gettop(L) < 1) {
+ /* error */
+ }
+ i = lmt_tointeger(L, 1);
+ // fall through
+ default:
+ /* no backend here */
+ if (cur_code < 32) {
+ int texstr = tex_the_convert_string(cur_code, i);
+ if (texstr) {
+ str = tex_makecstring(texstr);
+ tex_flush_str(texstr);
+ }
+ }
+ break;
+ }
+ /* end */
+ if (str) {
+ lua_pushstring(L, str);
+ lmt_memory_free(str);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_aux_scan_internal(lua_State *L, int cmd, int code, int values)
+{
+ int retval = 1 ;
+ int save_cur_val = cur_val;
+ int save_cur_val_level = cur_val_level;
+ tex_scan_something_simple(cmd, code);
+ switch (cur_val_level) {
+ case int_val_level:
+ case dimen_val_level:
+ case attr_val_level:
+ lua_pushinteger(L, cur_val);
+ break;
+ case glue_val_level:
+ case mu_val_level:
+ switch (values) {
+ case 0:
+ lua_pushinteger(L, glue_amount(cur_val));
+ tex_flush_node(cur_val);
+ break;
+ case 1:
+ lua_pushinteger(L, glue_amount(cur_val));
+ lua_pushinteger(L, glue_stretch(cur_val));
+ lua_pushinteger(L, glue_shrink(cur_val));
+ lua_pushinteger(L, glue_stretch_order(cur_val));
+ lua_pushinteger(L, glue_shrink_order(cur_val));
+ tex_flush_node(cur_val);
+ retval = 5;
+ break;
+ default:
+ lmt_push_node_fast(L, cur_val);
+ break;
+ }
+ break;
+ case list_val_level:
+ lmt_push_node_fast(L, cur_val);
+ break;
+ default:
+ {
+ int texstr = tex_the_scanned_result();
+ char *str = tex_makecstring(texstr);
+ if (str) {
+ lua_pushstring(L, str);
+ lmt_memory_free(str);
+ } else {
+ lua_pushnil(L);
+ }
+ tex_flush_str(texstr);
+ }
+ break;
+ }
+ cur_val = save_cur_val;
+ cur_val_level = save_cur_val_level;
+ return retval;
+}
+
+/*tex
+ Todo: complete this one.
+*/
+
+static int texlib_aux_someitem(lua_State *L, int code)
+{
+ switch (code) {
+ /* the next two do not actually exist */
+ /* case attrexpr_code: */
+ /* break; */
+ /* the expressions do something complicated with arguments, yuck */
+ case numexpr_code:
+ case dimexpr_code:
+ case glueexpr_code:
+ case muexpr_code:
+ case numexpression_code:
+ case dimexpression_code:
+ break;
+ // case dimen_to_scale_code:
+ case numeric_scale_code:
+ break;
+ case index_of_register_code:
+ case index_of_character_code:
+ break;
+ case last_chk_num_code:
+ case last_chk_dim_code:
+ break;
+ /* these read a glue or muglue, todo */
+ case mu_to_glue_code:
+ case glue_to_mu_code:
+ case glue_stretch_order_code:
+ case glue_shrink_order_code:
+ case glue_stretch_code:
+ case glue_shrink_code:
+ break;
+ /* these read a fontid and a char, todo */
+ case font_id_code:
+ case glyph_x_scaled_code:
+ case glyph_y_scaled_code:
+ /* these read a font, todo */
+ case font_spec_id_code:
+ case font_spec_scale_code:
+ case font_spec_xscale_code:
+ case font_spec_yscale_code:
+ /* these need a spec, todo */
+ break;
+ case font_char_wd_code:
+ case font_char_ht_code:
+ case font_char_dp_code:
+ case font_char_ic_code:
+ case font_char_ta_code:
+ /* these read a char, todo */
+ break;
+ case font_size_code:
+ lua_pushinteger(L, font_size(cur_font_par));
+ break;
+ case font_math_control_code:
+ lua_pushinteger(L, font_mathcontrol(cur_font_par));
+ break;
+ case font_text_control_code:
+ lua_pushinteger(L, font_textcontrol(cur_font_par));
+ break;
+ case math_scale_code:
+ break;
+ case math_style_code:
+ {
+ int style = tex_current_math_style();
+ if (style >= 0) {
+ lua_pushinteger(L, style);
+ return 1;
+ } else {
+ break;
+ }
+ }
+ /* these read a char, todo */
+ case math_main_style_code:
+ {
+ int style = tex_current_math_main_style();
+ if (style >= 0) {
+ lua_pushinteger(L, style);
+ return 1;
+ } else {
+ break;
+ }
+ }
+ /* these read a char, todo */
+ case math_char_class_code:
+ case math_char_fam_code:
+ case math_char_slot_code:
+ break;
+ case last_arguments_code:
+ lua_pushinteger(L, lmt_expand_state.arguments);
+ return 1;
+ case parameter_count_code:
+ lua_pushinteger(L, tex_get_parameter_count());
+ return 1;
+ /* case lua_value_function_code: */
+ /* break; */
+ case insert_progress_code:
+ break;
+ /* these read an integer, todo */
+ case left_margin_kern_code:
+ case right_margin_kern_code:
+ break;
+ case par_shape_length_code:
+ case par_shape_indent_code:
+ case par_shape_dimen_code:
+ break;
+ case lastpenalty_code:
+ case lastkern_code:
+ case lastskip_code:
+ case lastboundary_code:
+ case last_node_type_code:
+ case last_node_subtype_code:
+ case input_line_no_code:
+ case badness_code:
+ case overshoot_code:
+ case luatex_version_code:
+ case luatex_revision_code:
+ case current_group_level_code:
+ case current_group_type_code:
+ case current_if_level_code:
+ case current_if_type_code:
+ case current_if_branch_code:
+ return texlib_aux_scan_internal(L, some_item_cmd, code, -1);
+ case last_left_class_code:
+ lua_pushinteger(L, lmt_math_state.last_left);
+ return 1;
+ case last_right_class_code:
+ lua_pushinteger(L, lmt_math_state.last_right);
+ return 1;
+ case last_atom_class_code:
+ lua_pushinteger(L, lmt_math_state.last_atom);
+ return 1;
+ case current_loop_iterator_code:
+ case last_loop_iterator_code:
+ lua_pushinteger(L, lmt_main_control_state.loop_iterator);
+ return 1;
+ case current_loop_nesting_code:
+ lua_pushinteger(L, lmt_main_control_state.loop_nesting);
+ return 1;
+ case last_par_context_code:
+ lua_pushinteger(L, lmt_main_control_state.last_par_context);
+ return 1;
+ case last_page_extra_code:
+ lua_pushinteger(L, lmt_page_builder_state.last_extra_used);
+ return 1;
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_setmath(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 3) {
+ quarterword level;
+ int slot = lmt_check_for_level(L, 1, &level, cur_level);
+ int param = lmt_get_math_parameter(L, slot++, -1);
+ int style = lmt_get_math_style(L, slot++, -1);
+ if (param < 0 || style < 0) {
+ /* invalid spec, just ignore it */
+ } else {
+ switch (math_parameter_value_type(param)) {
+ case math_int_parameter:
+ case math_dimen_parameter:
+ case math_style_parameter:
+ tex_def_math_parameter(style, param, (scaled) lmt_optroundnumber(L, slot, 0), level, indirect_math_regular);
+ break;
+ case math_muglue_parameter:
+ {
+ halfword p = tex_copy_node(zero_glue);
+ glue_amount(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_stretch(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_shrink(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_stretch_order(p) = lmt_optroundnumber(L, slot++, 0);
+ glue_shrink_order(p) = lmt_optroundnumber(L, slot, 0);
+ tex_def_math_parameter(style, param, (scaled) p, level, indirect_math_regular);
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_getmath(lua_State *L)
+{
+ if (lua_gettop(L) == 2) {
+ int param = lmt_get_math_parameter(L, 1, -1);
+ int style = lmt_get_math_style(L, 2, -1);
+ if (param >= 0 && style >= 0) {
+ scaled value = tex_get_math_parameter(style, param, NULL);
+ if (value != undefined_math_parameter) {
+ switch (math_parameter_value_type(param)) {
+ case math_int_parameter:
+ case math_dimen_parameter:
+ case math_style_parameter:
+ lua_pushinteger(L, value);
+ return 1;
+ case math_muglue_parameter:
+ if (value <= thick_mu_skip_code) {
+ value = glue_parameter(value);
+ }
+ lua_pushinteger(L, glue_amount(value));
+ lua_pushinteger(L, glue_stretch(value));
+ lua_pushinteger(L, glue_shrink(value));
+ lua_pushinteger(L, glue_stretch_order(value));
+ lua_pushinteger(L, glue_shrink_order(value));
+ return 5;
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/*tex
+
+ This one is purely for diagnostic pusposed as normally there is some scaling
+ involved related to the current style and such.
+
+*/
+
+static int texlib_getfontname(lua_State *L)
+{
+ return texlib_aux_convert(L, font_name_code);
+}
+
+static int texlib_getfontidentifier(lua_State *L)
+{
+ return texlib_aux_convert(L, font_identifier_code);
+}
+
+static int texlib_getfontoffamily(lua_State *L)
+{
+ int f = lmt_checkinteger(L, 1);
+ int s = lmt_optinteger(L, 2, 0); /* this should be a multiple of 256 ! */
+ lua_pushinteger(L, tex_fam_fnt(f, s));
+ return 1;
+}
+
+static int texlib_getnumber(lua_State *L)
+{
+ return texlib_aux_convert(L, number_code); /* check */
+}
+
+// static int texlib_getdimension(lua_State *L)
+// {
+// return texlib_aux_convert(L, to_dimension_code); /* check */
+// }
+
+static int texlib_getromannumeral(lua_State *L)
+{
+ return texlib_aux_convert(L, roman_numeral_code);
+}
+
+static int texlib_get_internal(lua_State *L, int index, int all)
+{
+ if (lua_type(L, index) == LUA_TSTRING) {
+ size_t l;
+ const char *s = lua_tolstring(L, index, &l);
+ if (l == 0) {
+ return 0;
+ } else if (lua_key_eq(s, prevdepth)) {
+ lua_pushinteger(L, cur_list.prev_depth);
+ return 1;
+ } else if (lua_key_eq(s, prevgraf)) {
+ lua_pushinteger(L, cur_list.prev_graf);
+ return 1;
+ } else if (lua_key_eq(s, spacefactor)) {
+ lua_pushinteger(L, cur_list.space_factor);
+ return 1;
+ } else {
+ /*tex
+ We no longer get the info from the primitives hash but use the current
+ primitive meaning.
+ */ /*
+ int ts = maketexlstring(s, l);
+ int cs = prim_lookup(ts);
+ flush_str(ts);
+ if (cs > 0) {
+ int cs = string_locate(s, l, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = get_prim_eq_type(cs);
+ int code = get_prim_equiv(cs);
+ */
+ int cs = tex_string_locate(s, l, 0);
+ if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
+ int cmd = eq_type(cs);
+ int code = eq_value(cs);
+ switch (cmd) {
+ case some_item_cmd:
+ return texlib_aux_someitem(L, code);
+ case convert_cmd:
+ return texlib_aux_convert(L, code);
+ case internal_toks_cmd:
+ case register_toks_cmd:
+ case internal_int_cmd:
+ case register_int_cmd:
+ case internal_attribute_cmd:
+ case register_attribute_cmd:
+ case internal_dimen_cmd:
+ case register_dimen_cmd:
+ case lua_value_cmd:
+ case iterator_value_cmd:
+ case set_auxiliary_cmd:
+ case set_page_property_cmd:
+ case char_given_cmd:
+ // case math_char_given_cmd:
+ case integer_cmd:
+ case dimension_cmd:
+ case gluespec_cmd:
+ case mugluespec_cmd:
+ case mathspec_cmd:
+ case fontspec_cmd:
+ return texlib_aux_scan_internal(L, cmd, code, -1);
+ case internal_glue_cmd:
+ case register_glue_cmd:
+ case internal_mu_glue_cmd:
+ case register_mu_glue_cmd:
+ return texlib_aux_scan_internal(L, cmd, code, all);
+ case set_specification_cmd:
+ return lmt_push_specification(L, specification_parameter(internal_specification_number(code)), all); /* all == countonly */
+ default:
+ /* tex_formatted_warning("tex.get", "ignoring cmd %i: %s\n", cmd, s); */
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_get(lua_State *L)
+{
+ /* stack: key [boolean] */
+ int ret = texlib_get_internal(L, 1, (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : -1);
+ if (ret) {
+ return ret;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int texlib_index(lua_State *L)
+{
+ /* stack: table key */
+ int ret = texlib_get_internal(L, 2, -1);
+ if (ret) {
+ return ret;
+ } else {
+ lua_rawget(L, 1);
+ return 1;
+ }
+}
+
+static int texlib_getlist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ lua_pushnil(L);
+ } else if (lua_key_eq(s, pageinserthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_insert_list_type, NULL));
+ } else if (lua_key_eq(s, contributehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(contribute_list_type, NULL));
+ } else if (lua_key_eq(s, pagehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_list_type, NULL));
+ } else if (lua_key_eq(s, temphead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(temp_list_type, NULL));
+ } else if (lua_key_eq(s, holdhead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(hold_list_type, NULL));
+ } else if (lua_key_eq(s, postadjusthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(post_adjust_list_type, NULL));
+ } else if (lua_key_eq(s, preadjusthead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(pre_adjust_list_type, NULL));
+ } else if (lua_key_eq(s, postmigratehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(post_migrate_list_type, NULL));
+ } else if (lua_key_eq(s, premigratehead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(pre_migrate_list_type, NULL));
+ } else if (lua_key_eq(s, alignhead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(align_list_type, NULL));
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(page_discards_list_type, NULL));
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ lmt_push_node_fast(L, tex_get_special_node_list(split_discards_list_type, NULL));
+ } else if (lua_key_eq(s, bestpagebreak)) {
+ lmt_push_node_fast(L, lmt_page_builder_state.best_break);
+ } else if (lua_key_eq(s, leastpagecost)) {
+ lua_pushinteger(L, lmt_page_builder_state.least_cost);
+ } else if (lua_key_eq(s, bestsize)) {
+ lua_pushinteger(L, lmt_page_builder_state.best_size); /* is pagegoal but can be unset and also persistent */
+ } else if (lua_key_eq(s, insertpenalties)) {
+ lua_pushinteger(L, lmt_page_builder_state.insert_penalties);
+ } else if (lua_key_eq(s, insertheights)) {
+ lua_pushinteger(L, lmt_page_builder_state.insert_heights);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* todo: accept direct node too */
+
+static int texlib_setlist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ /* This is silently ignored */
+ } else if (lua_key_eq(s, bestsize)) {
+ lmt_page_builder_state.best_size = lmt_toscaled(L, 2); /* is pagegoal but can be unset and also persistent */
+ } else if (lua_key_eq(s, leastpagecost)) {
+ lmt_page_builder_state.least_cost = lmt_tointeger(L, 2);
+ } else if (lua_key_eq(s, insertpenalties)) {
+ lmt_page_builder_state.insert_penalties = lmt_tointeger(L, 2);
+ } else if (lua_key_eq(s, insertheights)) {
+ lmt_page_builder_state.insert_heights = lmt_tointeger(L, 2);
+ } else {
+ halfword n = null;
+ if (! lua_isnil(L, 2)) {
+ n = lmt_check_isnode(L, 2);
+ }
+ if (lua_key_eq(s, pageinserthead)) {
+ tex_set_special_node_list(page_insert_list_type, n);
+ } else if (lua_key_eq(s, contributehead)) {
+ tex_set_special_node_list(contribute_list_type, n);
+ } else if (lua_key_eq(s, pagehead)) {
+ tex_set_special_node_list(page_list_type, n);
+ } else if (lua_key_eq(s, temphead)) {
+ tex_set_special_node_list(temp_list_type, n);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ tex_set_special_node_list(page_discards_list_type, n);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ tex_set_special_node_list(split_discards_list_type, n);
+ } else if (lua_key_eq(s, holdhead)) {
+ tex_set_special_node_list(hold_list_type, n);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ tex_set_special_node_list(post_adjust_list_type, n);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ tex_set_special_node_list(pre_adjust_list_type, n);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ tex_set_special_node_list(post_migrate_list_type, n);
+ } else if (lua_key_eq(s, premigratehead)) {
+ tex_set_special_node_list(pre_migrate_list_type, n);
+ } else if (lua_key_eq(s, alignhead)) {
+ tex_set_special_node_list(align_list_type, n);
+ } else if (lua_key_eq(s, bestpagebreak)) {
+ lmt_page_builder_state.best_break = n;
+ }
+ }
+ return 0;
+}
+
+static void texlib_get_nest_field(lua_State *L, const char *field, list_state_record *r)
+{
+
+ if (lua_key_eq(field, mode)) {
+ lua_pushinteger(L, r->mode);
+ } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
+ /* we no longer check for special list nodes here so beware of prev-of-head */
+ lmt_push_node_fast(L, r->head);
+ } else if (lua_key_eq(field, tail)) {
+ /* we no longer check for special list nodes here so beware of next-of-tail */
+ lmt_push_node_fast(L, r->tail);
+ } else if (lua_key_eq(field, delimiter)) {
+ lmt_push_node_fast(L, r->delim);
+ } else if (lua_key_eq(field, prevgraf)) {
+ lua_pushinteger(L, r->prev_graf);
+ } else if (lua_key_eq(field, modeline)) {
+ lua_pushinteger(L, r->mode_line);
+ } else if (lua_key_eq(field, prevdepth)) {
+ lua_pushinteger(L, r->prev_depth);
+ } else if (lua_key_eq(field, spacefactor)) {
+ lua_pushinteger(L, r->space_factor);
+ } else if (lua_key_eq(field, noad)) {
+ lmt_push_node_fast(L, r->incomplete_noad);
+ } else if (lua_key_eq(field, direction)) {
+ lmt_push_node_fast(L, r->direction_stack);
+ } else if (lua_key_eq(field, mathdir)) {
+ lua_pushboolean(L, r->math_dir);
+ } else if (lua_key_eq(field, mathstyle)) {
+ lua_pushinteger(L, r->math_style);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void texlib_set_nest_field(lua_State *L, int n, const char *field, list_state_record *r)
+{
+ if (lua_key_eq(field, mode)) {
+ r->mode = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
+ r->head = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, tail)) {
+ r->tail = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, delimiter)) {
+ r->delim = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, prevgraf)) {
+ r->prev_graf = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, modeline)) {
+ r->mode_line = lmt_tointeger(L, n);
+ } else if (lua_key_eq(field, prevdepth)) {
+ r->prev_depth = lmt_toroundnumber(L, n);
+ } else if (lua_key_eq(field, spacefactor)) {
+ r->space_factor = lmt_toroundnumber(L, n);
+ } else if (lua_key_eq(field, noad)) {
+ r->incomplete_noad = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, direction)) {
+ r->direction_stack = lmt_check_isnode(L, n);
+ } else if (lua_key_eq(field, mathdir)) {
+ r->math_dir = lua_toboolean(L, n);
+ } else if (lua_key_eq(field, mathstyle)) {
+ r->math_style = lmt_tointeger(L, n);
+ }
+}
+
+static int texlib_aux_nest_getfield(lua_State *L)
+{
+ list_state_record **rv = lua_touserdata(L, -2);
+ list_state_record *r = *rv;
+ const char *field = lua_tostring(L, -1);
+ texlib_get_nest_field(L, field, r);
+ return 1;
+}
+
+static int texlib_aux_nest_setfield(lua_State *L)
+{
+ list_state_record **rv = lua_touserdata(L, -3);
+ list_state_record *r = *rv;
+ const char *field = lua_tostring(L, -2);
+ texlib_set_nest_field(L, -1, field, r);
+ return 0;
+}
+
+static const struct luaL_Reg texlib_nest_metatable[] = {
+ { "__index", texlib_aux_nest_getfield },
+ { "__newindex", texlib_aux_nest_setfield },
+ { NULL, NULL },
+};
+
+static void texlib_aux_init_nest_lib(lua_State *L)
+{
+ luaL_newmetatable(L, TEX_NEST_INSTANCE);
+ luaL_setfuncs(L, texlib_nest_metatable, 0);
+ lua_pop(L, 1);
+}
+
+/* getnest(<number>|top|ptr,[fieldname]) */
+
+static int texlib_getnest(lua_State *L)
+{
+ int p = -1 ;
+ int t = lua_gettop(L);
+ if (t == 0) {
+ p = lmt_nest_state.nest_data.ptr;
+ } else {
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ int ptr = lmt_tointeger(L, 1);
+ if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
+ p = ptr;
+ }
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ p = lmt_nest_state.nest_data.ptr;
+ } else if (lua_key_eq(s, ptr)) {
+ lua_pushinteger(L, lmt_nest_state.nest_data.ptr);
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ if (p > -1) {
+ if (t > 1) {
+ const char *field = lua_tostring(L, 2);
+ if (field) {
+ texlib_get_nest_field(L, field, &lmt_nest_state.nest[p]);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ list_state_record **nestitem = lua_newuserdatauv(L, sizeof(list_state_record *), 0);
+ *nestitem = &lmt_nest_state.nest[p];
+ luaL_getmetatable(L, TEX_NEST_INSTANCE);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* setnest(<number>|top,fieldname,value) */
+
+static int texlib_setnest(lua_State *L)
+{
+ if (lua_gettop(L) > 2) {
+ int p = -1 ;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ int ptr = lmt_tointeger(L, 1);
+ if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
+ p = ptr;
+ }
+ }
+ break;
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, top)) {
+ p = lmt_nest_state.nest_data.ptr;
+ }
+ }
+ break;
+ }
+ if (p > -1) {
+ const char *field = lua_tostring(L, 2);
+ if (field) {
+ texlib_set_nest_field(L, 3, field, &lmt_nest_state.nest[p]);
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_round(lua_State *L)
+{
+ /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1))); */
+ lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1)));
+ return 1;
+}
+
+static int texlib_scale(lua_State *L)
+{
+ double delta = luaL_checknumber(L, 2);
+ switch (lua_type(L, 1)) {
+ case LUA_TTABLE:
+ {
+ /*tex
+ We could preallocate the table or maybe scale in-place. The
+ new table is at index 3.
+ */
+ lua_newtable(L);
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ /*tex We have a numeric value. */
+ lua_pushvalue(L, -2);
+ lua_insert(L, -2);
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ double m = (double) lua_tonumber(L, -1) * delta;
+ lua_pop(L, 1);
+ /* lua_pushinteger(L, lmt_roundedfloat(m)); */
+ lua_pushinteger(L, clippedround(m));
+ }
+ lua_rawset(L, 3);
+ }
+ }
+ break;
+ case LUA_TNUMBER:
+ /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1) * delta)); */
+ lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1) * delta));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ return 1;
+}
+
+/*tex
+ For compatibility reasons we keep the check for a boolean for a while. For consistency
+ we now support flags too: |global cs id|.
+
+*/
+
+static int texlib_definefont(lua_State *L)
+{
+ size_t l;
+ int slot = 1;
+ int flags = (lua_isboolean(L, slot) && lua_toboolean(L, slot++)) ? add_global_flag(0) : 0;
+ const char *csname = lua_tolstring(L, slot++, &l);
+ halfword id = lmt_tohalfword(L, slot++);
+ int cs = tex_string_locate(csname, l, 1);
+ lmt_check_for_flags(L, slot, &flags, 1, 1);
+ tex_define(flags, cs, set_font_cmd, id);
+ return 0;
+}
+
+static int texlib_hashtokens(lua_State *L)
+{
+ int cs = 1;
+ int nt = 0;
+ int nx = 0;
+ int all = lua_toboolean(L, 1);
+ lua_createtable(L, hash_size, 0);
+ if (all) {
+ while (cs <= hash_size) {
+ /* because strings never get freed we can as well directly access |s|. */
+ strnumber s = cs_text(cs);
+ if (s > 0) {
+ halfword n = cs_next(cs);
+ char *ss = tex_makecstring(s);
+ if (n) {
+ int mt = 0;
+ lua_createtable(L, 2, 0);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ ++nt;
+ lua_rawseti(L, -2, ++mt);
+ while (n) {
+ s = cs_text(n);
+ if (s) {
+ ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++mt);
+ ++nt;
+ ++nx;
+ }
+ n = cs_next(n);
+ }
+ } else {
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ ++nt;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ lua_rawseti(L, -2, cs);
+ cs++;
+ }
+ } else {
+ while (cs < hash_size) {
+ strnumber s = cs_text(cs);
+ if (s > 0) {
+ halfword n = cs_next(cs);
+ char *ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ while (n) {
+ s = cs_text(n);
+ if (s) {
+ ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ ++nx;
+ }
+ n = cs_next(n);
+ }
+ }
+ cs++;
+ }
+ }
+ lua_pushinteger(L, --cs);
+ lua_pushinteger(L, nt);
+ lua_pushinteger(L, nx);
+ return 4;
+}
+
+static int texlib_primitives(lua_State *L)
+{
+ int cs = 0;
+ int nt = 0;
+ 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);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ }
+ cs++;
+ }
+ return 1;
+}
+
+static int texlib_extraprimitives(lua_State *L)
+{
+ int mask = 0;
+ int cs = 0;
+ int nt = 0;
+ int n = lua_gettop(L);
+ if (n == 0) {
+ mask = tex_command + etex_command + luatex_command;
+ } else {
+ for (int i = 1; i <= n; i++) {
+ if (lua_type(L, i) == LUA_TSTRING) {
+ const char *s = lua_tostring(L, i);
+ if (lua_key_eq(s, tex)) {
+ mask |= tex_command;
+ } else if (lua_key_eq(s, etex)) {
+ mask |= etex_command;
+ } else if (lua_key_eq(s, luatex)) {
+ mask |= luatex_command;
+ }
+ }
+ }
+ }
+ lua_createtable(L, prim_size, 0);
+ while (cs < prim_size) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0 && (get_prim_origin(cs) & mask)) {
+ char *ss = tex_makecstring(s);
+ lua_pushstring(L, ss);
+ lmt_memory_free(ss);
+ lua_rawseti(L, -2, ++nt);
+ }
+ cs++;
+ }
+ return 1;
+}
+
+static void texlib_aux_enableprimitive(const char *pre, size_t prel, const char *prm)
+{
+ strnumber s = tex_maketexstring(prm);
+ halfword prm_val = tex_prim_lookup(s);
+ tex_flush_str(s);
+ if (prm_val != undefined_primitive && get_prim_origin(prm_val) != no_command) {
+ char *newprm;
+ size_t newlen;
+ halfword cmd = get_prim_eq_type(prm_val);
+ halfword chr = get_prim_equiv(prm_val);
+ if (strncmp(pre, prm, prel) != 0) {
+ /* not a prefix */
+ newlen = strlen(prm) + prel;
+ newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
+ if (newprm) {
+ strcpy(newprm, pre);
+ strcat(newprm + prel, prm);
+ } else {
+ tex_overflow_error("primitives", (int) newlen + 1);
+ }
+ } else {
+ newlen = strlen(prm);
+ newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
+ if (newprm) {
+ strcpy(newprm, prm);
+ } else {
+ tex_overflow_error("primitives", (int) newlen + 1);
+ }
+ }
+ if (newprm) {
+ halfword val = tex_string_locate(newprm, newlen, 1);
+ if (val == undefined_control_sequence || eq_type(val) == undefined_cs_cmd) {
+ tex_primitive_def(newprm, newlen, (singleword) cmd, chr);
+ }
+ lmt_memory_free(newprm);
+ }
+ }
+}
+
+static int texlib_enableprimitives(lua_State *L)
+{
+ if (lua_gettop(L) == 2) {
+ size_t lpre;
+ const char *pre = luaL_checklstring(L, 1, &lpre);
+ switch (lua_type(L, 2)) {
+ case LUA_TTABLE:
+ {
+ int i = 1;
+ while (1) {
+ if (lua_rawgeti(L, 2, i) == LUA_TSTRING) {
+ const char *prm = lua_tostring(L, 3);
+ texlib_aux_enableprimitive(pre, lpre, prm);
+ } else {
+ lua_pop(L, 1);
+ break;
+ }
+ lua_pop(L, 1);
+ i++;
+ }
+ }
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ for (int cs = 0; cs < prim_size; cs++) {
+ strnumber s = get_prim_text(cs);
+ if (s > 0) {
+ /* there is actually no need to copy */
+ char *prm = tex_makecstring(s);
+ texlib_aux_enableprimitive(pre, lpre, prm);
+ lmt_memory_free(prm);
+ }
+ }
+ }
+ break;
+ default:
+ luaL_error(L, "array of names or 'true' expected");
+ }
+ } else {
+ luaL_error(L, "wrong number of arguments");
+ }
+ return 0;
+}
+
+/*tex penalties */
+
+static halfword texlib_topenalties(lua_State *L, int i, quarterword s)
+{
+ int n = 0;
+ lua_pushnil(L);
+ while (lua_next(L, i)) {
+ n++;
+ lua_pop(L, 1);
+ }
+ if (n > 0) {
+ int j = 0;
+ halfword p = tex_new_specification_node(n, s, 0); /* todo: repeat */
+ lua_pushnil(L);
+ while (lua_next(L, i)) {
+ j++;
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ tex_set_specification_penalty(p, j, lmt_tohalfword(L, -1));
+ }
+ lua_pop(L, 1);
+ }
+ return p;
+ } else {
+ return null;
+ }
+}
+
+/*tex We should check for proper glue spec nodes ... todo. */
+
+# define get_dimen_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_glue_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TUSERDATA) ? lmt_check_isnode(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_integer_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_tohalfword(L, -1) : B; \
+ lua_pop(L, 1);
+
+# define get_penalties_par(P,A,B,C) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_topenalties(L, lua_gettop(L), C) : B; \
+ lua_pop(L, 1);
+
+# define get_shape_par(P,A,B) \
+ lua_push_key(A); \
+ P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_toparshape(L, lua_gettop(L)) : B; \
+ lua_pop(L, 1);
+
+/*tex
+ The next function needs to be kept in sync with the regular linebreak handler, wrt the special
+ skips. This one can be called from within the callback so then we already have intialized.
+*/
+
+
+/* par leftinit rightinit leftindent ... leftfill rightfill */
+
+static int texlib_preparelinebreak(lua_State *L)
+{
+ halfword direct;
+ halfword par = lmt_check_isdirectornode(L, 1, &direct);
+ if (node_type(par) == par_node) {
+ halfword tail = tex_tail_of_node_list(par);
+ if (node_type(tail) == glue_node && node_subtype(tail) == par_fill_right_skip_glue) {
+ tex_formatted_warning("linebreak", "list seems already prepared");
+ } else {
+ halfword parinit_left_skip_glue = null;
+ halfword parinit_right_skip_glue = null;
+ halfword parfill_left_skip_glue = null;
+ halfword parfill_right_skip_glue = null;
+ halfword final_penalty = null;
+ tex_line_break_prepare(par, &tail, &parinit_left_skip_glue, &parinit_right_skip_glue, &parfill_left_skip_glue, &parfill_right_skip_glue, &final_penalty);
+ lmt_push_directornode(L, par, direct);
+ lmt_push_directornode(L, tail, direct);
+ lmt_push_directornode(L, parinit_left_skip_glue, direct);
+ lmt_push_directornode(L, parinit_right_skip_glue, direct);
+ lmt_push_directornode(L, parfill_left_skip_glue , direct);
+ lmt_push_directornode(L, parfill_right_skip_glue, direct);
+ /* lmt_push_directornode(L, final_penalty, direct); */ /*tex Not that relevant to know. */
+ return 6;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_linebreak(lua_State *L)
+{
+ // halfword par = lmt_check_isnode(L, 1);
+ halfword direct;
+ halfword par = lmt_check_isdirectornode(L, 1, &direct);
+ if (node_type(par) == par_node) {
+ line_break_properties properties;
+ halfword tail = par;
+ halfword has_indent = null;
+ halfword has_penalty = 0;
+ halfword prepared = 0;
+ properties.initial_par = par;
+ properties.display_math = 0;
+ properties.paragraph_dir = par_dir(par);
+ properties.parfill_left_skip = null;
+ properties.parfill_right_skip = null;
+ properties.parinit_left_skip = null;
+ properties.parinit_right_skip = null;
+ while (tail) {
+ switch (node_type(tail)) {
+ case glue_node:
+ switch (node_subtype(tail)) {
+ case indent_skip_glue:
+ if (has_indent) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "indent");
+ goto NOTHING;
+ } else {
+ has_indent = 1;
+ }
+ break;
+ case par_fill_left_skip_glue:
+ if (properties.parfill_left_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftskip");
+ goto NOTHING;
+ } else {
+ properties.parfill_left_skip = tail;
+ }
+ break;
+ case par_fill_right_skip_glue:
+ if (properties.parfill_right_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightskip");
+ goto NOTHING;
+ } else {
+ properties.parfill_right_skip = tail;
+ }
+ break;
+ case par_init_left_skip_glue:
+ if (properties.parinit_left_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftinit");
+ goto NOTHING;
+ } else {
+ properties.parinit_left_skip = tail;
+ }
+ break;
+ case par_init_right_skip_glue:
+ if (properties.parinit_right_skip) {
+ tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightinit");
+ goto NOTHING;
+ } else {
+ properties.parinit_right_skip = tail;
+ }
+ break;
+ }
+ break;
+ case penalty_node:
+ if (node_subtype(tail) == line_penalty_subtype && penalty_amount(tail) == infinite_penalty && ! (properties.parfill_left_skip && properties.parfill_right_skip)) {
+ has_penalty = 1;
+ }
+ }
+ if (node_next(tail)) {
+ tail = node_next(tail);
+ } else {
+ break;
+ }
+ }
+ {
+ int has_init = properties.parinit_left_skip && properties.parinit_right_skip;
+ int has_fill = properties.parfill_left_skip && properties.parfill_right_skip;
+ if (lmt_linebreak_state.calling_back) {
+ if (has_indent && ! (has_init && has_fill && has_penalty)) {
+ tex_formatted_warning("linebreak", "[ par + leftinit + rightinit + indentglue + ... + penalty + leftfill + righfill ] expected");
+ goto NOTHING;
+ } else if (! (has_fill && has_penalty)) {
+ tex_formatted_warning("linebreak", "[ par + indentbox + ... + penalty + leftfill + righfill ] expected");
+ goto NOTHING;
+ } else {
+ prepared = 1;
+ }
+ } else {
+ if (! (has_indent && has_init && has_fill)) {
+ tex_formatted_warning("linebreak", "[ leftinit | rightinit | leftfill | rigthfill ] expected");
+ goto NOTHING;
+ } else {
+ // prepared = 0;
+ prepared = has_init && has_fill;
+ }
+ }
+ }
+ tex_push_nest();
+ node_next(temp_head) = par;
+ /*tex initialize local parameters */
+ if (lua_gettop(L) != 2 || lua_type(L, 2) != LUA_TTABLE) {
+ lua_newtable(L);
+ }
+ lua_push_key(direction);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ properties.paragraph_dir = checked_direction_value(lmt_tointeger(L, -1));
+ }
+ lua_pop(L, 1);
+ get_integer_par (properties.tracing_paragraphs, tracingparagraphs, tracing_paragraphs_par);
+ get_integer_par (properties.pretolerance, pretolerance, tex_get_par_par(par, par_pre_tolerance_code));
+ get_integer_par (properties.tolerance, tolerance, tex_get_par_par(par, par_tolerance_code));
+ get_dimen_par (properties.emergency_stretch, emergencystretch, tex_get_par_par(par, par_emergency_stretch_code));
+ get_integer_par (properties.looseness, looseness, tex_get_par_par(par, par_looseness_code));
+ get_integer_par (properties.adjust_spacing, adjustspacing, tex_get_par_par(par, par_adjust_spacing_code));
+ get_integer_par (properties.protrude_chars, protrudechars, tex_get_par_par(par, par_protrude_chars_code));
+ get_integer_par (properties.adj_demerits, adjdemerits, tex_get_par_par(par, par_adj_demerits_code));
+ get_integer_par (properties.line_penalty, linepenalty, tex_get_par_par(par, par_line_penalty_code));
+ get_integer_par (properties.last_line_fit, lastlinefit, tex_get_par_par(par, par_last_line_fit_code));
+ get_integer_par (properties.double_hyphen_demerits, doublehyphendemerits, tex_get_par_par(par, par_double_hyphen_demerits_code));
+ get_integer_par (properties.final_hyphen_demerits, finalhyphendemerits, tex_get_par_par(par, par_final_hyphen_demerits_code));
+ get_dimen_par (properties.hsize, hsize, tex_get_par_par(par, par_hsize_code));
+ get_glue_par (properties.left_skip, leftskip, tex_get_par_par(par, par_left_skip_code));
+ get_glue_par (properties.right_skip, rightskip, tex_get_par_par(par, par_right_skip_code));
+ get_dimen_par (properties.hang_indent, hangindent, tex_get_par_par(par, par_hang_indent_code));
+ get_integer_par (properties.hang_after, hangafter, tex_get_par_par(par, par_hang_after_code));
+ get_integer_par (properties.inter_line_penalty, interlinepenalty, tex_get_par_par(par, par_inter_line_penalty_code));
+ get_integer_par (properties.club_penalty, clubpenalty, tex_get_par_par(par, par_club_penalty_code));
+ get_integer_par (properties.widow_penalty, widowpenalty, tex_get_par_par(par, par_widow_penalty_code));
+ get_integer_par (properties.display_widow_penalty, displaywidowpenalty, tex_get_par_par(par, par_display_widow_penalty_code));
+ get_integer_par (properties.orphan_penalty, orphanpenalty, tex_get_par_par(par, par_orphan_penalty_code));
+ get_integer_par (properties.broken_penalty, brokenpenalty, tex_get_par_par(par, par_broken_penalty_code));
+ get_glue_par (properties.baseline_skip, baselineskip, tex_get_par_par(par, par_baseline_skip_code));
+ get_glue_par (properties.line_skip, lineskip, tex_get_par_par(par, par_line_skip_code));
+ get_dimen_par (properties.line_skip_limit, lineskiplimit, tex_get_par_par(par, par_line_skip_limit_code));
+ get_integer_par (properties.adjust_spacing, adjustspacing, tex_get_par_par(par, par_adjust_spacing_code));
+ get_integer_par (properties.adjust_spacing_step, adjustspacingstep, tex_get_par_par(par, par_adjust_spacing_step_code));
+ get_integer_par (properties.adjust_spacing_shrink, adjustspacingshrink, tex_get_par_par(par, par_adjust_spacing_shrink_code));
+ get_integer_par (properties.adjust_spacing_stretch, adjustspacingstretch, tex_get_par_par(par, par_adjust_spacing_stretch_code));
+ get_integer_par (properties.hyphenation_mode, hyphenationmode, tex_get_par_par(par, par_hyphenation_mode_code));
+ get_integer_par (properties.shaping_penalties_mode, shapingpenaltiesmode, tex_get_par_par(par, par_shaping_penalties_mode_code));
+ get_integer_par (properties.shaping_penalty, shapingpenalty, tex_get_par_par(par, par_shaping_penalty_code));
+ get_shape_par (properties.par_shape, parshape, tex_get_par_par(par, par_par_shape_code));
+ get_penalties_par(properties.inter_line_penalties, interlinepenalties, tex_get_par_par(par, par_inter_line_penalties_code), inter_line_penalties_code);
+ get_penalties_par(properties.club_penalties, clubpenalties, tex_get_par_par(par, par_club_penalties_code), club_penalties_code);
+ get_penalties_par(properties.widow_penalties, widowpenalties, tex_get_par_par(par, par_widow_penalties_code), widow_penalties_code);
+ get_penalties_par(properties.display_widow_penalties,displaywidowpenalties,tex_get_par_par(par, par_display_widow_penalties_code), display_widow_penalties_code);
+ get_penalties_par(properties.orphan_penalties, orphanpenalties, tex_get_par_par(par, par_orphan_penalties_code), orphan_penalties_code);
+ if (! prepared) {
+ halfword attr_template = tail;
+ halfword final_penalty = tex_new_penalty_node(infinite_penalty, line_penalty_subtype);
+ /* */
+ get_glue_par(properties.parfill_left_skip, parfillleftskip, tex_get_par_par(par, par_par_fill_left_skip_code));
+ get_glue_par(properties.parfill_right_skip, parfillrightskip, tex_get_par_par(par, par_par_fill_right_skip_code));
+ get_glue_par(properties.parinit_left_skip, parinitleftskip, tex_get_par_par(par, par_par_init_left_skip_code));
+ get_glue_par(properties.parinit_right_skip, parinitrightskip, tex_get_par_par(par, par_par_init_right_skip_code));
+ /* */
+ properties.parfill_left_skip = tex_new_glue_node(properties.parfill_left_skip, par_fill_left_skip_glue);
+ properties.parfill_right_skip = tex_new_glue_node(properties.parfill_right_skip, par_fill_right_skip_glue);
+ tex_attach_attribute_list_copy(final_penalty, attr_template);
+ tex_attach_attribute_list_copy(properties.parfill_left_skip, attr_template);
+ tex_attach_attribute_list_copy(properties.parfill_right_skip, attr_template);
+ tex_couple_nodes(tail, final_penalty);
+ tex_couple_nodes(final_penalty, properties.parfill_left_skip);
+ tex_couple_nodes(properties.parfill_left_skip, properties.parfill_right_skip);
+ if (node_next(par)) { /* test can go, also elsewhere */
+ halfword n = node_next(par);
+ while (n) {
+ if (node_type(n) == glue_node && node_subtype(n) == indent_skip_glue) {
+ properties.parinit_left_skip = tex_new_glue_node(properties.parinit_left_skip, par_init_left_skip_glue);
+ properties.parinit_right_skip = tex_new_glue_node(properties.parinit_right_skip, par_init_right_skip_glue);
+ tex_attach_attribute_list_copy(properties.parinit_left_skip, attr_template); // maybe head .. also elsewhere
+ tex_attach_attribute_list_copy(properties.parinit_right_skip, attr_template); // maybe head .. also elsewhere
+ tex_try_couple_nodes(properties.parinit_right_skip, n);
+ tex_try_couple_nodes(properties.parinit_left_skip, properties.parinit_right_skip);
+ tex_try_couple_nodes(par, properties.parinit_left_skip);
+ break;
+ } else {
+ n = node_next(n);
+ }
+ }
+ }
+ }
+ lmt_linebreak_state.last_line_fill = properties.parfill_right_skip; /*tex I need to redo this. */
+ tex_do_line_break(&properties);
+ {
+ halfword fewest_demerits = 0;
+ halfword actual_looseness = 0;
+ /*tex return the generated list, and its prevdepth */
+ tex_get_linebreak_info(&fewest_demerits, &actual_looseness) ;
+ lmt_push_directornode(L, node_next(cur_list.head), direct);
+ lua_createtable(L, 0, 4);
+ /* set_integer_by_key(L, demerits, fewest_demerits); */
+ lua_push_key(demerits);
+ lua_pushinteger(L, fewest_demerits);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, looseness, actual_looseness); */
+ lua_push_key(looseness);
+ lua_pushinteger(L, actual_looseness);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, prevdepth, cur_list.prev_depth); */
+ lua_push_key(prevdepth);
+ lua_pushinteger(L, cur_list.prev_depth);
+ lua_settable(L, -3);
+ /* set_integer_by_key(L, prevgraf, cur_list.prev_graf); */
+ lua_push_key(prevgraf);
+ lua_pushinteger(L, cur_list.prev_graf);
+ lua_settable(L, -3);
+ }
+ tex_pop_nest();
+ if (properties.par_shape != tex_get_par_par(par, par_par_shape_code)) { tex_flush_node(properties.par_shape); }
+ if (properties.inter_line_penalties != tex_get_par_par(par, par_inter_line_penalties_code)) { tex_flush_node(properties.inter_line_penalties); }
+ if (properties.club_penalties != tex_get_par_par(par, par_club_penalties_code)) { tex_flush_node(properties.club_penalties); }
+ if (properties.widow_penalties != tex_get_par_par(par, par_widow_penalties_code)) { tex_flush_node(properties.widow_penalties); }
+ if (properties.display_widow_penalties != tex_get_par_par(par, par_display_widow_penalties_code)) { tex_flush_node(properties.display_widow_penalties); }
+ if (properties.orphan_penalties != tex_get_par_par(par, par_orphan_penalties_code)) { tex_flush_node(properties.orphan_penalties); }
+ return 2;
+ } else {
+ tex_formatted_warning("linebreak", "[ par ... ] expected");
+ }
+ NOTHING:
+ lmt_push_directornode(L, par, direct);
+ return 1;
+}
+
+static int texlib_resetparagraph(lua_State *L)
+{
+ (void) L;
+ tex_normal_paragraph(reset_par_context);
+ return 0;
+}
+
+static int texlib_shipout(lua_State *L)
+{
+ int boxnum = lmt_get_box_id(L, 1, 1);
+ if (box_register(boxnum)) {
+ tex_flush_node_list(box_register(boxnum));
+ box_register(boxnum) = null;
+ }
+ return 0;
+}
+
+static int texlib_badness(lua_State *L)
+{
+ scaled t = lmt_roundnumber(L, 1);
+ scaled s = lmt_roundnumber(L, 2);
+ lua_pushinteger(L, tex_badness(t, s));
+ return 1;
+}
+
+static int texlib_showcontext(lua_State *L)
+{
+ (void) L;
+ tex_show_context();
+ return 0;
+}
+
+/*tex
+ When we pass |true| the page builder will only be invoked in the main vertical list in which
+ case |lmt_nest_state.nest_data.ptr == 1| or |cur_list.mode != vmode|.
+*/
+
+static int texlib_triggerbuildpage(lua_State *L)
+{
+ if (lua_toboolean(L, 1) && cur_list.mode != vmode) {
+ return 0;
+ }
+ tex_build_page();
+ return 0;
+}
+
+static int texlib_getpagestate(lua_State *L)
+{
+ lua_pushinteger(L, lmt_page_builder_state.contents);
+ return 1;
+}
+
+static int texlib_getlocallevel(lua_State *L)
+{
+ lua_pushinteger(L, lmt_main_control_state.local_level);
+ return 1;
+}
+
+/* input state aka synctex */
+
+static int texlib_setinputstatemode(lua_State *L)
+{
+ input_file_state.mode = lmt_tohalfword(L, 1);
+ return 0;
+}
+static int texlib_getinputstatemode(lua_State *L)
+{
+ lua_pushinteger(L, input_file_state.mode);
+ return 1;
+}
+
+static int texlib_setinputstatefile(lua_State *L)
+{
+ lmt_input_state.cur_input.state_file = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_getinputstatefile(lua_State *L)
+{
+ lua_pushinteger(L, lmt_input_state.cur_input.state_file);
+ return 1;
+}
+
+static int texlib_forceinputstatefile(lua_State *L)
+{
+ input_file_state.forced_file = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_forceinputstateline(lua_State *L)
+{
+ input_file_state.forced_line = lmt_tointeger(L, 1);
+ return 0;
+}
+
+static int texlib_setinputstateline(lua_State *L)
+{
+ input_file_state.line = lmt_tohalfword(L, 1);
+ return 0;
+}
+
+static int texlib_getinputstateline(lua_State *L)
+{
+ lua_pushinteger(L, input_file_state.line);
+ return 1;
+}
+
+/*tex
+ This is experimental and might change. In version 10 we hope to have the final version available.
+ It actually took quite a bit of time to understand the implications of mixing lua prints in here.
+ The current variant is (so far) the most robust (wrt crashes and side effects).
+*/
+
+// # define mode cur_list.mode_field
+
+/*tex
+ When we add save levels then we can get crashes when one flushed bad groups due to out of order
+ flushing. So we play safe! But still we can have issues so best make sure you're in hmode.
+*/
+
+static int texlib_forcehmode(lua_State *L)
+{
+ if (abs(cur_list.mode) == vmode) {
+ if (lua_type(L, 1) == LUA_TBOOLEAN) {
+ tex_begin_paragraph(lua_toboolean(L, 1), force_par_begin);
+ } else {
+ tex_begin_paragraph(1, force_par_begin);
+ }
+ }
+ return 0;
+}
+
+/* tex
+ The first argument can be a number (of a token register), a macro name or the name of a token
+ list. The second argument is optional and when true forces expansion inside a definition. The
+ optional third argument can be used to force oing. The return value indicates an error: 0
+ means no error, 1 means that a bad register number has been passed, a value of 2 indicated an
+ unknown register or macro name, while 3 reports that the macro is not suitable for local
+ control because it takes arguments.
+*/
+
+static int texlib_runlocal(lua_State *L)
+{
+ // int obeymode = lua_toboolean(L, 4);
+ int obeymode = 1; /* always 1 */
+ halfword tok = -1;
+ int mac = 0 ;
+ switch (lua_type(L, 1)) {
+ case LUA_TFUNCTION:
+ {
+ /* todo: also a variant that calls an already registered function */
+ int ref;
+ halfword r, t;
+ lua_pushvalue(L, 1);
+ ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ r = tex_get_available_token(token_val(end_local_cmd, 0));
+ t = tex_get_available_token(token_val(lua_local_call_cmd, ref));
+ token_link(t) = r;
+ tex_begin_inserted_list(t);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering token scanner via function");
+ }
+ tex_local_control(obeymode);
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ return 0;
+ }
+ case LUA_TNUMBER:
+ {
+ halfword k = lmt_checkhalfword(L, 1);
+ if (k >= 0 && k <= 65535) {
+ tok = toks_register(k);
+ goto TOK;
+ } else {
+ tex_local_control_message("invalid token register number");
+ return 0;
+ }
+ }
+ case LUA_TSTRING:
+ {
+ size_t lname = 0;
+ const char *name = lua_tolstring(L, 1, &lname);
+ int cs = tex_string_locate(name, lname, 0);
+ int cmd = eq_type(cs);
+ if (cmd < call_cmd) { // is_call_cmd
+ // todo: use the better register helpers and range checkers
+ switch (cmd) {
+ case register_toks_cmd:
+ tok = toks_register(register_toks_number(eq_value(cs)));
+ goto TOK;
+ case undefined_cs_cmd:
+ tex_local_control_message("undefined macro or token register");
+ return 0;
+ default:
+ /* like cs == case undefined_control_sequence */
+ tex_local_control_message("invalid macro or token register");
+ return 0;
+ }
+ } else {
+ halfword ref = eq_value(cs);
+ halfword head = token_link(ref);
+ if (head && get_token_parameters(ref)) {
+ tex_local_control_message("macro takes arguments and is ignored");
+ return 0;
+ } else {
+ tok = cs_token_flag + cs;
+ mac = 1 ;
+ goto TOK;
+ }
+ }
+ }
+ case LUA_TUSERDATA:
+ /* no checking yet */
+ tok = token_info(lmt_token_code_from_lua(L, 1));
+ mac = 1;
+ goto TOK;
+ default:
+ return 0;
+ }
+ TOK:
+ if (tok < 0) {
+ /* nothing to do */
+ } else if (lmt_input_state.scanner_status != scanner_is_defining || lua_toboolean(L, 2)) {
+ // todo: make list
+ int grouped = lua_toboolean(L, 3);
+ if (grouped) {
+ tex_begin_inserted_list(tex_get_available_token(token_val(right_brace_cmd, 0)));
+ }
+ tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
+ if (mac) {
+ tex_begin_inserted_list(tex_get_available_token(tok));
+ } else {
+ tex_begin_token_list(tok, local_text);
+ }
+ if (grouped) {
+ tex_begin_inserted_list(tex_get_available_token(token_val(left_brace_cmd, 0)));
+ }
+ /*tex hm, needed here? */
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ if (mac) {
+ tex_local_control_message("entering token scanner via macro");
+ } else {
+ tex_local_control_message("entering token scanner via register");
+ }
+ }
+ tex_local_control(obeymode);
+ } else if (mac) {
+ tex_back_input(tok);
+ } else {
+ halfword h = null;
+ halfword t = null;
+ halfword r = token_link(tok);
+ while (r) {
+ t = tex_store_new_token(t, token_info(r));
+ if (! h) {
+ h = t;
+ }
+ r = token_link(r);
+ }
+ tex_begin_inserted_list(h);
+ }
+ return 0;
+}
+
+static int texlib_quitlocal(lua_State *L)
+{
+ (void) L;
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("quitting token scanner");
+ }
+ tex_end_local_control();
+ return 0;
+}
+
+/* todo: no tryagain and justincase here */
+
+static int texlib_expandasvalue(lua_State *L) /* mostly like the mp one */
+{
+ int kind = lmt_tointeger(L, 1);
+ halfword tail = null;
+ halfword head = lmt_macro_to_tok(L, 2, &tail);
+ if (head) {
+ switch (kind) {
+ case lua_value_none_code:
+ case lua_value_dimension_code:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_dimen(0, 0, 0, 0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (! lmt_error_state.last_intercept) {
+ lua_pushinteger(L, value);
+ break;
+ } else if (kind == lua_value_none_code) {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto TRYAGAIN;
+ } else {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto JUSTINCASE;
+ }
+ }
+ case lua_value_integer_code:
+ case lua_value_cardinal_code:
+ case lua_value_boolean_code:
+ TRYAGAIN:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_int(0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (lmt_error_state.last_intercept) {
+ head = lmt_macro_to_tok(L, 2, &tail);
+ goto JUSTINCASE;
+ } else if (kind == lua_value_boolean_code) {
+ lua_pushboolean(L, value);
+ break;
+ } else {
+ lua_pushinteger(L, value);
+ break;
+ }
+ }
+ default:
+ JUSTINCASE:
+ {
+ int len = 0;
+ const char *str = (const char *) lmt_get_expansion(head, &len);
+ lua_pushlstring(L, str, str ? len : 0); /* len includes \0 */
+ break;
+ }
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* string, expand-in-def, group */
+
+static int texlib_runstring(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ size_t lstr = 0;
+ const char *str = NULL;
+ 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, slot++, &lstr);
+ if (lstr > 0) {
+ int obeymode = 1; /* always 1 */
+ int expand = lua_toboolean(L, slot++);
+ int grouped = lua_toboolean(L, slot++);
+ int ignore = lua_toboolean(L, slot++);
+ halfword h = get_reference_token();
+ halfword t = h;
+ if (grouped) {
+ // t = tex_store_new_token(a, left_brace_token + '{');
+ t = tex_store_new_token(t, token_val(right_brace_cmd, 0));
+ }
+ /*tex Options: 1=create (will trigger an error), 2=ignore. */
+ tex_parse_str_to_tok(h, &t, ct, str, lstr, ignore ? 2 : 1);
+ if (grouped) {
+ // t = tex_store_new_token(a, left_brace_token + '}');
+ t = tex_store_new_token(t, token_val(left_brace_cmd, 0));
+ }
+ if (lmt_input_state.scanner_status != scanner_is_defining || expand) {
+ // t = tex_store_new_token(t, token_val(end_local_cmd, 0));
+ tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
+ tex_begin_token_list(h, local_text);
+ if (lmt_token_state.luacstrings > 0) {
+ tex_lua_string_start();
+ }
+ if (tracing_nesting_par > 2) {
+ tex_local_control_message("entering token scanner via register");
+ }
+ tex_local_control(obeymode);
+ } else {
+ tex_begin_inserted_list(h);
+ }
+ }
+ }
+ return 0;
+}
+
+/* new, can go into luatex too */
+
+static int texlib_getmathdir(lua_State *L)
+{
+ lua_pushinteger(L, math_direction_par);
+ return 0;
+}
+
+static int texlib_setmathdir(lua_State *L)
+{
+ tex_set_math_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_getpardir(lua_State *L)
+{
+ lua_pushinteger(L, par_direction_par);
+ return 1;
+}
+
+static int texlib_setpardir(lua_State *L)
+{
+ tex_set_par_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_gettextdir(lua_State *L)
+{
+ lua_pushinteger(L, text_direction_par);
+ return 1;
+}
+
+static int texlib_settextdir(lua_State *L)
+{
+ tex_set_text_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+/* Getting the line direction makes no sense, it's just the text direction. */
+
+static int texlib_setlinedir(lua_State *L)
+{
+ tex_set_line_dir(lmt_tohalfword(L, 1));
+ return 0;
+}
+
+static int texlib_getboxdir(lua_State *L)
+{
+ int index = lmt_tointeger(L, 1);
+ if (index >= 0 && index <= max_box_register_index) {
+ if (box_register(index)) {
+ lua_pushinteger(L, box_dir(box_register(index)));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ } else {
+ texlib_aux_show_box_index_error(L);
+ }
+ return 0;
+}
+
+static int texlib_setboxdir(lua_State *L)
+{
+ int index = lmt_tointeger(L, 1);
+ if (index >= 0 && index <= max_box_register_index) {
+ tex_set_box_dir(index, lmt_tointeger(L, 2));
+ } else {
+ texlib_aux_show_box_index_error(L);
+ }
+ return 0;
+}
+
+static int texlib_gethelptext(lua_State *L)
+{
+ if (lmt_error_state.help_text) {
+ lua_pushstring(L, lmt_error_state.help_text);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_setinteraction(lua_State *L)
+{
+ if (lua_type(L,1) == LUA_TNUMBER) {
+ int i = lmt_tointeger(L, 1);
+ if (i >= 0 && i <= 3) {
+ lmt_error_state.interaction = i;
+ }
+ }
+ return 0;
+}
+
+static int texlib_getinteraction(lua_State *L)
+{
+ lua_pushinteger(L, lmt_error_state.interaction);
+ return 1;
+}
+
+static int texlib_setglyphdata(lua_State *L)
+{
+ update_tex_glyph_data(0, lmt_opthalfword(L, 1, unused_attribute_value));
+ return 0;
+}
+
+static int texlib_getglyphdata(lua_State *L)
+{
+ lua_pushinteger(L, glyph_data_par);
+ return 1;
+}
+
+static int texlib_setglyphstate(lua_State *L)
+{
+ update_tex_glyph_state(0, lmt_opthalfword(L, 1, unused_state_value));
+ return 0;
+}
+
+static int texlib_getglyphstate(lua_State *L)
+{
+ lua_pushinteger(L, glyph_state_par);
+ return 1;
+}
+
+static int texlib_setglyphscript(lua_State *L)
+{
+ update_tex_glyph_script(0, lmt_opthalfword(L, 1, unused_script_value));
+ return 0;
+}
+
+static int texlib_getglyphscript(lua_State *L)
+{
+ lua_pushinteger(L, glyph_script_par);
+ return 1;
+}
+
+static int texlib_getglyphscales(lua_State *L)
+{
+ lua_pushinteger(L, glyph_scale_par);
+ lua_pushinteger(L, glyph_x_scale_par);
+ lua_pushinteger(L, glyph_y_scale_par);
+ lua_pushinteger(L, glyph_data_par);
+ return 4;
+}
+
+static int texlib_fatalerror(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ tex_fatal_error(s);
+ return 1;
+}
+
+static int texlib_lastnodetype(lua_State *L)
+{
+ halfword tail = cur_list.tail;
+ int t = -1;
+ int s = -1;
+ if (tail) {
+ halfword mode = cur_list.mode;
+ if (mode != nomode && tail != contribute_head && node_type(tail) != glyph_node) {
+ t = node_type(tail);
+ s = node_subtype(tail);
+ } else if (mode == vmode && tail == cur_list.head) {
+ t = lmt_page_builder_state.last_node_type;
+ s = lmt_page_builder_state.last_node_subtype;
+ } else if (mode == nomode || tail == cur_list.head) {
+ /* already -1 */
+ } else {
+ t = node_type(tail);
+ s = node_subtype(tail);
+ }
+ }
+ if (t >= 0) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, s);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* we can have all defs here */
+
+static int texlib_chardef(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int code = lmt_tointeger(L, 2);
+ if (code >= 0 && code <= max_character_code) {
+ tex_define(flags, cs, (quarterword) char_given_cmd, code);
+ } else {
+ tex_formatted_error("lua", "chardef only accepts codes in the range 0-%i", max_character_code);
+ }
+ }
+ }
+ return 0;
+}
+
+/* todo: same range checks as in texlib_setmathcode */
+
+static int texlib_mathchardef(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 5, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ mathcodeval m;
+ mathdictval d;
+ m.class_value = lmt_tointeger(L, 2);
+ m.family_value = lmt_tointeger(L, 3);
+ m.character_value = lmt_tointeger(L, 4);
+ d.properties = lmt_optquarterword(L, 6, 0);
+ d.group = lmt_optquarterword(L, 7, 0);
+ d.index = lmt_optinteger(L, 8, 0);
+ if (class_in_range(m.class_value) && family_in_range(m.family_value) && character_in_range(m.character_value)) {
+ tex_define(flags, cs, mathspec_cmd, tex_new_math_dict_spec(d, m, umath_mathcode));
+ // halfword code = math_packed_character(m.class_value, m.family_value, m.character_value);
+ // tex_define(flags, cs, (quarterword) math_char_xgiven_cmd, code);
+ } else {
+ tex_normal_error("lua", "mathchardef needs proper class, family and character codes");
+ }
+ } else {
+ /* maybe a message */
+ }
+ }
+ return 0;
+}
+
+static int texlib_setintegervalue(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int value = lmt_optroundnumber(L, 2, 0);
+ if (value >= min_integer && value <= max_integer) {
+ tex_define(flags, cs, (quarterword) integer_cmd, value);
+ } else {
+ tex_formatted_error("lua", "integer only accepts values in the range %i-%i", min_integer, max_integer);
+ }
+ }
+ }
+ return 0;
+}
+
+static int texlib_setdimensionvalue(lua_State *L)
+{
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s) {
+ int cs = tex_string_locate(s, l, 1);
+ int flags = 0;
+ lmt_check_for_flags(L, 3, &flags, 1, 0);
+ if (tex_define_permitted(cs, flags)) {
+ int value = lmt_optroundnumber(L, 2, 0);
+ if (value >= min_dimen && value <= max_dimen) {
+ tex_define(flags, cs, (quarterword) dimension_cmd, value);
+ } else {
+ tex_formatted_error("lua", "dimension only accepts values in the range %i-%i", min_dimen, max_dimen);
+ }
+ }
+ }
+ return 0;
+}
+
+// static int texlib_setgluespecvalue(lua_State *L)
+// {
+// return 0;
+// }
+
+static int texlib_aux_getvalue(lua_State *L, halfword level, halfword cs)
+{
+ halfword chr = eq_value(cs);
+ if (chr && ! get_token_parameters(chr)) {
+ halfword value = 0;
+ tex_begin_inserted_list(tex_get_available_token(cs_token_flag + cs));
+ if (tex_scan_tex_value(level, &value)) {
+ lua_pushinteger(L, value);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_getintegervalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+{
+ 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);
+ switch (eq_type(cs)) {
+ case integer_cmd:
+ lua_pushinteger(L, eq_value(cs));
+ return 1;
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ return texlib_aux_getvalue(L, int_val_level, cs);
+ default:
+ /* twice a lookup but fast enough for now */
+ return texlib_getcount(L);
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int texlib_getdimensionvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+{
+ 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);
+ switch (eq_type(cs)) {
+ case dimension_cmd:
+ lua_pushinteger(L, eq_value(cs));
+ return 1;
+ case call_cmd:
+ case protected_call_cmd:
+ case semi_protected_call_cmd:
+ return texlib_aux_getvalue(L, dimen_val_level, cs);
+ default:
+ /* twice a lookup but fast enough for now */
+ return texlib_getdimen(L);
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+// static int texlib_getgluespecvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
+// {
+// return 1;
+// }
+
+/*tex
+ Negative values are internal and inline. At some point I might do this as with modes and tokens
+ although we don't have lookups here.
+
+ In these list we don't really need the predefined keys.
+*/
+
+static int texlib_getmodevalues(lua_State *L)
+{
+ lua_createtable(L, 4, 1);
+ lua_push_key_at_index(L, unset, nomode);
+ lua_push_key_at_index(L, vertical, vmode);
+ lua_push_key_at_index(L, horizontal, hmode);
+ lua_push_key_at_index(L, math, mmode);
+ return 1;
+}
+
+static int texlib_getmode(lua_State *L)
+{
+ lua_pushinteger(L, abs(cur_list.mode));
+ return 1;
+}
+
+static int texlib_getrunstatevalues(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_set_string_by_index(L, initializing_state, "initializing");
+ lua_set_string_by_index(L, updating_state, "updating");
+ lua_set_string_by_index(L, production_state, "production");
+ return 1;
+}
+
+static int texlib_setrunstate(lua_State *L)
+{
+ int state = lmt_tointeger(L, 1);
+ if (state == updating_state || state == production_state) {
+ lmt_main_state.run_state = state;
+ }
+ return 0;
+}
+
+static int texlib_gethyphenationvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 17);
+ lua_push_key_at_index(L, normal, normal_hyphenation_mode);
+ lua_push_key_at_index(L, automatic, automatic_hyphenation_mode);
+ lua_push_key_at_index(L, explicit, explicit_hyphenation_mode);
+ lua_push_key_at_index(L, syllable, syllable_hyphenation_mode);
+ lua_push_key_at_index(L, uppercase, uppercase_hyphenation_mode);
+ lua_push_key_at_index(L, compound, compound_hyphenation_mode);
+ lua_push_key_at_index(L, strictstart, strict_start_hyphenation_mode);
+ lua_push_key_at_index(L, strictend, strict_end_hyphenation_mode);
+ lua_push_key_at_index(L, automaticpenalty, automatic_penalty_hyphenation_mode);
+ lua_push_key_at_index(L, explicitpenalty, explicit_penalty_hyphenation_mode);
+ lua_push_key_at_index(L, permitglue, permit_glue_hyphenation_mode);
+ lua_push_key_at_index(L, permitall, permit_all_hyphenation_mode);
+ lua_push_key_at_index(L, permitmathreplace, permit_math_replace_hyphenation_mode);
+ lua_push_key_at_index(L, forcecheck, force_check_hyphenation_mode);
+ lua_push_key_at_index(L, lazyligatures, lazy_ligatures_hyphenation_mode);
+ lua_push_key_at_index(L, forcehandler, force_handler_hyphenation_mode);
+ lua_push_key_at_index(L, feedbackcompound, feedback_compound_hyphenation_mode);
+ lua_push_key_at_index(L, ignorebounds, ignore_bounds_hyphenation_mode);
+ lua_push_key_at_index(L, collapse, collapse_hyphenation_mode);
+ return 1;
+}
+
+static int texlib_getglyphoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 3, 7);
+ lua_set_string_by_index(L, glyph_option_normal_glyph, "normal");
+ lua_set_string_by_index(L, glyph_option_no_left_ligature, "noleftligature");
+ lua_set_string_by_index(L, glyph_option_no_right_ligature, "norightligature");
+ lua_set_string_by_index(L, glyph_option_no_left_kern, "noleftkern");
+ lua_set_string_by_index(L, glyph_option_no_right_kern, "norightkern");
+ lua_set_string_by_index(L, glyph_option_no_expansion, "noexpansion");
+ lua_set_string_by_index(L, glyph_option_no_protrusion, "noprotrusion");
+ lua_set_string_by_index(L, glyph_option_no_italic_correction, "noitaliccorrection");
+ lua_set_string_by_index(L, glyph_option_math_discretionary, "mathdiscretionary");
+ lua_set_string_by_index(L, glyph_option_math_italics_too, "mathsitalicstoo");
+ return 1;
+}
+
+static int texlib_getnoadoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 32);
+ lua_push_key_at_index(L, axis, noad_option_axis);
+ lua_push_key_at_index(L, noaxis, noad_option_no_axis);
+ lua_push_key_at_index(L, exact, noad_option_exact);
+ lua_push_key_at_index(L, left, noad_option_left);
+ lua_push_key_at_index(L, middle, noad_option_middle);
+ lua_push_key_at_index(L, right, noad_option_right);
+ lua_push_key_at_index(L, adapttoleftsize, noad_option_adapt_to_left_size);
+ lua_push_key_at_index(L, adapttorightsize, noad_option_adapt_to_right_size);
+ lua_push_key_at_index(L, nosubscript, noad_option_no_sub_script);
+ lua_push_key_at_index(L, nosuperscript, noad_option_no_super_script);
+ lua_push_key_at_index(L, nosubprescript, noad_option_no_sub_pre_script);
+ lua_push_key_at_index(L, nosuperprescript, noad_option_no_super_pre_script);
+ lua_push_key_at_index(L, noscript, noad_option_no_script);
+ lua_push_key_at_index(L, nooverflow, noad_option_no_overflow);
+ lua_push_key_at_index(L, void, noad_option_void);
+ lua_push_key_at_index(L, phantom, noad_option_phantom);
+ lua_push_key_at_index(L, openupheight, noad_option_openup_height);
+ lua_push_key_at_index(L, openupdepth, noad_option_openup_depth);
+ lua_push_key_at_index(L, limits, noad_option_limits);
+ lua_push_key_at_index(L, nolimits, noad_option_no_limits);
+ lua_push_key_at_index(L, preferfontthickness, noad_option_prefer_font_thickness);
+ lua_push_key_at_index(L, noruling, noad_option_no_ruling);
+ lua_push_key_at_index(L, shiftedsubscript, noad_option_shifted_sub_script);
+ lua_push_key_at_index(L, shiftedsuperscript, noad_option_shifted_super_script);
+ lua_push_key_at_index(L, shiftedsubprescript, noad_option_shifted_sub_pre_script);
+ lua_push_key_at_index(L, shiftedsuperprescript, noad_option_shifted_super_pre_script);
+ lua_push_key_at_index(L, unpacklist, noad_option_unpack_list);
+ lua_push_key_at_index(L, nocheck, noad_option_no_check);
+ lua_push_key_at_index(L, auto, noad_option_auto);
+ lua_push_key_at_index(L, unrolllist, noad_option_unroll_list);
+ lua_push_key_at_index(L, followedbyspace, noad_option_followed_by_space);
+ return 1;
+}
+
+static int texlib_getdiscoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_set_string_by_index(L, disc_option_normal_word, "normalword");
+ lua_set_string_by_index(L, disc_option_pre_word, "preword");
+ lua_set_string_by_index(L, disc_option_post_word, "postword");
+ return 1;
+}
+
+static int texlib_getlistanchorvalues(lua_State *L)
+{
+ lua_createtable(L, 14, 0);
+ lua_set_string_by_index(L, left_origin_anchor, "leftorigin");
+ lua_set_string_by_index(L, left_height_anchor, "leftheight");
+ lua_set_string_by_index(L, left_depth_anchor, "leftdepth");
+ lua_set_string_by_index(L, right_origin_anchor, "rightorigin");
+ lua_set_string_by_index(L, right_height_anchor, "rightheight");
+ lua_set_string_by_index(L, right_depth_anchor, "rightdepth");
+ lua_set_string_by_index(L, center_origin_anchor, "centerorigin");
+ lua_set_string_by_index(L, center_height_anchor, "centerheight");
+ lua_set_string_by_index(L, center_depth_anchor, "centerdepth");
+ lua_set_string_by_index(L, halfway_total_anchor, "halfwaytotal");
+ lua_set_string_by_index(L, halfway_height_anchor, "halfwayheight");
+ lua_set_string_by_index(L, halfway_depth_anchor, "halfwaydepth");
+ lua_set_string_by_index(L, halfway_left_anchor, "halfwayleft");
+ lua_set_string_by_index(L, halfway_right_anchor, "halfwayright");
+ return 1;
+}
+
+static int texlib_getlistsignvalues(lua_State *L)
+{
+ lua_createtable(L, 0, 2);
+ lua_set_string_by_index(L, negate_x_anchor, "negatex");
+ lua_set_string_by_index(L, negate_y_anchor, "negatey");
+ return 1;
+}
+
+static int texlib_getlistgeometryalues(lua_State *L)
+{
+ lua_createtable(L, 3, 0);
+ lua_set_string_by_index(L, offset_geometry, "offset");
+ lua_set_string_by_index(L, orientation_geometry, "orientation");
+ lua_set_string_by_index(L, anchor_geometry, "anchor");
+ return 1;
+}
+
+static int texlib_getautomigrationvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 3);
+ lua_push_key_at_index(L, mark, auto_migrate_mark);
+ lua_push_key_at_index(L, insert, auto_migrate_insert);
+ lua_push_key_at_index(L, adjust, auto_migrate_adjust);
+ lua_push_key_at_index(L, pre, auto_migrate_pre);
+ lua_push_key_at_index(L, post, auto_migrate_post);
+ return 1;
+}
+
+static int texlib_getflagvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 15);
+ lua_push_key_at_index(L, frozen, frozen_flag_bit);
+ lua_push_key_at_index(L, permanent, permanent_flag_bit);
+ lua_push_key_at_index(L, immutable, immutable_flag_bit);
+ lua_push_key_at_index(L, primitive, primitive_flag_bit);
+ lua_push_key_at_index(L, mutable, mutable_flag_bit);
+ lua_push_key_at_index(L, noaligned, noaligned_flag_bit);
+ lua_push_key_at_index(L, instance, instance_flag_bit);
+ lua_push_key_at_index(L, untraced, untraced_flag_bit);
+ lua_push_key_at_index(L, global, global_flag_bit);
+ lua_push_key_at_index(L, tolerant, tolerant_flag_bit);
+ lua_push_key_at_index(L, protected, protected_flag_bit);
+ lua_push_key_at_index(L, overloaded, overloaded_flag_bit);
+ lua_push_key_at_index(L, aliased, aliased_flag_bit);
+ lua_push_key_at_index(L, immediate, immediate_flag_bit);
+ lua_push_key_at_index(L, conditional, conditional_flag_bit);
+ lua_push_key_at_index(L, value, value_flag_bit);
+ return 1;
+}
+
+static int texlib_getspecialmathclassvalues(lua_State *L)
+{
+ lua_createtable(L, 0, 3);
+ lua_set_string_by_index(L, math_all_class, "all");
+ lua_set_string_by_index(L, math_begin_class, "begin");
+ lua_set_string_by_index(L, math_end_class, "end");
+ return 1;
+}
+
+static int texlib_getmathclassoptionvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 19);
+ lua_set_string_by_index(L, no_pre_slack_class_option, "nopreslack");
+ lua_set_string_by_index(L, no_post_slack_class_option, "nopostslack");
+ lua_set_string_by_index(L, left_top_kern_class_option, "lefttopkern");
+ lua_set_string_by_index(L, right_top_kern_class_option, "righttopkern");
+ lua_set_string_by_index(L, left_bottom_kern_class_option, "leftbottomkern");
+ lua_set_string_by_index(L, right_bottom_kern_class_option, "rightbottomkern");
+ lua_set_string_by_index(L, look_ahead_for_end_class_option, "lookaheadforend");
+ lua_set_string_by_index(L, no_italic_correction_class_option, "noitaliccorrection");
+ lua_set_string_by_index(L, check_ligature_class_option, "checkligature");
+ lua_set_string_by_index(L, check_kern_pair_class_option, "checkkernpair");
+ lua_set_string_by_index(L, check_italic_correction_class_option, "checkitaliccorrection");
+ lua_set_string_by_index(L, flatten_class_option, "flatten");
+ lua_set_string_by_index(L, omit_penalty_class_option, "omitpenalty");
+ // lua_set_string_by_index(L, open_fence_class_option, "openfence");
+ // lua_set_string_by_index(L, close_fence_class_option, "closefence");
+ // lua_set_string_by_index(L, middle_fence_class_option, "middlefence");
+ lua_set_string_by_index(L, unpack_class_option, "unpack");
+ lua_set_string_by_index(L, raise_prime_option, "raiseprime");
+ lua_set_string_by_index(L, carry_over_left_top_kern_class_option, "carryoverlefttopkern");
+ lua_set_string_by_index(L, carry_over_left_bottom_kern_class_option, "carryoverleftbottomkern");
+ lua_set_string_by_index(L, carry_over_right_top_kern_class_option, "carryoverrighttopkern");
+ lua_set_string_by_index(L, carry_over_right_bottom_kern_class_option, "carryoverrightbottomkern");
+ lua_set_string_by_index(L, prefer_delimiter_dimensions_class_option, "preferdelimiterdimensions");
+ lua_set_string_by_index(L, auto_inject_class_option, "autoinject");
+ lua_set_string_by_index(L, remove_italic_correction_class_option, "removeitaliccorrection");
+ return 1;
+}
+
+static int texlib_getnormalizelinevalues(lua_State *L)
+{
+ lua_createtable(L, 2, 7);
+ lua_set_string_by_index(L, normalize_line_mode, "normalizeline");
+ lua_set_string_by_index(L, parindent_skip_mode, "parindentskip");
+ lua_set_string_by_index(L, swap_hangindent_mode, "swaphangindent");
+ lua_set_string_by_index(L, swap_parshape_mode, "swapparshape");
+ lua_set_string_by_index(L, break_after_dir_mode, "breakafterdir");
+ lua_set_string_by_index(L, remove_margin_kerns_mode, "removemarginkerns");
+ lua_set_string_by_index(L, clip_width_mode, "clipwidth");
+ lua_set_string_by_index(L, flatten_discretionaries_mode, "flattendiscretionaries");
+ lua_set_string_by_index(L, discard_zero_tab_skips_mode, "discardzerotabskips");
+ lua_set_string_by_index(L, flatten_h_leaders_mode, "flattenhleaders");
+ return 1;
+}
+
+static int texlib_getnormalizeparvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 0);
+ lua_set_string_by_index(L, normalize_par_mode, "normalizepar");
+ lua_set_string_by_index(L, flatten_v_leaders_mode, "flattenvleaders");
+ return 1;
+}
+
+static int texlib_geterrorvalues(lua_State *L)
+{
+ lua_createtable(L, 7, 1);
+ lua_set_string_by_index(L, normal_error_type, "normal");
+ lua_set_string_by_index(L, back_error_type, "back");
+ lua_set_string_by_index(L, insert_error_type, "insert");
+ lua_set_string_by_index(L, succumb_error_type, "succumb");
+ lua_set_string_by_index(L, eof_error_type, "eof");
+ lua_set_string_by_index(L, condition_error_type,"condition");
+ lua_set_string_by_index(L, runaway_error_type, "runaway");
+ lua_set_string_by_index(L, warning_error_type, "warning");
+ return 1;
+}
+
+static int texlib_getiovalues(lua_State *L) /* for reporting so we keep spaces */
+{
+ lua_createtable(L, 5, 1);
+ lua_set_string_by_index(L, io_initial_input_code, "initial");
+ lua_set_string_by_index(L, io_lua_input_code, "lua print");
+ lua_set_string_by_index(L, io_token_input_code, "scan token");
+ lua_set_string_by_index(L, io_token_eof_input_code, "scan token eof");
+ lua_set_string_by_index(L, io_tex_macro_code, "tex macro");
+ lua_set_string_by_index(L, io_file_input_code, "file");
+ return 1;
+}
+
+static int texlib_getfrozenparvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 20);
+ lua_set_string_by_index(L, par_hsize_category, "hsize");
+ lua_set_string_by_index(L, par_skip_category, "skip");
+ lua_set_string_by_index(L, par_hang_category, "hang");
+ lua_set_string_by_index(L, par_indent_category, "indent");
+ lua_set_string_by_index(L, par_par_fill_category, "parfill");
+ lua_set_string_by_index(L, par_adjust_category, "adjust");
+ lua_set_string_by_index(L, par_protrude_category, "protrude");
+ lua_set_string_by_index(L, par_tolerance_category, "tolerance");
+ lua_set_string_by_index(L, par_stretch_category, "stretch");
+ lua_set_string_by_index(L, par_looseness_category, "looseness");
+ lua_set_string_by_index(L, par_last_line_category, "lastline");
+ lua_set_string_by_index(L, par_line_penalty_category, "linepenalty");
+ lua_set_string_by_index(L, par_club_penalty_category, "clubpenalty");
+ lua_set_string_by_index(L, par_widow_penalty_category, "widowpenalty");
+ lua_set_string_by_index(L, par_display_penalty_category, "displaypenalty");
+ lua_set_string_by_index(L, par_broken_penalty_category, "brokenpenalty");
+ lua_set_string_by_index(L, par_demerits_category, "demerits");
+ lua_set_string_by_index(L, par_shape_category, "shape");
+ lua_set_string_by_index(L, par_line_category, "line");
+ lua_set_string_by_index(L, par_hyphenation_category, "hyphenation");
+ lua_set_string_by_index(L, par_shaping_penalty_category, "shapingpenalty");
+ lua_set_string_by_index(L, par_orphan_penalty_category, "orphanpenalty");
+ /* lua_set_string_by_index(L, par_all_category, "all"); */
+ return 1;
+}
+
+static int texlib_getshapingpenaltiesvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 2);
+ lua_push_key_at_index(L, interlinepenalty, inter_line_penalty_shaping);
+ lua_push_key_at_index(L, widowpenalty, widow_penalty_shaping);
+ lua_push_key_at_index(L, clubpenalty, club_penalty_shaping);
+ lua_push_key_at_index(L, brokenpenalty, broken_penalty_shaping);
+ return 1;
+}
+
+
+static int texlib_getprimitiveorigins(lua_State *L)
+{
+ lua_createtable(L, 2, 1);
+ lua_push_key_at_index(L, tex, tex_command);
+ lua_push_key_at_index(L, etex, etex_command);
+ lua_push_key_at_index(L, luatex, luatex_command);
+ return 1;
+}
+
+static int texlib_getlargestusedmark(lua_State* L)
+{
+ lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
+ return 1;
+}
+
+static int texlib_getoutputactive(lua_State* L)
+{
+ lua_pushboolean(L, lmt_page_builder_state.output_active);
+ return 1;
+}
+
+/*tex Moved from lmtnodelib to here. */
+
+int lmt_push_info_keys(lua_State *L, value_info *values)
+{
+ lua_newtable(L);
+ for (int i = 0; values[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
+ lua_rawseti(L, -2, i);
+ }
+ return 1;
+}
+
+int lmt_push_info_values(lua_State *L, value_info *values)
+{
+ lua_newtable(L);
+ for (int i = 0; values[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
+ lua_rawseti(L, -2, values[i].value);
+ }
+ return 1;
+}
+
+static int texlib_getgroupvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.group_code_values);
+}
+
+static int texlib_getmathparametervalues(lua_State *L)
+{
+ return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
+}
+
+static int texlib_getmathstylevalues(lua_State* L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_values);
+}
+
+static int texlib_getpacktypevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.pack_type_values);
+}
+
+static int texlib_getparcontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_context_values);
+}
+
+static int texlib_getpagecontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.page_context_values);
+}
+
+static int texlib_getappendlinecontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.append_line_context_values);
+}
+
+static int texlib_getalignmentcontextvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.alignment_context_values);
+}
+
+static int texlib_getparbeginvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_begin_values);
+}
+
+static int texlib_getparmodevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.par_mode_values);
+}
+
+static int texlib_getmathstylenamevalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_name_values);
+}
+
+static int texlib_getmathvariantvalues(lua_State *L)
+{
+ return lmt_push_info_values(L, lmt_interface.math_style_variant_values);
+}
+
+// static int texlib_getmathflattenvalues(lua_State *L)
+// {
+// lua_createtable(L, 2, 3);
+// lua_set_string_by_index(L, math_flatten_ordinary, "ord");
+// lua_set_string_by_index(L, math_flatten_binary, "bin");
+// lua_set_string_by_index(L, math_flatten_relation, "rel");
+// lua_set_string_by_index(L, math_flatten_punctuation, "punct");
+// lua_set_string_by_index(L, math_flatten_inner, "inner");
+// return 1;
+// }
+
+static int texlib_getdiscstatevalues(lua_State *L)
+{
+ lua_createtable(L, 4, 1);
+ lua_set_string_by_index(L, glyph_discpart_unset, "unset");
+ lua_set_string_by_index(L, glyph_discpart_pre, "pre");
+ lua_set_string_by_index(L, glyph_discpart_post, "post");
+ lua_set_string_by_index(L, glyph_discpart_replace, "replace");
+ lua_set_string_by_index(L, glyph_discpart_always, "always");
+ return 1;
+}
+
+static int texlib_getmathcontrolvalues(lua_State *L)
+{
+ lua_createtable(L, 2, 19);
+ lua_set_string_by_index(L, math_control_use_font_control, "usefontcontrol");
+ lua_set_string_by_index(L, math_control_over_rule, "overrule");
+ lua_set_string_by_index(L, math_control_under_rule, "underrule");
+ lua_set_string_by_index(L, math_control_radical_rule, "radicalrule");
+ lua_set_string_by_index(L, math_control_fraction_rule, "fractionrule");
+ lua_set_string_by_index(L, math_control_accent_skew_half, "accentskewhalf");
+ lua_set_string_by_index(L, math_control_accent_skew_apply, "accentskewapply");
+ lua_set_string_by_index(L, math_control_apply_ordinary_kern_pair, "applyordinarykernpair");
+ lua_set_string_by_index(L, math_control_apply_vertical_italic_kern, "applyverticalitalickern");
+ lua_set_string_by_index(L, math_control_apply_ordinary_italic_kern, "applyordinaryitalickern");
+ lua_set_string_by_index(L, math_control_apply_char_italic_kern, "applycharitalickern");
+ lua_set_string_by_index(L, math_control_rebox_char_italic_kern, "reboxcharitalickern");
+ lua_set_string_by_index(L, math_control_apply_boxed_italic_kern, "applyboxeditalickern");
+ lua_set_string_by_index(L, math_control_staircase_kern, "staircasekern");
+ lua_set_string_by_index(L, math_control_apply_text_italic_kern, "applytextitalickern");
+ lua_set_string_by_index(L, math_control_check_text_italic_kern, "checktextitalickern");
+ lua_set_string_by_index(L, math_control_check_space_italic_kern, "checkspaceitalickern");
+ lua_set_string_by_index(L, math_control_apply_script_italic_kern, "applyscriptitalickern");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_char, "analyzescriptnucleuschar");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_list, "analyzescriptnucleuslist");
+ lua_set_string_by_index(L, math_control_analyze_script_nucleus_box, "analyzescriptnucleusbox");
+ return 1;
+}
+
+static int texlib_gettextcontrolvalues(lua_State *L)
+{
+ lua_createtable(L, 1, 0);
+ lua_set_string_by_index(L, text_control_collapse_hyphens, "collapsehyphens");
+ return 1;
+}
+
+/* relatively new */
+
+static int texlib_getinsertdistance(lua_State *L)
+{
+ return texlib_aux_push_glue(L, tex_get_insert_distance(lmt_tointeger(L, 1)));
+}
+
+static int texlib_getinsertmultiplier(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_multiplier(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertlimit(lua_State *L)
+{
+ tex_set_insert_limit(lmt_tointeger(L, 1), lmt_opthalfword(L, 2, 0));
+ return 0;
+}
+
+static int texlib_setinsertdistance(lua_State *L)
+{
+ tex_set_insert_distance(lmt_tointeger(L, 1), texlib_aux_make_glue(L, lua_gettop(L), 2));
+ return 0;
+}
+
+static int texlib_setinsertmultiplier(lua_State *L)
+{
+ tex_set_insert_multiplier(lmt_tointeger(L, 1), lmt_tohalfword(L, 2));
+ return 0;
+}
+
+static int texlib_setinsertlimit(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_limit(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertheight(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_height(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertdepth(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_depth(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertwidth(lua_State *L)
+{
+ lua_pushinteger(L, tex_get_insert_width(lmt_tointeger(L, 1)));
+ return 1;
+}
+
+static int texlib_getinsertcontent(lua_State *L)
+{
+ halfword index = lmt_tointeger(L, 1);
+ lmt_node_list_to_lua(L, tex_get_insert_content(index));
+ tex_set_insert_content(index, null);
+ return 1;
+}
+
+static int texlib_setinsertcontent(lua_State *L)
+{
+ halfword index = lmt_tointeger(L, 1);
+ tex_flush_node(tex_get_insert_content(index));
+ tex_set_insert_content(index, lmt_node_list_from_lua(L, 2));
+ return 0;
+}
+
+static int texlib_getlocalbox(lua_State *L)
+{
+ int location = lmt_tointeger(L, 1);
+ if (is_valid_local_box_code(location)) {
+ lmt_node_list_to_lua(L, tex_get_local_boxes(location));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int texlib_setlocalbox(lua_State *L)
+{
+ int location = lmt_tointeger(L, 1);
+ if (is_valid_local_box_code(location)) {
+ tex_set_local_boxes(lmt_node_list_from_lua(L, 1), location);
+ }
+ return 0;
+}
+
+static int texlib_pushsavelevel(lua_State *L)
+{
+ (void) L;
+ tex_new_save_level(lua_group);
+ return 0;
+}
+
+static int texlib_popsavelevel(lua_State *L)
+{
+ (void) L;
+ // tex_off_save();
+ tex_unsave();
+ return 0;
+}
+
+/*tex
+ When testing all these math finetuning options we needed to typeset the box contents and
+ instead of filtering from the log or piping the log to a file, this more ssd friendly
+ feature was added. The first argument is a box (id) and the second an optional detail
+ directive. This is (currently) the only case where we write to a \LUA\ buffer, but I
+ might add variants for a macro and tokenlist at some point (less interesting).
+*/
+
+/* till here */
+
+static const struct luaL_Reg texlib_function_list[] = {
+ { "write", texlib_write },
+ { "print", texlib_print },
+ { "sprint", texlib_sprint },
+ { "mprint", texlib_mprint },
+ { "tprint", texlib_tprint },
+ { "cprint", texlib_cprint },
+ { "isprintable", texlib_isprintable },
+ { "pushlocal", texlib_pushlocal },
+ { "poplocal", texlib_poplocal },
+ { "runlocal", texlib_runlocal },
+ { "runstring", texlib_runstring },
+ { "quitlocal", texlib_quitlocal },
+ { "expandasvalue", texlib_expandasvalue }, /* experiment */
+ { "error", texlib_error },
+ { "set", texlib_set },
+ { "get", texlib_get },
+ { "getregisterindex", texlib_get_register_index },
+ { "isdimen", texlib_isdimen },
+ { "setdimen", texlib_setdimen },
+ { "getdimen", texlib_getdimen },
+ { "isskip", texlib_isskip },
+ { "setskip", texlib_setskip },
+ { "getskip", texlib_getskip },
+ { "isglue", texlib_isglue },
+ { "setglue", texlib_setglue },
+ { "getglue", texlib_getglue },
+ { "ismuskip", texlib_ismuskip },
+ { "setmuskip", texlib_setmuskip },
+ { "getmuskip", texlib_getmuskip },
+ { "ismuglue", texlib_ismuglue },
+ { "setmuglue", texlib_setmuglue },
+ { "getmuglue", texlib_getmuglue },
+ { "isattribute", texlib_isattribute },
+ { "setattribute", texlib_setattribute },
+ { "getattribute", texlib_getattribute },
+ { "iscount", texlib_iscount },
+ { "setcount", texlib_setcount },
+ { "getcount", texlib_getcount },
+ { "istoks", texlib_istoks },
+ { "settoks", texlib_settoks },
+ { "scantoks", texlib_scantoks },
+ { "gettoks", texlib_gettoks },
+ { "getmark", texlib_getmark },
+ { "isbox", texlib_isbox },
+ { "setbox", texlib_setbox },
+ { "getbox", texlib_getbox },
+ { "splitbox", texlib_splitbox },
+ { "setlist", texlib_setlist },
+ { "getlist", texlib_getlist },
+ { "setnest", texlib_setnest }, /* only a message */
+ { "getnest", texlib_getnest },
+ { "setcatcode", texlib_setcatcode },
+ { "getcatcode", texlib_getcatcode },
+ { "setdelcode", texlib_setdelcode },
+ { "getdelcode", texlib_getdelcode },
+ { "getdelcodes", texlib_getdelcodes },
+ { "sethccode", texlib_sethccode },
+ { "gethccode", texlib_gethccode },
+ { "sethmcode", texlib_sethmcode },
+ { "gethmcode", texlib_gethmcode },
+ { "setlccode", texlib_setlccode },
+ { "getlccode", texlib_getlccode },
+ { "setmathcode", texlib_setmathcode },
+ { "getmathcode", texlib_getmathcode },
+ { "getmathcodes", texlib_getmathcodes },
+ { "setsfcode", texlib_setsfcode },
+ { "getsfcode", texlib_getsfcode },
+ { "setuccode", texlib_setuccode },
+ { "getuccode", texlib_getuccode },
+ { "round", texlib_round },
+ { "scale", texlib_scale },
+ { "sp", texlib_toscaled },
+ { "toscaled", texlib_toscaled },
+ { "tonumber", texlib_tonumber },
+ { "fontname", texlib_getfontname },
+ { "fontidentifier", texlib_getfontidentifier },
+ { "getfontoffamily", texlib_getfontoffamily },
+ { "number", texlib_getnumber },
+ // { "dimension", texlib_getdimension },
+ { "romannumeral", texlib_getromannumeral },
+ { "definefont", texlib_definefont },
+ { "hashtokens", texlib_hashtokens },
+ { "primitives", texlib_primitives },
+ { "extraprimitives", texlib_extraprimitives },
+ { "enableprimitives", texlib_enableprimitives },
+ { "shipout", texlib_shipout },
+ { "badness", texlib_badness },
+ { "setmath", texlib_setmath },
+ { "getmath", texlib_getmath },
+ { "linebreak", texlib_linebreak },
+ { "preparelinebreak", texlib_preparelinebreak },
+ { "resetparagraph", texlib_resetparagraph },
+ { "showcontext", texlib_showcontext },
+ { "triggerbuildpage", texlib_triggerbuildpage },
+ { "gethelptext", texlib_gethelptext },
+ { "getpagestate", texlib_getpagestate },
+ { "getlocallevel", texlib_getlocallevel },
+ { "setinputstatemode", texlib_setinputstatemode },
+ { "getinputstatemode", texlib_getinputstatemode },
+ { "setinputstatefile", texlib_setinputstatefile },
+ { "getinputstatefile", texlib_getinputstatefile },
+ { "forceinputstatefile", texlib_forceinputstatefile },
+ { "forceinputstateline", texlib_forceinputstateline },
+ { "setinputstateline", texlib_setinputstateline },
+ { "getinputstateline", texlib_getinputstateline },
+ { "forcehmode", texlib_forcehmode },
+ { "gettextdir", texlib_gettextdir },
+ { "settextdir", texlib_settextdir },
+ { "getlinedir", texlib_gettextdir }, /* we're nice */
+ { "setlinedir", texlib_setlinedir },
+ { "getmathdir", texlib_getmathdir },
+ { "setmathdir", texlib_setmathdir },
+ { "getpardir", texlib_getpardir },
+ { "setpardir", texlib_setpardir },
+ { "getboxdir", texlib_getboxdir },
+ { "setboxdir", texlib_setboxdir },
+ { "getinteraction", texlib_getinteraction },
+ { "setinteraction", texlib_setinteraction },
+ { "getglyphdata", texlib_getglyphdata },
+ { "setglyphdata", texlib_setglyphdata },
+ { "getglyphstate", texlib_getglyphstate },
+ { "setglyphstate", texlib_setglyphstate },
+ { "getglyphscript", texlib_getglyphscript },
+ { "setglyphscript", texlib_setglyphscript },
+ { "getglyphscales", texlib_getglyphscales },
+ { "fatalerror", texlib_fatalerror },
+ { "lastnodetype", texlib_lastnodetype },
+ { "chardef", texlib_chardef },
+ { "mathchardef", texlib_mathchardef },
+ { "integerdef", texlib_setintegervalue },
+ { "setintegervalue", texlib_setintegervalue },
+ { "getintegervalue", texlib_getintegervalue },
+ { "dimensiondef", texlib_setdimensionvalue },
+ { "setdimensionvalue", texlib_setdimensionvalue },
+ { "getdimensionvalue", texlib_getdimensionvalue },
+ { "getmode", texlib_getmode },
+ { "getmodevalues", texlib_getmodevalues },
+ { "getrunstatevalues", texlib_getrunstatevalues },
+ { "setrunstate", texlib_setrunstate },
+ { "gethyphenationvalues", texlib_gethyphenationvalues },
+ { "getglyphoptionvalues", texlib_getglyphoptionvalues },
+ { "getnoadoptionvalues", texlib_getnoadoptionvalues },
+ { "getdiscoptionvalues", texlib_getdiscoptionvalues },
+ { "getlistanchorvalues", texlib_getlistanchorvalues },
+ { "getlistsignvalues", texlib_getlistsignvalues },
+ { "getlistgeometryvalues", texlib_getlistgeometryalues },
+ { "getdiscstatevalues", texlib_getdiscstatevalues },
+ { "getmathparametervalues", texlib_getmathparametervalues },
+ { "getmathstylenamevalues", texlib_getmathstylenamevalues },
+ { "getmathstylevalues", texlib_getmathstylevalues },
+ { "getmathvariantvalues", texlib_getmathvariantvalues },
+ /* {"getmathflattenvalues", texlib_getmathflattenvalues }, */
+ { "getmathcontrolvalues", texlib_getmathcontrolvalues },
+ { "gettextcontrolvalues", texlib_gettextcontrolvalues },
+ { "getpacktypevalues", texlib_getpacktypevalues },
+ { "getgroupvalues", texlib_getgroupvalues },
+ { "getparcontextvalues", texlib_getparcontextvalues },
+ { "getpagecontextvalues", texlib_getpagecontextvalues },
+ { "getappendlinecontextvalues", texlib_getappendlinecontextvalues },
+ { "getalignmentcontextvalues", texlib_getalignmentcontextvalues },
+ { "getparbeginvalues", texlib_getparbeginvalues },
+ { "getparmodevalues", texlib_getparmodevalues },
+ { "getautomigrationvalues", texlib_getautomigrationvalues },
+ { "getflagvalues", texlib_getflagvalues },
+ { "getmathclassoptionvalues", texlib_getmathclassoptionvalues },
+ { "getnormalizelinevalues", texlib_getnormalizelinevalues },
+ { "getnormalizeparvalues", texlib_getnormalizeparvalues },
+ { "geterrorvalues", texlib_geterrorvalues },
+ { "getiovalues", texlib_getiovalues },
+ { "getprimitiveorigins", texlib_getprimitiveorigins },
+ { "getfrozenparvalues", texlib_getfrozenparvalues },
+ { "getshapingpenaltiesvalues", texlib_getshapingpenaltiesvalues },
+ { "getspecialmathclassvalues", texlib_getspecialmathclassvalues },
+ { "getlargestusedmark", texlib_getlargestusedmark },
+ { "getoutputactive", texlib_getoutputactive },
+ /* experiment (metafun update) */
+ { "shiftparshape", texlib_shiftparshape },
+ { "snapshotpar", texlib_snapshotpar },
+ { "getparstate", texlib_getparstate },
+ /* */
+ { "getinsertdistance", texlib_getinsertdistance },
+ { "getinsertmultiplier", texlib_getinsertmultiplier },
+ { "getinsertlimit", texlib_getinsertlimit },
+ { "getinsertheight", texlib_getinsertheight },
+ { "getinsertdepth", texlib_getinsertdepth },
+ { "getinsertwidth", texlib_getinsertwidth },
+ { "getinsertcontent", texlib_getinsertcontent },
+ { "setinsertdistance", texlib_setinsertdistance },
+ { "setinsertmultiplier", texlib_setinsertmultiplier },
+ { "setinsertlimit", texlib_setinsertlimit },
+ { "setinsertcontent", texlib_setinsertcontent },
+ { "getlocalbox", texlib_getlocalbox },
+ { "setlocalbox", texlib_setlocalbox },
+ /* */
+ { "pushsavelevel", texlib_pushsavelevel },
+ { "popsavelevel", texlib_popsavelevel },
+ /* */
+ { NULL, NULL },
+};
+
+# define defineindexers(name) \
+ static int texlib_index_##name (lua_State *L) { lua_remove(L, 1); return texlib_get##name(L); } \
+ static int texlib_newindex_##name(lua_State *L) { lua_remove(L, 1); return texlib_set##name(L); }
+
+defineindexers(attribute)
+defineindexers(skip)
+defineindexers(glue)
+defineindexers(muskip)
+defineindexers(muglue)
+defineindexers(dimen)
+defineindexers(count)
+defineindexers(toks)
+defineindexers(box)
+defineindexers(sfcode)
+defineindexers(lccode)
+defineindexers(uccode)
+defineindexers(hccode)
+defineindexers(hmcode)
+defineindexers(catcode)
+defineindexers(mathcode)
+defineindexers(delcode)
+defineindexers(list)
+defineindexers(nest)
+
+/*tex
+ At some point the |__index| and |__newindex |below will go away so that we no longer get
+ interferences when we extedn the |tex| table.
+*/
+
+int luaopen_tex(lua_State *L)
+{
+ texlib_aux_initialize();
+ /* */
+ lua_newtable(L);
+ luaL_setfuncs(L, texlib_function_list, 0);
+ lmt_make_table(L, "attribute", TEX_METATABLE_ATTRIBUTE, texlib_index_attribute, texlib_newindex_attribute);
+ lmt_make_table(L, "skip", TEX_METATABLE_SKIP, texlib_index_skip, texlib_newindex_skip);
+ lmt_make_table(L, "glue", TEX_METATABLE_GLUE, texlib_index_glue, texlib_newindex_glue);
+ lmt_make_table(L, "muskip", TEX_METATABLE_MUSKIP, texlib_index_muskip, texlib_newindex_muskip);
+ lmt_make_table(L, "muglue", TEX_METATABLE_MUGLUE, texlib_index_muglue, texlib_newindex_muglue);
+ lmt_make_table(L, "dimen", TEX_METATABLE_DIMEN, texlib_index_dimen, texlib_newindex_dimen);
+ lmt_make_table(L, "count", TEX_METATABLE_COUNT, texlib_index_count, texlib_newindex_count);
+ lmt_make_table(L, "toks", TEX_METATABLE_TOKS, texlib_index_toks, texlib_newindex_toks);
+ lmt_make_table(L, "box", TEX_METATABLE_BOX, texlib_index_box, texlib_newindex_box);
+ lmt_make_table(L, "sfcode", TEX_METATABLE_SFCODE, texlib_index_sfcode, texlib_newindex_sfcode);
+ lmt_make_table(L, "lccode", TEX_METATABLE_LCCODE, texlib_index_lccode, texlib_newindex_lccode);
+ lmt_make_table(L, "uccode", TEX_METATABLE_UCCODE, texlib_index_uccode, texlib_newindex_uccode);
+ lmt_make_table(L, "hccode", TEX_METATABLE_HCCODE, texlib_index_hccode, texlib_newindex_hccode);
+ lmt_make_table(L, "hmcode", TEX_METATABLE_HMCODE, texlib_index_hmcode, texlib_newindex_hmcode);
+ lmt_make_table(L, "catcode", TEX_METATABLE_CATCODE, texlib_index_catcode, texlib_newindex_catcode);
+ lmt_make_table(L, "mathcode", TEX_METATABLE_MATHCODE, texlib_index_mathcode, texlib_newindex_mathcode);
+ lmt_make_table(L, "delcode", TEX_METATABLE_DELCODE, texlib_index_delcode, texlib_newindex_delcode);
+ lmt_make_table(L, "lists", TEX_METATABLE_LISTS, texlib_index_list, texlib_newindex_list);
+ lmt_make_table(L, "nest", TEX_METATABLE_NEST, texlib_index_nest, texlib_newindex_nest);
+ texlib_aux_init_nest_lib(L);
+ /*tex make the meta entries and fetch it back */
+ luaL_newmetatable(L, TEX_METATABLE_TEX);
+ lua_pushstring(L, "__index");
+ lua_pushcfunction(L, texlib_index);
+ lua_settable(L, -3);
+ lua_pushstring(L, "__newindex");
+ lua_pushcfunction(L, texlib_newindex);
+ lua_settable(L, -3);
+ lua_setmetatable(L, -2);
+ return 1;
+}
diff --git a/source/luametatex/source/lua/lmttexlib.h b/source/luametatex/source/lua/lmttexlib.h
new file mode 100644
index 000000000..b0033535a
--- /dev/null
+++ b/source/luametatex/source/lua/lmttexlib.h
@@ -0,0 +1,29 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LTEXLIB_H
+# define LMT_LTEXLIB_H
+
+extern void lmt_cstring_start (void);
+extern void lmt_cstring_close (void);
+extern int lmt_cstring_input (halfword *n, int *cattable, int *partial, int *finalline);
+
+extern void lmt_cstring_print (int cattable, const char *s, int ispartial);
+extern void lmt_tstring_store (strnumber s, int cattable);
+extern void lmt_cstring_store (char *s, int l, int cattable);
+
+extern int lmt_check_for_flags (lua_State *L, int slot, int *flags, int prefixes, int numeric); /* returns slot */
+extern int lmt_check_for_level (lua_State *L, int slot, quarterword *level, quarterword defaultlevel); /* returns slot */
+
+extern int lmt_get_box_id (lua_State *L, int slot, int report);
+
+/*tex
+ In the meantime keys are sequential so we can replace values by keys especially when the type
+ field is used.
+*/
+
+extern int lmt_push_info_values (lua_State *L, value_info *values);
+extern int lmt_push_info_keys (lua_State *L, value_info *values);
+
+# endif
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;
+}
diff --git a/source/luametatex/source/lua/lmttokenlib.h b/source/luametatex/source/lua/lmttokenlib.h
new file mode 100644
index 000000000..5339a80fe
--- /dev/null
+++ b/source/luametatex/source/lua/lmttokenlib.h
@@ -0,0 +1,52 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_LTOKENLIB_H
+# define LMT_LTOKENLIB_H
+
+typedef enum token_origins {
+ token_origin_lua,
+ token_origin_tex,
+} token_origins;
+
+typedef struct lua_token {
+ int token;
+ token_origins origin;
+} lua_token;
+
+typedef enum command_item_types {
+ unused_command_item,
+ regular_command_item,
+ character_command_item,
+ register_command_item,
+ internal_command_item,
+ reference_command_item,
+ data_command_item,
+ token_command_item,
+ node_command_item,
+} command_item_types;
+
+extern void lmt_token_list_to_lua (lua_State *L, halfword p);
+extern void lmt_token_list_to_luastring (lua_State *L, halfword p, int nospace, int strip);
+extern halfword lmt_token_list_from_lua (lua_State *L, int slot);
+extern halfword lmt_token_code_from_lua (lua_State *L, int slot);
+
+extern void lmt_function_call (int slot, int prefix);
+extern int lmt_function_call_by_class (int slot, int property, halfword *value);
+extern void lmt_token_call (int p);
+extern void lmt_local_call (int slot);
+
+extern char *lmt_get_expansion (halfword head, int *len);
+
+extern void lmt_token_register_to_lua (lua_State *L, halfword t);
+
+extern void lmt_tokenlib_initialize (void);
+
+extern int lmt_push_specification (lua_State *L, halfword ptr, int onlycount);
+
+extern void lmt_push_cmd_name (lua_State *L, int cmd);
+
+extern halfword lmt_macro_to_tok (lua_State* L, int slot, halfword *tail);
+
+# endif