From c161b7d6fe142231346cc1844e6e27c0ab7718c1 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Fri, 16 Sep 2022 15:53:42 +0200 Subject: 2022-09-16 14:41:00 --- source/luametatex/source/lua/lmtcallbacklib.c | 615 ++ source/luametatex/source/lua/lmtcallbacklib.h | 105 + source/luametatex/source/lua/lmtenginelib.c | 1146 +++ source/luametatex/source/lua/lmtenginelib.h | 41 + source/luametatex/source/lua/lmtfontlib.c | 1020 +++ source/luametatex/source/lua/lmtfontlib.h | 10 + source/luametatex/source/lua/lmtinterface.c | 544 ++ source/luametatex/source/lua/lmtinterface.h | 1754 ++++ source/luametatex/source/lua/lmtlanguagelib.c | 439 + source/luametatex/source/lua/lmtlanguagelib.h | 20 + source/luametatex/source/lua/lmtlibrary.c | 106 + source/luametatex/source/lua/lmtlibrary.h | 60 + source/luametatex/source/lua/lmtluaclib.c | 660 ++ source/luametatex/source/lua/lmtluaclib.h | 10 + source/luametatex/source/lua/lmtlualib.c | 627 ++ source/luametatex/source/lua/lmtlualib.h | 25 + source/luametatex/source/lua/lmtmplib.c | 3137 +++++++ source/luametatex/source/lua/lmtnodelib.c | 10324 ++++++++++++++++++++++++ source/luametatex/source/lua/lmtnodelib.h | 114 + source/luametatex/source/lua/lmtstatuslib.c | 526 ++ source/luametatex/source/lua/lmttexiolib.c | 307 + source/luametatex/source/lua/lmttexiolib.h | 13 + source/luametatex/source/lua/lmttexlib.c | 5580 +++++++++++++ source/luametatex/source/lua/lmttexlib.h | 29 + source/luametatex/source/lua/lmttokenlib.c | 3894 +++++++++ source/luametatex/source/lua/lmttokenlib.h | 52 + 26 files changed, 31158 insertions(+) create mode 100644 source/luametatex/source/lua/lmtcallbacklib.c create mode 100644 source/luametatex/source/lua/lmtcallbacklib.h create mode 100644 source/luametatex/source/lua/lmtenginelib.c create mode 100644 source/luametatex/source/lua/lmtenginelib.h create mode 100644 source/luametatex/source/lua/lmtfontlib.c create mode 100644 source/luametatex/source/lua/lmtfontlib.h create mode 100644 source/luametatex/source/lua/lmtinterface.c create mode 100644 source/luametatex/source/lua/lmtinterface.h create mode 100644 source/luametatex/source/lua/lmtlanguagelib.c create mode 100644 source/luametatex/source/lua/lmtlanguagelib.h create mode 100644 source/luametatex/source/lua/lmtlibrary.c create mode 100644 source/luametatex/source/lua/lmtlibrary.h create mode 100644 source/luametatex/source/lua/lmtluaclib.c create mode 100644 source/luametatex/source/lua/lmtluaclib.h create mode 100644 source/luametatex/source/lua/lmtlualib.c create mode 100644 source/luametatex/source/lua/lmtlualib.h create mode 100644 source/luametatex/source/lua/lmtmplib.c create mode 100644 source/luametatex/source/lua/lmtnodelib.c create mode 100644 source/luametatex/source/lua/lmtnodelib.h create mode 100644 source/luametatex/source/lua/lmtstatuslib.c create mode 100644 source/luametatex/source/lua/lmttexiolib.c create mode 100644 source/luametatex/source/lua/lmttexiolib.h create mode 100644 source/luametatex/source/lua/lmttexlib.c create mode 100644 source/luametatex/source/lua/lmttexlib.h create mode 100644 source/luametatex/source/lua/lmttokenlib.c create mode 100644 source/luametatex/source/lua/lmttokenlib.h (limited to 'source/luametatex/source/lua') 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 + || 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 |[{},"dimen"]| */ + lua_newtable(L); /*tex |[{},"dimen",{}]| */ + lua_settable(L, -3); /*tex |[{}]| */ + lua_pushstring(L, tab); /*tex |[{},"dimen"]| */ + lua_gettable(L, -2); /*tex |[{},{}]| */ + luaL_newmetatable(L, mttab); /*tex |[{},{},{}]| */ + lua_pushstring(L, "__index"); /*tex |[{},{},{},"__index"]| */ + lua_pushcfunction(L, getfunc); /*tex |[{},{},{},"__index","getdimen"]| */ + lua_settable(L, -3); /*tex |[{},{},{}]| */ + lua_pushstring(L, "__newindex"); /*tex |[{},{},{},"__newindex"]| */ + lua_pushcfunction(L, setfunc); /*tex |[{},{},{},"__newindex","setdimen"]| */ + lua_settable(L, -3); /*tex |[{},{},{}]| */ + lua_setmetatable(L, -2); /*tex |[{},{}]| : assign the metatable */ + lua_pop(L, 1); /*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 : "", 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 + +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 + + 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 + + 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 +# 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, ""); + } else if (ar.namewhat) { + lua_pushstring(L, ar.namewhat); + } else if (ar.what) { + lua_pushstring(L, ar.what); + } else { + lua_pushliteral(L, ""); + } + 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 : ""))); + 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 expected", detail); +} + +static void mplib_aux_invalid_object_error(const char * detail) +{ + tex_formatted_error("mp lib","lua 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); + } 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, "", *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, "", *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 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 + + + + + \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) +{ /* */ + 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) +{ /* */ + 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) +{ + /* */ + 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); + /* */ + lua_replace(L, -3); + /* */ + lua_rawseti(L, -2, n); /* actually it is a hash */ + } + return 0; +} + +static int nodelib_direct_setproperty(lua_State *L) +{ + /* */ + 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); + /* */ + lua_replace(L, 1); + /* */ + 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) +{ /* */ + 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) +{ /* */ + 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) +{ /* */ + halfword n = lmt_check_isnode(L, 2); + if (n) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + /*
*/ + lua_rawgeti(L, -1, n); + } else { + lua_pushnil(L); + } + return 1; +} + +static int nodelib_set_property_t(lua_State *L) +{ + /*
*/ + halfword n = lmt_check_isnode(L, 2); + if (n) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + /*
*/ + lua_insert(L, -2); + /*
*/ + 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); + } + /* */ + lmt_node_memory_state.node_properties_id = luaL_ref(L, LUA_REGISTRYINDEX); + /* not needed, so unofficial */ + lua_pushstring(L, NODE_PROPERTIES_DIRECT); + /* */ + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + /* */ + lua_settable(L, LUA_REGISTRYINDEX); + /* */ + lua_pushstring(L, NODE_PROPERTIES_INDIRECT); + /* */ + lua_newtable(L); + /* */ + luaL_newmetatable(L, NODE_PROPERTIES_INSTANCE); + /* */ + luaL_setfuncs(L, nodelib_p, 0); + /* */ + lua_setmetatable(L, -2); + /* */ + 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 +# include + +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 */ + 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(|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(|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 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 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, "", n->cs, n->cmd, n->chr, get_token_reference(n->chr)); + } else { + lua_pushfstring(L, "", 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 |{}| |\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 -- cgit v1.2.3