summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua/lmtnodelib.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua/lmtnodelib.c')
-rw-r--r--source/luametatex/source/lua/lmtnodelib.c10324
1 files changed, 10324 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtnodelib.c b/source/luametatex/source/lua/lmtnodelib.c
new file mode 100644
index 000000000..ff98a7064
--- /dev/null
+++ b/source/luametatex/source/lua/lmtnodelib.c
@@ -0,0 +1,10324 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This module is one of the backbones on \LUAMETATEX. It has gradually been extended based on
+ experiences in \CONTEXT\ \MKIV\ and later \LMTX. There are many helpers here and the main
+ reason is that the more callbacks one enables and the more one does in them, the larger the
+ impact on performance.
+
+ After doing lots of tests with \LUATEX\ and \LUAJITTEX, with and without jit, and with and
+ without ffi, I came to the conclusion that userdata prevents a speedup. I also found that the
+ checking of metatables as well as assignment comes with overhead that can't be neglected. This
+ is normally not really a problem but when processing fonts for more complex scripts it's quite
+ some overhead. So \unknown\ direct nodes were introduced (we call them nuts in \CONTEXT).
+
+ Because the userdata approach has some benefits, we keep that interface too. We did some
+ experiments with fast access (assuming nodes), but eventually settled for the direct approach.
+ For code that is proven to be okay, one can use the direct variants and operate on nodes more
+ directly. Currently these are numbers but don't rely on that property; treat them aslhmin
+
+ abstractions. An important aspect is that one cannot mix both methods, although with |tonode|
+ and |todirect| one can cast representations.
+
+ So the advice is: use the indexed (userdata) approach when possible and investigate the direct
+ one when speed might be an issue. For that reason we also provide some get* and set* functions
+ in the top level node namespace. There is a limited set of getters for nodes and a generic
+ getfield to complement them. The direct namespace has a few more.
+
+ Keep in mind that such speed considerations only make sense when we're accessing nodes millions
+ of times (which happens in font processing for instance). Setters are less important as
+ documents have not that many content related nodes and setting many thousands of properties is
+ hardly a burden contrary to millions of consultations. And with millions, we're talking of tens
+ of millions which is not that common.
+
+ Another change is that |__index| and |__newindex| are (as expected) exposed to users but do no
+ checking. The getfield and setfield functions do check. In fact, a fast mode can be simulated
+ by fast_getfield = __index but the (measured) benefit on average runs is not that large (some
+ 5\% when we also use the other fast ones) which is easily nilled by inefficient coding. The
+ direct variants on the other hand can be significantly faster but with the drawback of lack of
+ userdata features. With respect to speed: keep in mind that measuring a speedup on these
+ functions is not representative for a normal run, where much more happens.
+
+ A user should beware of the fact that messing around with |prev|, |next| and other links can
+ lead to crashes. Don't complain about this: you get what you ask for. Examples are bad loops
+ in nodes lists that make the program run out of stack space.
+
+ The code below differs from the \LUATEX\ code in that it drops some userdata related
+ accessors. These can easily be emulates in \LUA, which is what we do in \CONTEXT\ \LMTX. Also,
+ some optimizations, like using macros and dedicated |getfield| and |setfield| functions for
+ userdata and direct nodes were removed because on a regular run there is not much impact and
+ the less code we have, the better. In the early days of \LUATEX\ it really did improve the
+ overall performance but computers (as well as compilers) have become better. But still, it
+ could be that \LUATEX\ has a better performance here; so be it. A performance hit can also be
+ one of the side effects of the some more rigourous testing of direct node validity introduced
+ here.
+
+ Attribute nodes are special as their prev and subtype fields are used for other purposes.
+ Setting them can confuse the checkers but we don't check each case for performance reasons.
+ Messing a list up is harmless and only affects functionality which is the users responsibility
+ anyway.
+
+ In \LUAMETATEX\ nodes can have different names and properties as in \LUATEX. Some might be
+ backported but that is kind of dangerous as macro packages other than \CONTEXT\ depend on
+ stability of \LUATEX. (It's one of the reasons for \LUAMETATEX\ being around: it permits us
+ to move on).
+
+ Todo: getters/setters for leftovers.
+
+*/
+
+/*
+ direct_prev_id(n) => returns prev and id
+ direct_next_id(n) => returns next and id
+*/
+
+# include "luametatex.h"
+
+/* # define NODE_METATABLE_INSTANCE "node.instance" */
+/* # define NODE_PROPERTIES_DIRECT "node.properties" */
+/* # define NODE_PROPERTIES_INDIRECT "node.properties.indirect" */
+/* # define NODE_PROPERTIES_INSTANCE "node.properties.instance" */
+
+/*tex
+
+ There is a bit of checking for validity of direct nodes but of course one can still create
+ havoc by using flushed nodes, setting bad links, etc.
+
+ Although we could gain a little by moving the body of the valid checker into the caller (that
+ way the field variables might be shared) there is no real measurable gain in that on a regular
+ run. So, in the end I settled for function calls.
+
+*/
+
+halfword lmt_check_isdirect(lua_State *L, int i)
+{
+ halfword n = lmt_tohalfword(L, i);
+ return n && _valid_node_(n) ? n : null;
+}
+
+inline static halfword nodelib_valid_direct_from_index(lua_State *L, int i)
+{
+ halfword n = lmt_tohalfword(L, i);
+ return n && _valid_node_(n) ? n : null;
+}
+
+inline static void nodelib_push_direct_or_nil(lua_State *L, halfword n)
+{
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+inline static void nodelib_push_direct_or_nil_node_prev(lua_State *L, halfword n)
+{
+ if (n) {
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+inline static void nodelib_push_node_on_top(lua_State *L, halfword n)
+{
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, -2);
+ lua_setmetatable(L, -2);
+}
+
+/*tex
+ Many of these small functions used to be macros but that no longer pays off because compilers
+ became better (for instance at deciding when to inline small functions). We could have explicit
+ inline variants of these too but normally the compiler will inline small functions anyway.
+
+*/
+
+static halfword lmt_maybe_isnode(lua_State *L, int i)
+{
+ halfword *p = lua_touserdata(L, i);
+ halfword n = null;
+ if (p && lua_getmetatable(L, i)) {
+ lua_get_metatablelua(node_instance);
+ if (lua_rawequal(L, -1, -2)) {
+ n = *p;
+ }
+ lua_pop(L, 2);
+ }
+ return n;
+}
+
+halfword lmt_check_isnode(lua_State *L, int i)
+{
+ halfword n = lmt_maybe_isnode(L, i);
+ if (! n) {
+ // formatted_error("node lib", "lua <node> expected, not an object with type %s", luaL_typename(L, i));
+ luaL_error(L, "invalid node");
+ }
+ return n;
+}
+
+/* helpers */
+
+static void nodelib_push_direct_or_node(lua_State *L, int direct, halfword n)
+{
+ if (n) {
+ if (direct) {
+ lua_pushinteger(L, n);
+ } else {
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, 1);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void nodelib_push_direct_or_node_node_prev(lua_State *L, int direct, halfword n)
+{
+ if (n) {
+ node_prev(n) = null;
+ if (direct) {
+ lua_pushinteger(L, n);
+ } else {
+ *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
+ lua_getmetatable(L, 1);
+ lua_setmetatable(L, -2);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static halfword nodelib_direct_or_node_from_index(lua_State *L, int direct, int i)
+{
+ if (direct) {
+ return nodelib_valid_direct_from_index(L, i);
+ } else if (lua_isuserdata(L, i)) {
+ return lmt_check_isnode(L, i);
+ } else {
+ return null;
+ }
+}
+
+halfword lmt_check_isdirectornode(lua_State *L, int i, int *isdirect)
+{
+ *isdirect = ! lua_isuserdata(L, i);
+ return *isdirect ? nodelib_valid_direct_from_index(L, i) : lmt_check_isnode(L, i);
+}
+
+static void nodelib_push_attribute_data(lua_State* L, halfword n)
+{
+ if (node_type(n) == attribute_list_subtype) {
+ lua_newtable(L);
+ n = node_next(n);
+ while (n) {
+ lua_pushinteger(L, attribute_value(n));
+ lua_rawseti(L, -2, attribute_index(n));
+ n = node_next(n);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+/*tex Another shortcut: */
+
+inline static singleword nodelib_getdirection(lua_State *L, int i)
+{
+ return ((lua_type(L, i) == LUA_TNUMBER) ? checked_direction_value(lmt_tohalfword(L, i)) : direction_def_value);
+}
+
+/*tex
+
+ This routine finds the numerical value of a string (or number) at \LUA\ stack index |n|. If it
+ is not a valid node type |-1| is returned.
+
+*/
+
+static quarterword nodelib_aux_get_node_type_id_from_name(lua_State *L, int n, node_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (int j = 0; data[j].id != -1; j++) {
+ if (s == data[j].name) {
+ if (data[j].visible) {
+ return (quarterword) j;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ return unknown_node;
+}
+
+static quarterword nodelib_aux_get_node_subtype_id_from_name(lua_State *L, int n, value_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (quarterword j = 0; data[j].id != -1; j++) {
+ if (s == data[j].name) {
+ return j;
+ }
+ }
+ }
+ return unknown_subtype;
+}
+
+static quarterword nodelib_aux_get_field_index_from_name(lua_State *L, int n, value_info *data)
+{
+ if (data) {
+ const char *s = lua_tostring(L, n);
+ for (quarterword j = 0; data[j].name; j++) {
+ if (s == data[j].name) {
+ return j;
+ }
+ }
+ }
+ return unknown_field;
+}
+
+static quarterword nodelib_aux_get_valid_node_type_id(lua_State *L, int n)
+{
+ quarterword i = unknown_node;
+ switch (lua_type(L, n)) {
+ case LUA_TSTRING:
+ i = nodelib_aux_get_node_type_id_from_name(L, n, lmt_interface.node_data);
+ if (i == unknown_node) {
+ luaL_error(L, "invalid node type id: %s", lua_tostring(L, n));
+ }
+ break;
+ case LUA_TNUMBER:
+ i = lmt_toquarterword(L, n);
+ if (! tex_nodetype_is_visible(i)) {
+ luaL_error(L, "invalid node type id: %d", i);
+ }
+ break;
+ default:
+ luaL_error(L, "invalid node type id");
+ }
+ return i;
+}
+
+int lmt_get_math_style(lua_State *L, int n, int dflt)
+{
+ int i = -1;
+ switch (lua_type(L, n)) {
+ case LUA_TNUMBER:
+ i = lmt_tointeger(L, n);
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_style_values);
+ break;
+ }
+ if (i >= 0 && i <= cramped_script_script_style) {
+ return i;
+ } else {
+ return dflt;
+ }
+}
+
+int lmt_get_math_parameter(lua_State *L, int n, int dflt)
+{
+ int i;
+ switch (lua_type(L, n)) {
+ case LUA_TNUMBER:
+ i = lmt_tointeger(L, n);
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_parameter_values);
+ break;
+ default:
+ i = -1;
+ break;
+ }
+ if (i >= 0 && i < math_parameter_last) {
+ return i;
+ } else {
+ return dflt;
+ }
+}
+
+/*tex
+
+ Creates a userdata object for a number found at the stack top, if it is representing a node
+ (i.e. an pointer into |varmem|). It replaces the stack entry with the new userdata, or pushes
+ |nil| if the number is |null|, or if the index is definately out of range. This test could be
+ improved.
+
+*/
+
+void lmt_push_node(lua_State *L)
+{
+ halfword n = null;
+ if (lua_type(L, -1) == LUA_TNUMBER) {
+ n = lmt_tohalfword(L, -1);
+ }
+ lua_pop(L, 1);
+ if ((! n) || (n > lmt_node_memory_state.nodes_data.allocated)) {
+ lua_pushnil(L);
+ } else {
+ halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ }
+ return;
+}
+
+void lmt_push_node_fast(lua_State *L, halfword n)
+{
+ if (n) {
+ halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+void lmt_push_directornode(lua_State *L, halfword n, int isdirect)
+{
+ if (! n) {
+ lua_pushnil(L);
+ } else if (isdirect) {
+ lua_push_integer(L, n);
+ } else {
+ lmt_push_node_fast(L, n);
+ }
+}
+
+/*tex getting and setting fields (helpers) */
+
+static int nodelib_getlist(lua_State *L, int n)
+{
+ if (lua_isuserdata(L, n)) {
+ return lmt_check_isnode(L, n);
+ } else {
+ return null;
+ }
+}
+
+/*tex converts type strings to type ids */
+
+static int nodelib_shared_id(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ int i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data);
+ if (i >= 0) {
+ lua_pushinteger(L, i);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getid */
+
+static int nodelib_direct_getid(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_pushinteger(L, node_type(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getsubtype */
+/* node.direct.setsubtype */
+
+static int nodelib_direct_getsubtype(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_pushinteger(L, node_subtype(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setsubtype(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ node_subtype(n) = lmt_toquarterword(L, 2);
+ }
+ return 0;
+}
+
+/* node.direct.getexpansion */
+/* node.direct.setexpansion */
+
+static int nodelib_direct_getexpansion(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_expansion(n));
+ break;
+ case kern_node:
+ lua_pushinteger(L, kern_expansion(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setexpansion(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword e = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ e = (halfword) lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_expansion(n) = e;
+ break;
+ case kern_node:
+ kern_expansion(n) = e;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getfont */
+/* node.direct.setfont */
+
+static int nodelib_direct_getfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_font(n));
+ break;
+ case glue_node:
+ lua_pushinteger(L, glue_font(n));
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ break;
+ case delimiter_node:
+ lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_character(n) = lmt_tohalfword(L, 3);
+ }
+ break;
+ case rule_node:
+ tex_set_rule_font(n, lmt_tohalfword(L, 2));
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_character(n) = lmt_tohalfword(L, 3);
+ }
+ break;
+ case glue_node:
+ glue_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getchardict(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_properties(n));
+ lua_pushinteger(L, glyph_group(n));
+ lua_pushinteger(L, glyph_index(n));
+ lua_pushinteger(L, glyph_font(n));
+ lua_pushinteger(L, glyph_character(n));
+ return 5;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_properties(n));
+ lua_pushinteger(L, kernel_math_group(n));
+ lua_pushinteger(L, kernel_math_index(n));
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n),0));
+ lua_pushinteger(L, kernel_math_character(n));
+ return 5;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setchardict(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_properties(n) = lmt_optquarterword(L, 2, 0);
+ glyph_group(n) = lmt_optquarterword(L, 3, 0);
+ glyph_index(n) = lmt_opthalfword(L, 4, 0);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_properties(n) = lmt_optquarterword(L, 2, 0);
+ kernel_math_group(n) = lmt_optquarterword(L, 3, 0);
+ kernel_math_index(n) = lmt_opthalfword(L, 4, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getchar */
+/* node.direct.setchar */
+
+static int nodelib_direct_getchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch(node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_character(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_character(n));
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_character(n));
+ break;
+ case delimiter_node:
+ /* used in wide fonts */
+ lua_pushinteger(L, delimiter_small_character(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case rule_node:
+ rule_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_character(n) = lmt_tohalfword(L, 2);
+ break;
+ case delimiter_node:
+ /* used in wide fonts */
+ delimiter_small_character(n) = lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* bonus */
+
+static int nodelib_direct_getcharspec(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ return 2;
+ case rule_node:
+ lua_pushinteger(L, rule_character(n));
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ break;
+ case simple_noad:
+ n = noad_nucleus(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_character(n));
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ lua_pushinteger(L, kernel_math_family(n));
+ return 3;
+ case delimiter_node:
+ lua_pushinteger(L, delimiter_small_character(n));
+ lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
+ lua_pushinteger(L, delimiter_small_family(n));
+ return 3;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getfam */
+/* node.direct.setfam */
+
+static int nodelib_direct_getfam(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch(node_type(n)) {
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_family(n));
+ break;
+ case delimiter_node:
+ lua_pushinteger(L, delimiter_small_family(n));
+ break;
+ case fraction_noad:
+ case simple_noad:
+ lua_pushinteger(L, noad_family(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, tex_get_rule_family(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setfam(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && lua_type(L, 2) == LUA_TNUMBER) {
+ switch (node_type(n)) {
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_family(n) = lmt_tohalfword(L, 2);
+ break;
+ case delimiter_node:
+ delimiter_small_family(n) = lmt_tohalfword(L, 2);
+ break;
+ case fraction_noad:
+ case simple_noad:
+ set_noad_family(n, lmt_tohalfword(L, 2));
+ break;
+ case rule_node:
+ tex_set_rule_family(n, lmt_tohalfword(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getstate(n) */
+/* node.direct.setstate(n) */
+
+/*tex
+ A zero state is considered to be false or basically the same as \quote {unset}. That way we
+ can are compatible with an unset property. This is cheaper on testing too. But I might
+ reconsider this at some point. (In which case I need to adapt the context source but by then
+ we have a lua/lmt split.)
+*/
+
+static int nodelib_direct_getstate(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int state = 0;
+ switch (node_type(n)) {
+ case glyph_node:
+ state = get_glyph_state(n);
+ break;
+ case hlist_node:
+ case vlist_node:
+ state = box_package_state(n);
+ break;
+ default:
+ goto NOPPES;
+ }
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_pushboolean(L, lua_tointeger(L, 2) == state);
+ return 1;
+ } else if (state) {
+ lua_pushinteger(L, state);
+ return 1;
+ } else {
+ /*tex Indeed, |nil|. */
+ }
+ }
+ NOPPES:
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setstate(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ set_glyph_state(n, lmt_opthalfword(L, 2, 0));
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_package_state(n) = (singleword) lmt_opthalfword(L, 2, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getclass(n,main,left,right) */
+/* node.direct.setclass(n,main,left,right) */
+
+static int nodelib_direct_getclass(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ lua_push_integer(L, get_noad_main_class(n));
+ lua_push_integer(L, get_noad_left_class(n));
+ lua_push_integer(L, get_noad_right_class(n));
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setclass(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ set_noad_main_class(n, lmt_tohalfword(L, 2));
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ set_noad_left_class(n, lmt_tohalfword(L, 3));
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ set_noad_right_class(n, lmt_tohalfword(L, 4));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getscript(n) */
+/* node.direct.setscript(n) */
+
+static int nodelib_direct_getscript(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node && get_glyph_script(n)) {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ lua_pushboolean(L, lua_tointeger(L, 2) == get_glyph_script(n));
+ } else {
+ lua_pushinteger(L, get_glyph_script(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setscript(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_script(n, lmt_opthalfword(L, 2, 0));
+ }
+ return 0;
+}
+
+/* node.direct.getlang */
+/* node.direct.setlang */
+
+static int nodelib_direct_getlanguage(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, get_glyph_language(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setlanguage(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_language(n, lmt_opthalfword(L, 2, 0));
+ }
+ return 0;
+}
+
+/* node.direct.getattributelist */
+/* node.direct.setattributelist */
+
+static int nodelib_direct_getattributelist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n)) && node_attr(n)) {
+ if (lua_toboolean(L, 2)) {
+ nodelib_push_attribute_data(L, n);
+ } else {
+ lua_pushinteger(L, node_attr(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static void nodelib_aux_setattributelist(lua_State *L, halfword n, int index)
+{
+ if (n && tex_nodetype_has_attributes(node_type(n))) {
+ halfword a = null;
+ switch (lua_type(L, index)) {
+ case LUA_TNUMBER:
+ {
+ halfword m = nodelib_valid_direct_from_index(L, index);
+ if (m) {
+ quarterword t = node_type(m);
+ if (t == attribute_node) {
+ if (node_subtype(m) == attribute_list_subtype) {
+ a = m;
+ } else {
+ /* invalid list, we could make a proper one if needed */
+ }
+ } else if (tex_nodetype_has_attributes(t)) {
+ a = node_attr(m);
+ }
+ }
+ }
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, index)) {
+ a = tex_current_attribute_list();
+ }
+ break;
+ case LUA_TTABLE:
+ {
+ /* kind of slow because we need a sorted inject */
+ lua_pushnil(L); /* push initial key */
+ while (lua_next(L, index)) {
+ halfword key = lmt_tohalfword(L, -2);
+ halfword val = lmt_tohalfword(L, -1);
+ a = tex_patch_attribute_list(a, key, val);
+ lua_pop(L, 1); /* pop value, keep key */
+ }
+ lua_pop(L, 1); /* pop key */
+ }
+ break;
+ }
+ tex_attach_attribute_list_attribute(n, a);
+ }
+}
+
+static int nodelib_direct_setattributelist(lua_State *L)
+{
+ nodelib_aux_setattributelist(L, nodelib_valid_direct_from_index(L, 1), 2);
+ return 0;
+}
+
+/* node.direct.getpenalty */
+/* node.direct.setpenalty */
+
+static int nodelib_direct_getpenalty(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case penalty_node:
+ lua_pushinteger(L, penalty_amount(n));
+ break;
+ case disc_node:
+ lua_pushinteger(L, disc_penalty(n));
+ break;
+ case math_node:
+ lua_pushinteger(L, math_penalty(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setpenalty(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case penalty_node:
+ penalty_amount(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ case disc_node:
+ disc_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ case math_node:
+ math_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getnucleus */
+/* node.direct.getsub */
+/* node.direct.getsup */
+
+static int nodelib_direct_getnucleus(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_nucleus(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setnucleus(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_nucleus(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getsub(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_subscr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getsubpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_subprescr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setsub(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_subscr(n) = nodelib_valid_direct_from_index(L, 2);
+ // if (lua_gettop(L) > 2) {
+ // noad_subprescr(n) = nodelib_valid_direct_from_index(L, 3);
+ // }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setsubpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_subprescr(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getsup(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_supscr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getsuppre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_supprescr(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getprime(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, noad_prime(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setsup(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_supscr(n) = nodelib_valid_direct_from_index(L, 2);
+ // if (lua_gettop(L) > 2) {
+ // supprescr(n) = nodelib_valid_direct_from_index(L, 3);
+ // }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setsuppre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_supprescr(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setprime(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ noad_prime(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getkern (overlaps with getwidth) */
+/* node.direct.setkern (overlaps with getwidth) */
+
+static int nodelib_direct_getkern(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case kern_node:
+ lua_pushnumber(L, kern_amount(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, kern_expansion(n));
+ return 2;
+ } else {
+ break;
+ }
+ case math_node:
+ lua_pushinteger(L, math_surround(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setkern(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case kern_node:
+ kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ node_subtype(n) = lmt_toquarterword(L, 3);
+ }
+ break;
+ case math_node:
+ math_surround(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getdirection */
+/* node.direct.setdirection */
+
+static int nodelib_direct_getdirection(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case dir_node:
+ lua_pushinteger(L, dir_direction(n));
+ lua_pushboolean(L, node_subtype(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ break;
+ case par_node:
+ lua_pushinteger(L, par_dir(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setdirection(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case dir_node:
+ dir_direction(n) = nodelib_getdirection(L, 2);
+ if (lua_type(L, 3) == LUA_TBOOLEAN) {
+ if (lua_toboolean(L, 3)) {
+ node_subtype(n) = (quarterword) (lua_toboolean(L, 3) ? cancel_dir_subtype : normal_dir_subtype);
+ }
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_dir(n) = (singleword) nodelib_getdirection(L, 2);
+ break;
+ case par_node:
+ par_dir(n) = nodelib_getdirection(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getanchors */
+/* node.direct.setanchors */
+
+static int nodelib_direct_getanchors(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_anchor(n)) {
+ lua_pushinteger(L, box_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_source_anchor(n)) {
+ lua_pushinteger(L, box_source_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_target_anchor(n)) {
+ lua_pushinteger(L, box_target_anchor(n));
+ } else {
+ lua_pushnil(L);
+ }
+ /* bonus detail: source, target */
+ if (box_source_anchor(n)) {
+ lua_pushinteger(L, box_anchor(n) & 0x0FFF);
+ } else {
+ lua_pushnil(L);
+ }
+ if (box_target_anchor(n)) {
+ lua_pushinteger(L, (box_anchor(n) >> 16) & 0x0FFF);
+ } else {
+ lua_pushnil(L);
+ }
+ return 5;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (noad_source(n)) {
+ lua_pushinteger(L, noad_source(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setanchors(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ box_anchor(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ break;
+ default:
+ box_anchor(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER :
+ box_source_anchor(n) = lmt_tohalfword(L, 3);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 3)) {
+ break;
+ }
+ default:
+ box_source_anchor(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 4)) {
+ case LUA_TNUMBER:
+ box_target_anchor(n) = lmt_tohalfword(L, 4);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 4)) {
+ break;
+ }
+ default:
+ box_target_anchor(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER :
+ noad_source(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ default:
+ noad_source(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getxoffset */
+/* node.direct.getyoffset */
+/* node.direct.getoffsets */
+/* node.direct.setxoffset */
+/* node.direct.setyoffset */
+/* node.direct.setoffsets */
+
+static int nodelib_direct_getoffsets(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_x_offset(n));
+ lua_pushinteger(L, glyph_y_offset(n));
+ lua_pushinteger(L, glyph_left(n));
+ lua_pushinteger(L, glyph_right(n));
+ lua_pushinteger(L, glyph_raise(n));
+ return 5;
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_x_offset(n));
+ lua_pushinteger(L, box_y_offset(n));
+ return 2;
+ case rule_node:
+ lua_pushinteger(L, rule_x_offset(n));
+ lua_pushinteger(L, rule_y_offset(n));
+ lua_pushinteger(L, rule_left(n));
+ lua_pushinteger(L, rule_right(n));
+ return 4;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setoffsets(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_left(n) = (halfword) lmt_roundnumber(L, 4);
+ }
+ if (lua_type(L, 5) == LUA_TNUMBER) {
+ glyph_right(n) = (halfword) lmt_roundnumber(L, 5);
+ }
+ if (lua_type(L, 6) == LUA_TNUMBER) {
+ glyph_raise(n) = (halfword) lmt_roundnumber(L, 6);
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ box_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ rule_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ rule_left(n) = (halfword) lmt_roundnumber(L, 4);
+ }
+ if (lua_type(L, 5) == LUA_TNUMBER) {
+ rule_right(n) = (halfword) lmt_roundnumber(L, 5);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addxoffset(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ rule_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addyoffset(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ tex_check_box_geometry(n);
+ break;
+ case rule_node:
+ rule_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* */
+
+static int nodelib_direct_addmargins(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_left(n) += (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_right(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_raise(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ break;
+ case rule_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ rule_left(n) += (halfword) lmt_roundnumber(L, 2);
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ rule_right(n) += (halfword) lmt_roundnumber(L, 3);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_addxymargins(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ scaled y = glyph_y_scale(n);
+ double sx, sy;
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ sx = 1;
+ } else {
+ sx = 0.001 * x;
+ }
+ if (y == 0 || y == 1000) {
+ sy = 1;
+ } else {
+ sy = 0.001 * y;
+ }
+ } else {
+ if (x == 0 || x == 1000) {
+ sx = 0.001 * s;
+ } else {
+ sx = 0.000001 * s * x;
+ }
+ if (y == 0 || y == 1000) {
+ sy = 0.001 * s;
+ } else {
+ sy = 0.000001 * s * y;
+ }
+ }
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_left(n) += scaledround(sx * lua_tonumber(L, 2));
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_right(n) += scaledround(sx * lua_tonumber(L, 3));
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_raise(n) += scaledround(sy * lua_tonumber(L, 4));
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getscale */
+/* node.direct.getxscale */
+/* node.direct.getyscale */
+/* node.direct.getxyscale */
+/* node.direct.setscale */
+/* node.direct.setxscale */
+/* node.direct.setyscale */
+/* node.direct.setxyscale */
+
+static int nodelib_direct_getscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, glyph_scale(n));
+ return 1;
+ }
+ return 0;
+}
+
+static int nodelib_direct_getscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, glyph_scale(n));
+ lua_pushinteger(L, glyph_x_scale(n));
+ lua_pushinteger(L, glyph_y_scale(n));
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_setscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ glyph_scale(n) = (halfword) lmt_roundnumber(L, 2);
+ if (! glyph_scale(n)) {
+ glyph_scale(n) = 1000;
+ }
+ }
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_x_scale(n)) {
+ glyph_x_scale(n) = 1000;
+ }
+ }
+ if (lua_type(L, 4) == LUA_TNUMBER) {
+ glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 4);
+ if (! glyph_y_scale(n)) {
+ glyph_y_scale(n) = 1000;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getxscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ double d;
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ goto DONE;
+ } else {
+ d = 0.001 * x;
+ }
+ } else if (x == 0 || x == 1000) {
+ d = 0.001 * s;
+ } else {
+ d = 0.000001 * s * x;
+ }
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ DONE:
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+static int nodelib_direct_xscaled(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_Number v = lua_tonumber(L, 2);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ if (s == 0 || s == 1000) {
+ if (x == 0 || x == 1000) {
+ /* okay */
+ } else {
+ v = 0.001 * x * v;
+ }
+ } else if (x == 0 || x == 1000) {
+ v = 0.001 * s * v;
+ } else {
+ v = 0.000001 * s * x * v;
+ }
+ }
+ lua_pushnumber(L, v);
+ return 1;
+}
+
+static int nodelib_direct_getyscale(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled y = glyph_y_scale(n);
+ double d;
+ if (s == 0 || s == 1000) {
+ if (y == 0 || y == 1000) {
+ goto DONE;
+ } else {
+ d = 0.001 * y;
+ }
+ } else if (y == 0 || y == 1000) {
+ d = 0.001 * s;
+ } else {
+ d = 0.000001 * s * y;
+ }
+ lua_pushnumber(L, d);
+ return 1;
+ }
+ DONE:
+ lua_pushinteger(L, 1);
+ return 1;
+}
+
+static int nodelib_direct_yscaled(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_Number v = lua_tonumber(L, 2);
+ if (n && node_type(n) == glyph_node) {
+ scaled s = glyph_scale(n);
+ scaled y = glyph_y_scale(n);
+ if (s == 0 || s == 1000) {
+ if (y == 0 || y == 1000) {
+ /* okay */
+ } else {
+ v = 0.001 * y * v;
+ }
+ } else if (y == 0 || y == 1000) {
+ v = 0.001 * s * v;
+ } else {
+ v = 0.000001 * s * y * v;
+ }
+ }
+ lua_pushnumber(L, v);
+ return 1;
+}
+
+static void nodelib_aux_pushxyscales(lua_State *L, halfword n)
+{
+ scaled s = glyph_scale(n);
+ scaled x = glyph_x_scale(n);
+ scaled y = glyph_y_scale(n);
+ double dx;
+ double dy;
+ if (s && s != 1000) {
+ dx = (x && x != 1000) ? 0.000001 * s * x : 0.001 * s;
+ } else if (x && x != 1000) {
+ dx = 0.001 * x;
+ } else {
+ lua_pushinteger(L, 1);
+ goto DONEX;
+ }
+ lua_pushnumber(L, dx);
+ DONEX:
+ if (s && s != 1000) {
+ dy = (y && y != 1000) ? 0.000001 * s * y : 0.001 * s;
+ } else if (y && y != 1000) {
+ dy = 0.001 * y;
+ } else {
+ lua_pushinteger(L, 1);
+ goto DONEY;
+ }
+ lua_pushnumber(L, dy);
+ DONEY: ;
+}
+
+static int nodelib_direct_getxyscales(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ nodelib_aux_pushxyscales(L, n);
+ } else {
+ lua_pushinteger(L, 1);
+ lua_pushinteger(L, 1);
+ }
+ return 2;
+}
+
+/* node.direct.getdisc */
+/* node.direct.setdisc */
+
+/*tex
+ For the moment we don't provide setters for math discretionaries, mainly because these are
+ special and I don't want to waste time on checking and intercepting errors. They are not that
+ widely used anyway.
+*/
+
+static int nodelib_direct_getdisc(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_head(n));
+ if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
+ nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
+ return 6;
+ } else {
+ return 3;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_pre_break(n));
+ nodelib_push_direct_or_nil(L, choice_post_break(n));
+ nodelib_push_direct_or_nil(L, choice_no_break(n));
+ if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_pre_break(n)));
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_post_break(n)));
+ nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_post_break(n)));
+ return 6;
+ } else {
+ return 3;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getdiscpart(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ lua_pushinteger(L, get_glyph_discpart(n));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_getpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword h = box_pre_migrated(n);
+ halfword t = tex_tail_of_node_list(h);
+ nodelib_push_direct_or_nil(L, h);
+ nodelib_push_direct_or_nil(L, t);
+ return 2;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_pre_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getpost(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_post_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
+ return 2;
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword h = box_post_migrated(n);
+ halfword t = tex_tail_of_node_list(h);
+ nodelib_push_direct_or_nil(L, h);
+ nodelib_push_direct_or_nil(L, t);
+ return 2;
+ }
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_post_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getreplace(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case disc_node:
+ nodelib_push_direct_or_nil(L, disc_no_break_head(n));
+ nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
+ return 2;
+ case choice_node:
+ if (node_subtype(n) == discretionary_choice_subtype) {
+ nodelib_push_direct_or_nil(L, choice_no_break(n));
+ return 1;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdisc(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ int t = lua_gettop(L) ;
+ if (t > 1) {
+ tex_set_disc_field(n, pre_break_code, nodelib_valid_direct_from_index(L, 2));
+ if (t > 2) {
+ tex_set_disc_field(n, post_break_code, nodelib_valid_direct_from_index(L, 3));
+ if (t > 3) {
+ tex_set_disc_field(n, no_break_code, nodelib_valid_direct_from_index(L, 4));
+ if (t > 4) {
+ node_subtype(n) = lmt_toquarterword(L, 5);
+ if (t > 5) {
+ disc_penalty(n) = lmt_tohalfword(L, 6);
+ }
+ }
+ } else {
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ } else {
+ tex_set_disc_field(n, post_break_code, null);
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ } else {
+ tex_set_disc_field(n, pre_break_code, null);
+ tex_set_disc_field(n, post_break_code, null);
+ tex_set_disc_field(n, no_break_code, null);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdiscpart(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ set_glyph_discpart(n, luaL_optinteger(L, 2, glyph_discpart_unset));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_setpre(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ switch (node_type(n)) {
+ case disc_node:
+ tex_set_disc_field(n, pre_break_code, m);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_pre_migrated(n) = m;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setpost(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ switch (node_type(n)) {
+ case disc_node:
+ tex_set_disc_field(n, post_break_code, m);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_post_migrated(n) = m;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setreplace(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
+ tex_set_disc_field(n, no_break_code, m);
+ }
+ return 0;
+}
+
+/* node.direct.getwidth */
+/* node.direct.setwidth */
+/* node.direct.getheight (for consistency) */
+/* node.direct.setheight (for consistency) */
+/* node.direct.getdepth (for consistency) */
+/* node.direct.setdepth (for consistency) */
+
+/* split ifs for clearity .. compiler will optimize */
+
+static int nodelib_direct_getwidth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_width(n));
+ break;
+ case align_record_node:
+ lua_pushinteger(L, box_width(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, box_size(n));
+ return 2;
+ }
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_width(n));
+ break;
+ case glue_node:
+ case glue_spec_node:
+ lua_pushinteger(L, glue_amount(n));
+ break;
+ case glyph_node:
+ lua_pushnumber(L, tex_glyph_width(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ return 2;
+ }
+ break;
+ case kern_node:
+ lua_pushinteger(L, kern_amount(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushinteger(L, kern_expansion(n));
+ return 2;
+ }
+ break;
+ case math_node:
+ lua_pushinteger(L, math_amount(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setwidth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ box_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ box_size(n) = lmt_roundnumber(L, 3);
+ box_package_state(n) = package_dimension_size_set;
+ }
+ break;
+ case rule_node:
+ rule_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case glue_node:
+ case glue_spec_node:
+ glue_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case kern_node:
+ kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ case math_node:
+ math_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getindex(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_index(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_index(n));
+ break;
+ case mark_node:
+ lua_pushinteger(L, mark_index(n));
+ break;
+ case adjust_node:
+ lua_pushinteger(L, adjust_index(n));
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setindex(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_box_index(index)) {
+ box_index(n) = index;
+ } else {
+ /* error or just ignore */
+ }
+ break;
+ }
+ case insert_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ } else {
+ /* error or just ignore */
+ }
+ break;
+ }
+ case mark_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_mark(index)) {
+ mark_index(n) = index;
+ }
+ }
+ break;
+ case adjust_node:
+ {
+ halfword index = lmt_tohalfword(L, 2);
+ if (tex_valid_adjust_index(index)) {
+ adjust_index(n) = index;
+ }
+ }
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_getheight(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_height(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_height(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_total_height(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, tex_glyph_height(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, noad_height(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setheight(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword h = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ h = lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_height(n) = h;
+ break;
+ case rule_node:
+ rule_height(n) = h;
+ break;
+ case insert_node:
+ insert_total_height(n) = h;
+ break;
+ case fence_noad:
+ noad_height(n) = h;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getdepth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_depth(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, rule_depth(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, insert_max_depth(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, tex_glyph_depth(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, noad_depth(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setdepth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword d = 0;
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ d = lmt_roundnumber(L, 2);
+ }
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_depth(n) = d;
+ break;
+ case rule_node:
+ rule_depth(n) = d;
+ break;
+ case insert_node:
+ insert_max_depth(n) = d;
+ break;
+ case fence_noad:
+ noad_depth(n) = d;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_gettotal(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, (lua_Integer) box_total(n));
+ break;
+ case rule_node:
+ lua_pushinteger(L, (lua_Integer) rule_total(n));
+ break;
+ case insert_node:
+ lua_pushinteger(L, (lua_Integer) insert_total_height(n));
+ break;
+ case glyph_node:
+ lua_pushinteger(L, (lua_Integer) tex_glyph_total(n));
+ break;
+ case fence_noad:
+ lua_pushinteger(L, (lua_Integer) noad_total(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_settotal(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case insert_node:
+ insert_total_height(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L,2) : 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getshift */
+/* node.direct.setshift */
+
+static int nodelib_direct_getshift(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_shift_amount(n));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setshift(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ box_shift_amount(n) = (halfword) lmt_roundnumber(L,2);
+ } else {
+ box_shift_amount(n) = 0;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.hasgeometry */
+/* node.direct.getgeometry */
+/* node.direct.setgeometry */
+/* node.direct.getorientation */
+/* node.direct.setorientation */
+
+static int nodelib_direct_hasgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_geometry(n)) {
+ lua_pushinteger(L, box_geometry(n));
+ return 1;
+ }
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+static int nodelib_direct_getgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ if (box_geometry(n)) {
+ lua_pushinteger(L, box_geometry(n));
+ if (lua_toboolean(L, 2)) {
+ lua_pushboolean(L, tex_has_box_geometry(n, offset_geometry));
+ lua_pushboolean(L, tex_has_box_geometry(n, orientation_geometry));
+ lua_pushboolean(L, tex_has_box_geometry(n, anchor_geometry));
+ return 4;
+ } else {
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+static int nodelib_direct_setgeometry(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ box_geometry(n) = (singleword) lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getorientation(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ lua_pushinteger(L, box_orientation(n));
+ lua_pushinteger(L, box_x_offset(n));
+ lua_pushinteger(L, box_y_offset(n));
+ lua_pushinteger(L, box_w_offset(n));
+ lua_pushinteger(L, box_h_offset(n));
+ lua_pushinteger(L, box_d_offset(n));
+ return 6;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setorientation(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ box_orientation(n) = lmt_tohalfword(L, 2);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 2)) {
+ break;
+ }
+ default:
+ box_orientation(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ box_x_offset(n) = lmt_tohalfword(L, 3);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 3)) {
+ break;
+ }
+ default:
+ box_x_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 4)) {
+ case LUA_TNUMBER:
+ box_y_offset(n) = lmt_tohalfword(L, 4);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 4)) {
+ break;
+ }
+ default:
+ box_y_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 5)) {
+ case LUA_TNUMBER:
+ box_w_offset(n) = lmt_tohalfword(L, 5);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 5)) {
+ break;
+ }
+ default:
+ box_w_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 6)) {
+ case LUA_TNUMBER:
+ box_h_offset(n) = lmt_tohalfword(L, 6);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 6)) {
+ break;
+ }
+ default:
+ box_h_offset(n) = 0;
+ break;
+ }
+ switch (lua_type(L, 7)) {
+ case LUA_TNUMBER:
+ box_d_offset(n) = lmt_tohalfword(L, 7);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, 7)) {
+ break;
+ }
+ default:
+ box_d_offset(n) = 0;
+ break;
+ }
+ tex_check_box_geometry(n);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.setoptions */
+/* node.direct.getoptions */
+
+static int nodelib_direct_getoptions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_options(n));
+ return 1;
+ case disc_node:
+ lua_pushinteger(L, disc_options(n));
+ return 1;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ lua_pushinteger(L, noad_options(n));
+ return 1;
+ case math_char_node:
+ case math_text_char_node:
+ lua_pushinteger(L, kernel_math_options(n));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setoptions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ set_glyph_options(n, lmt_tohalfword(L, 2));
+ break;
+ case disc_node:
+ set_disc_options(n, lmt_tohalfword(L, 2));
+ break;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ noad_options(n) = lmt_tohalfword(L, 2);
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ kernel_math_options(n) = lmt_tohalfword(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getwhd */
+/* node.direct.setwhd */
+
+static int nodelib_direct_getwhd(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_width(n));
+ lua_pushinteger(L, box_height(n));
+ lua_pushinteger(L, box_depth(n));
+ return 3;
+ case rule_node:
+ lua_pushinteger(L, rule_width(n));
+ lua_pushinteger(L, rule_height(n));
+ lua_pushinteger(L, rule_depth(n));
+ return 3;
+ case glyph_node:
+ /* or glyph_dimensions: */
+ lua_pushinteger(L, tex_glyph_width(n));
+ lua_pushinteger(L, tex_glyph_height(n));
+ lua_pushinteger(L, tex_glyph_depth(n));
+ if (lua_toboolean(L,2)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ return 4;
+ } else {
+ return 3;
+ }
+ case glue_node:
+ n = glue_leader_ptr(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setwhd(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ AGAIN:
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ {
+ int top = lua_gettop(L) ;
+ if (top > 1) {
+ if ((lua_type(L, 2) == LUA_TNUMBER)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 2);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 2) {
+ if ((lua_type(L, 3) == LUA_TNUMBER)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 3) {
+ if ((lua_type(L, 4) == LUA_TNUMBER)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 4);
+ } else {
+ /*Leave as is */
+ }
+ }
+ }
+ }
+ }
+ break;
+ case rule_node:
+ {
+ int top = lua_gettop(L) ;
+ if (top > 1) {
+ if ((lua_type(L, 2) == LUA_TNUMBER)) {
+ rule_width(n) = (halfword) lmt_roundnumber(L, 2);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 2) {
+ if ((lua_type(L, 3) == LUA_TNUMBER)) {
+ rule_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ /*Leave as is */
+ }
+ if (top > 3) {
+ if ((lua_type(L, 4) == LUA_TNUMBER)) {
+ rule_depth(n) = (halfword) lmt_roundnumber(L, 4);
+ } else {
+ /*Leave as is */
+ }
+ }
+ }
+ }
+ }
+ break;
+ case glue_node:
+ n = glue_leader_ptr(n);
+ if (n) {
+ goto AGAIN;
+ } else {
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_hasdimensions(lua_State *L)
+{
+ int b = 0;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ b = (box_width(n) > 0) || (box_total(n) > 0);
+ break;
+ case rule_node:
+ b = (rule_width(n) > 0) || (rule_total(n) > 0);
+ break;
+ case glyph_node:
+ b = tex_glyph_has_dimensions(n);
+ break;
+ case glue_node:
+ {
+ halfword l = glue_leader_ptr(n);
+ if (l) {
+ switch (node_type(l)) {
+ case hlist_node:
+ case vlist_node:
+ b = (box_width(l) > 0) || (box_total(l) > 0);
+ break;
+ case rule_node:
+ b = (rule_width(l) > 0) || (rule_total(l) > 0);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+ lua_pushboolean(L, b);
+ return 1;
+}
+
+/* node.direct.getglyphwhd */
+
+/*tex
+
+ When the height and depth of a box is calculated the |y-offset| is taken into account. In \LUATEX\
+ this is different for the height and depth, an historic artifact. However, because that can be
+ controlled we now have this helper, mostly for tracing purposes because it listens to the mode
+ parameter (and one can emulate other scenarios already).
+
+*/
+
+static int nodelib_direct_getglyphdimensions(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ scaledwhd whd = tex_glyph_dimensions_ex(n);
+ lua_pushinteger(L, whd.wd);
+ lua_pushinteger(L, whd.ht);
+ lua_pushinteger(L, whd.dp);
+ lua_pushinteger(L, glyph_expansion(n)); /* in case we need it later on */
+ nodelib_aux_pushxyscales(L, n);
+ return 6;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_getkerndimension(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == kern_node) {
+ lua_pushinteger(L, tex_kern_dimension_ex(n));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* node.direct.getlist */
+
+static int nodelib_direct_getlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ nodelib_push_direct_or_nil_node_prev(L, box_list(n));
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ nodelib_push_direct_or_nil_node_prev(L, kernel_math_list(n));
+ break;
+ case insert_node:
+ /* kind of fuzzy */
+ nodelib_push_direct_or_nil_node_prev(L, insert_list(n));
+ break;
+ case adjust_node:
+ nodelib_push_direct_or_nil_node_prev(L, adjust_list(n));
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ kernel_math_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case insert_node:
+ /* kind of fuzzy */
+ insert_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case adjust_node:
+ adjust_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getleader */
+/* node.direct.setleader */
+
+static int nodelib_direct_getleader(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glue_node) {
+ nodelib_push_direct_or_nil(L, glue_leader_ptr(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setleader(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glue_node) {
+ glue_leader_ptr(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+/* node.direct.getdata */
+/* node.direct.setdata */
+
+/*tex
+
+ These getter and setter get |data| as well as |value| fields. One can make them equivalent to
+ |getvalue| and |setvalue| if needed.
+
+*/
+
+static int nodelib_direct_getdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_data(n));
+ return 1;
+ case rule_node:
+ lua_pushinteger(L, rule_data(n));
+ return 1;
+ case glue_node:
+ lua_pushinteger(L, glue_data(n));
+ return 1;
+ case boundary_node:
+ lua_pushinteger(L, boundary_data(n));
+ return 1;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ nodelib_push_attribute_data(L, n);
+ break;
+ case attribute_value_subtype:
+ /*tex Only used for introspection so it's okay to return 2 values. */
+ lua_pushinteger(L, attribute_index(n));
+ lua_pushinteger(L, attribute_value(n));
+ return 2;
+ default:
+ /*tex We just ignore. */
+ break;
+ }
+ case mark_node:
+ if (lua_toboolean(L, 2)) {
+ lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0);
+ } else {
+ lmt_token_list_to_lua(L, mark_ptr(n));
+ }
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setdata(lua_State *L) /* data and value */
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case rule_node:
+ rule_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case glue_node:
+ glue_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case boundary_node:
+ boundary_data(n) = lmt_tohalfword(L, 2);
+ break;
+ case attribute_node:
+ /*tex Not supported for now! */
+ break;
+ case mark_node:
+ tex_delete_token_reference(mark_ptr(n));
+ mark_ptr(n) = lmt_token_list_from_lua(L, 2); /* check ref */
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.get[left|right|]delimiter */
+/* node.direct.set[left|right|]delimiter */
+
+static int nodelib_direct_getleftdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_left_delimiter(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getrightdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_right_delimiter(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_node(L, n, fence_delimiter_list(n));
+ return 1;
+ case radical_noad:
+ nodelib_push_direct_or_node(L, n, radical_left_delimiter(n));
+ return 1;
+ case accent_noad:
+ nodelib_push_direct_or_node(L, n, accent_middle_character(n)); /* not really a delimiter */
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setleftdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setrightdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdelimiter(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_middle_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case fence_noad:
+ fence_delimiter_list(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case radical_noad:
+ radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ case accent_noad:
+ accent_middle_character(n) = nodelib_valid_direct_from_index(L, 2); /* not really a delimiter */
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.get[top|bottom] */
+/* node.direct.set[top|bottom] */
+
+static int nodelib_direct_gettop(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ nodelib_push_direct_or_nil(L, accent_top_character(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_nil(L, fence_delimiter_top(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getbottom(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ nodelib_push_direct_or_nil(L, accent_bottom_character(n));
+ return 1;
+ case fence_noad:
+ nodelib_push_direct_or_nil(L, fence_delimiter_bottom(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_settop(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ accent_top_character(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ case fence_noad:
+ fence_delimiter_top(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setbottom(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case accent_noad:
+ accent_bottom_character(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ case fence_noad:
+ fence_delimiter_bottom(n) = nodelib_valid_direct_from_index(L, 2);
+ return 0;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.get[numerator|denominator] */
+/* node.direct.set[numerator|denominator] */
+
+static int nodelib_direct_getnumerator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_numerator(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getdenominator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ nodelib_push_direct_or_nil(L, fraction_denominator(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setnumerator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_numerator(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setdenominator(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case fraction_noad:
+ fraction_denominator(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getdegree */
+/* node.direct.setdegree */
+
+static int nodelib_direct_getdegree(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case radical_noad:
+ nodelib_push_direct_or_nil(L, radical_degree(n));
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setdegree(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case radical_noad:
+ radical_degree(n) = nodelib_valid_direct_from_index(L, 2);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getchoice */
+/* node.direct.setchoice */
+
+static int nodelib_direct_getchoice(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ halfword c = null;
+ if (n && node_type(n) == choice_node) {
+ switch (lmt_tointeger(L, 2)) {
+ case 1: c =
+ choice_display_mlist(n);
+ break;
+ case 2: c =
+ choice_text_mlist(n);
+ break;
+ case 3:
+ c = choice_script_mlist(n);
+ break;
+ case 4:
+ c = choice_script_script_mlist(n);
+ break;
+ }
+ }
+ nodelib_push_direct_or_nil(L, c);
+ return 1;
+}
+
+static int nodelib_direct_setchoice(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == choice_node) {
+ halfword c = nodelib_valid_direct_from_index(L, 2);
+ switch (lmt_tointeger(L, 2)) {
+ case 1:
+ choice_display_mlist(n) = c;
+ break;
+ case 2:
+ choice_text_mlist(n) = c;
+ break;
+ case 3:
+ choice_script_mlist(n) = c;
+ break;
+ case 4:
+ choice_script_script_mlist(n) = c;
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This is an experiment, we have a field left that we can use as attribute. */
+
+static int nodelib_direct_getglyphdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && (node_type(n) == glyph_node) && (glyph_data(n) != unused_attribute_value)) {
+ lua_pushinteger(L, glyph_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setglyphdata(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == glyph_node) {
+ glyph_data(n) = (halfword) luaL_optinteger(L, 2, unused_attribute_value);
+ }
+ return 0;
+}
+
+/* node.direct.getnext */
+/* node.direct.setnext */
+
+static int nodelib_direct_getnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_next(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isnext(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == lmt_tohalfword(L, 2)) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getprev */
+/* node.direct.setprev */
+
+static int nodelib_direct_getprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_prev(n) = nodelib_valid_direct_from_index(L, 2);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isprev(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == lmt_tohalfword(L, 2)) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getboth */
+/* node.direct.setboth */
+
+static int nodelib_direct_getboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ nodelib_push_direct_or_nil(L, node_next(n));
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+static int nodelib_direct_setboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ node_prev(n) = nodelib_valid_direct_from_index(L, 2);
+ node_next(n) = nodelib_valid_direct_from_index(L, 3);
+ }
+ return 0;
+}
+
+static int nodelib_direct_isboth(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword typ = lmt_tohalfword(L, 2);
+ halfword prv = node_prev(n);
+ halfword nxt = node_next(n);
+ nodelib_push_direct_or_nil(L, prv && node_type(prv) == typ ? prv : null);
+ nodelib_push_direct_or_nil(L, nxt && node_type(nxt) == typ ? nxt : null);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.direct.setlink */
+/* node.direct.setsplit */
+
+/*
+ a b b nil c d : prev-a-b-c-d-next
+ nil a b b nil c d nil : nil-a-b-c-d-nil
+*/
+
+static int nodelib_direct_setlink(lua_State *L)
+{
+ int n = lua_gettop(L);
+ halfword h = null; /* head node */
+ halfword t = null; /* tail node */
+ for (int i = 1; i <= n; i++) {
+ /*
+ We don't go for the tail of the current node because we can inject between existing nodes
+ and the nodes themselves can have old values for prev and next, so ... only single nodes
+ are looked at!
+ */
+ if (lua_type(L, i) == LUA_TNUMBER) {
+ halfword c = nodelib_valid_direct_from_index(L, i); /* current node */
+ if (c) {
+ if (c != t) {
+ if (t) {
+ node_next(t) = c;
+ node_prev(c) = t;
+ } else if (i > 1) {
+ /* we assume that the first node is a kind of head */
+ node_prev(c) = null;
+ }
+ t = c;
+ if (! h) {
+ h = t;
+ }
+ } else {
+ /* we ignore duplicate nodes which can be tails or the previous */
+ }
+ } else {
+ /* we ignore bad nodes, but we could issue a message */
+ }
+ } else if (t) {
+ /* safeguard: a nil in the list can be meant as end so we nil the next of tail */
+ node_next(t) = null;
+ } else {
+ /* we just ignore nil nodes and have no tail yet */
+ }
+ }
+ nodelib_push_direct_or_nil(L, h);
+ return 1;
+}
+
+static int nodelib_direct_setsplit(lua_State *L)
+{
+ halfword l = nodelib_valid_direct_from_index(L, 1);
+ halfword r = nodelib_valid_direct_from_index(L, 2); /* maybe default to next */
+ if (l && r) {
+ if (l != r) {
+ node_prev(node_next(l)) = null;
+ node_next(node_prev(r)) = null;
+ }
+ node_next(l) = null;
+ node_prev(r) = null;
+ }
+ return 0;
+}
+
+/*tex Local_par nodes can have frozen properties. */
+
+static int nodelib_direct_getparstate(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (! p) {
+ p = tex_find_par_par(cur_list.head);
+ } else if (node_type(p) != par_node) {
+ while (node_prev(p)) {
+ p = node_prev(p);
+ }
+ }
+ if (p && node_type(p) == par_node) {
+ int limited = lua_toboolean(L, 2);
+ lua_createtable(L, 0, 24);
+ if (p && node_type(p) == par_node) {
+ /* todo: optional: all skip components */
+ lua_push_integer_at_key(L, hsize, tex_get_par_par(p, par_hsize_code));
+ lua_push_integer_at_key(L, leftskip, glue_amount(tex_get_par_par(p, par_left_skip_code)));
+ lua_push_integer_at_key(L, rightskip, glue_amount(tex_get_par_par(p, par_right_skip_code)));
+ lua_push_integer_at_key(L, hangindent, tex_get_par_par(p, par_hang_indent_code));
+ lua_push_integer_at_key(L, hangafter, tex_get_par_par(p, par_hang_after_code));
+ lua_push_integer_at_key(L, parindent, tex_get_par_par(p, par_par_indent_code));
+ if (! limited) {
+ lua_push_integer_at_key(L, parfillleftskip, glue_amount(tex_get_par_par(p, par_par_fill_left_skip_code)));
+ lua_push_integer_at_key(L, parfillskip, glue_amount(tex_get_par_par(p, par_par_fill_right_skip_code)));
+ lua_push_integer_at_key(L, parinitleftskip, glue_amount(tex_get_par_par(p, par_par_init_left_skip_code)));
+ lua_push_integer_at_key(L, parinitrightskip, glue_amount(tex_get_par_par(p, par_par_init_right_skip_code)));
+ lua_push_integer_at_key(L, adjustspacing, tex_get_par_par(p, par_adjust_spacing_code));
+ lua_push_integer_at_key(L, protrudechars, tex_get_par_par(p, par_protrude_chars_code));
+ lua_push_integer_at_key(L, pretolerance, tex_get_par_par(p, par_pre_tolerance_code));
+ lua_push_integer_at_key(L, tolerance, tex_get_par_par(p, par_tolerance_code));
+ lua_push_integer_at_key(L, emergencystretch, tex_get_par_par(p, par_emergency_stretch_code));
+ lua_push_integer_at_key(L, looseness, tex_get_par_par(p, par_looseness_code));
+ lua_push_integer_at_key(L, lastlinefit, tex_get_par_par(p, par_last_line_fit_code));
+ lua_push_integer_at_key(L, linepenalty, tex_get_par_par(p, par_line_penalty_code));
+ lua_push_integer_at_key(L, interlinepenalty, tex_get_par_par(p, par_inter_line_penalty_code));
+ lua_push_integer_at_key(L, clubpenalty, tex_get_par_par(p, par_club_penalty_code));
+ lua_push_integer_at_key(L, widowpenalty, tex_get_par_par(p, par_widow_penalty_code));
+ lua_push_integer_at_key(L, displaywidowpenalty, tex_get_par_par(p, par_display_widow_penalty_code));
+ lua_push_integer_at_key(L, orphanpenalty, tex_get_par_par(p, par_orphan_penalty_code));
+ lua_push_integer_at_key(L, brokenpenalty, tex_get_par_par(p, par_broken_penalty_code));
+ lua_push_integer_at_key(L, adjdemerits, tex_get_par_par(p, par_adj_demerits_code));
+ lua_push_integer_at_key(L, doublehyphendemerits, tex_get_par_par(p, par_double_hyphen_demerits_code));
+ lua_push_integer_at_key(L, finalhyphendemerits, tex_get_par_par(p, par_final_hyphen_demerits_code));
+ lua_push_integer_at_key(L, baselineskip, glue_amount(tex_get_par_par(p, par_baseline_skip_code)));
+ lua_push_integer_at_key(L, lineskip, glue_amount(tex_get_par_par(p, par_line_skip_code)));
+ lua_push_integer_at_key(L, lineskiplimit, tex_get_par_par(p, par_line_skip_limit_code));
+ lua_push_integer_at_key(L, shapingpenaltiesmode, tex_get_par_par(p, par_shaping_penalties_mode_code));
+ lua_push_integer_at_key(L, shapingpenalty, tex_get_par_par(p, par_shaping_penalty_code));
+ }
+ lua_push_specification_at_key(L, parshape, tex_get_par_par(p, par_par_shape_code));
+ if (! limited) {
+ lua_push_specification_at_key(L, interlinepenalties, tex_get_par_par(p, par_inter_line_penalties_code));
+ lua_push_specification_at_key(L, clubpenalties, tex_get_par_par(p, par_club_penalties_code));
+ lua_push_specification_at_key(L, widowpenalties, tex_get_par_par(p, par_widow_penalties_code));
+ lua_push_specification_at_key(L, displaywidowpenalties, tex_get_par_par(p, par_display_widow_penalties_code));
+ lua_push_specification_at_key(L, orphanpenalties, tex_get_par_par(p, par_orphan_penalties_code));
+ }
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* node.type (converts id numbers to type names) */
+
+static int nodelib_hybrid_type(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ halfword i = lmt_tohalfword(L, 1);
+ if (tex_nodetype_is_visible(i)) {
+ lua_push_key_by_index(lmt_interface.node_data[i].lua);
+ return 1;
+ }
+ } else if (lmt_maybe_isnode(L, 1)) {
+ lua_push_key(node);
+ return 1;
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.new (allocate a new node) */
+
+static halfword nodelib_new_node(lua_State *L)
+{
+ quarterword i = unknown_node;
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ i = lmt_toquarterword(L, 1);
+ if (! tex_nodetype_is_visible(i)) {
+ i = unknown_node;
+ }
+ break;
+ case LUA_TSTRING:
+ i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data);
+ break;
+ }
+ if (tex_nodetype_is_visible(i)) {
+ quarterword j = unknown_subtype;
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ j = lmt_toquarterword(L, 2);
+ break;
+ case LUA_TSTRING:
+ j = nodelib_aux_get_node_subtype_id_from_name(L, 2, lmt_interface.node_data[i].subtypes);
+ break;
+ }
+ return tex_new_node(i, (j == unknown_subtype) ? 0 : j);
+ } else {
+ return luaL_error(L, "invalid node id for creating new node");
+ }
+}
+
+static int nodelib_userdata_new(lua_State *L)
+{
+ lmt_push_node_fast(L, nodelib_new_node(L));
+ return 1;
+}
+
+/* node.direct.new */
+
+static int nodelib_direct_new(lua_State *L)
+{
+ lua_pushinteger(L, nodelib_new_node(L));
+ return 1;
+}
+
+static int nodelib_direct_newtextglyph(lua_State* L)
+{
+ halfword glyph = tex_new_text_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
+ nodelib_aux_setattributelist(L, glyph, 3);
+ lua_pushinteger(L, glyph);
+ return 1;
+}
+
+static int nodelib_direct_newmathglyph(lua_State* L)
+{
+ /*tex For now we don't set a properties, group and/or index here. */
+ halfword glyph = tex_new_math_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
+ nodelib_aux_setattributelist(L, glyph, 3);
+ lua_pushinteger(L, glyph);
+ return 1;
+}
+
+/* node.free (this function returns the 'next' node, because that may be helpful) */
+
+static int nodelib_userdata_free(lua_State *L)
+{
+ if (lua_gettop(L) < 1) {
+ lua_pushnil(L);
+ } else if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ halfword p = node_next(n);
+ tex_flush_node(n);
+ lmt_push_node_fast(L, p);
+ }
+ return 1;
+}
+
+/* node.direct.free */
+
+static int nodelib_direct_free(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword p = node_next(n);
+ tex_flush_node(n);
+ n = p;
+ } else {
+ n = null;
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+/* node.flushnode (no next returned) */
+
+static int nodelib_userdata_flushnode(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ tex_flush_node(n);
+ }
+ return 0;
+}
+
+/* node.direct.flush_node */
+
+static int nodelib_direct_flushnode(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_flush_node(n);
+ }
+ return 0;
+}
+
+/* node.flushlist */
+
+static int nodelib_userdata_flushlist(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n_ptr = lmt_check_isnode(L, 1);
+ tex_flush_node_list(n_ptr);
+ }
+ return 0;
+}
+
+/* node.direct.flush_list */
+
+static int nodelib_direct_flushlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_flush_node_list(n);
+ }
+ return 0;
+}
+
+/* node.remove */
+
+static int nodelib_userdata_remove(lua_State *L)
+{
+ if (lua_gettop(L) < 2) {
+ return luaL_error(L, "Not enough arguments for node.remove()");
+ } else {
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ return 2;
+ } else {
+ halfword current = lmt_check_isnode(L, 2);
+ halfword removed = current;
+ int remove = lua_toboolean(L, 3);
+ if (head == current) {
+ if (node_prev(current)){
+ node_next(node_prev(current)) = node_next(current);
+ }
+ if (node_next(current)){
+ node_prev(node_next(current)) = node_prev(current);
+ }
+ head = node_next(current);
+ current = node_next(current);
+ } else {
+ halfword t = node_prev(current);
+ if (t) {
+ node_next(t) = node_next(current);
+ if (node_next(current)) {
+ node_prev(node_next(current)) = t;
+ }
+ current = node_next(current);
+ } else {
+ return luaL_error(L, "Bad arguments to node.remove()");
+ }
+ }
+ lmt_push_node_fast(L, head);
+ lmt_push_node_fast(L, current);
+ if (remove) {
+ tex_flush_node(removed);
+ return 2;
+ } else {
+ lmt_push_node_fast(L, removed);
+ node_next(removed) = null;
+ node_prev(removed) = null;
+ return 3;
+ }
+ }
+ }
+}
+
+/* node.direct.remove */
+
+static int nodelib_direct_remove(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ if (current) {
+ halfword removed = current;
+ int remove = lua_toboolean(L, 3);
+ halfword prev = node_prev(current);
+ if (head == current) {
+ halfword next = node_next(current);
+ if (prev){
+ node_next(prev) = next;
+ }
+ if (next){
+ node_prev(next) = prev;
+ }
+ head = node_next(current);
+ current = head;
+ } else {
+ if (prev) {
+ halfword next = node_next(current);
+ node_next(prev) = next;
+ if (next) {
+ node_prev(next) = prev;
+ }
+ current = next;
+ } else {
+ /* tex_formatted_warning("nodes","invalid arguments to node.remove"); */
+ return 2;
+ }
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, current);
+ if (remove) {
+ tex_flush_node(removed);
+ return 2;
+ } else {
+ nodelib_push_direct_or_nil(L, removed);
+ node_next(removed) = null;
+ node_prev(removed) = null;
+ return 3;
+ }
+ } else {
+ lua_pushinteger(L, head);
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.insertbefore (insert a node in a list) */
+
+static int nodelib_userdata_insertbefore(lua_State *L)
+{
+ if (lua_gettop(L) < 3) {
+ return luaL_error(L, "Not enough arguments for node.insertbefore()");
+ } else if (lua_isnil(L, 3)) {
+ lua_settop(L, 2);
+ } else {
+ halfword n = lmt_check_isnode(L, 3);
+ if (lua_isnil(L, 1)) {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lmt_push_node_fast(L, n);
+ lua_pushvalue(L, -1);
+ } else {
+ halfword current;
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ current = tex_tail_of_node_list(head);
+ } else {
+ current = lmt_check_isnode(L, 2);
+ }
+ if (head != current) {
+ halfword t = node_prev(current);
+ if (t) {
+ tex_couple_nodes(t, n);
+ } else {
+ return luaL_error(L, "Bad arguments to node.insertbefore()");
+ }
+ }
+ tex_couple_nodes(n, current);
+ lmt_push_node_fast(L, (head == current) ? n : head);
+ lmt_push_node_fast(L, n);
+ }
+ }
+ return 2;
+}
+
+/* node.direct.insertbefore */
+
+static int nodelib_direct_insertbefore(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 3);
+ if (n) {
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ /* no head, ignore current */
+ if (head) {
+ if (! current) {
+ current = tex_tail_of_node_list(head);
+ }
+ if (head != current) {
+ halfword prev = node_prev(current);
+ if (prev) {
+ tex_couple_nodes(prev, n);
+ } else {
+ /* error so just quit and return originals */
+ return 2;
+ }
+ }
+ tex_couple_nodes(n, current); /* nice but incompatible: tex_couple_nodes(tail_of_list(n),current) */
+ lua_pushinteger(L, (head == current) ? n : head);
+ lua_pushinteger(L, n);
+ } else {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ lua_pushinteger(L, n);
+ /* n, n */
+ }
+ } else {
+ lua_settop(L, 2);
+ }
+ return 2;
+}
+
+/* node.insertafter */
+
+static int nodelib_userdata_insertafter(lua_State *L)
+{
+ if (lua_gettop(L) < 3) {
+ return luaL_error(L, "Not enough arguments for node.insertafter()");
+ } else if (lua_isnil(L, 3)) {
+ lua_settop(L, 2);
+ } else {
+ halfword n = lmt_check_isnode(L, 3);
+ if (lua_isnil(L, 1)) {
+ node_next(n) = null;
+ node_prev(n) = null;
+ lmt_push_node_fast(L, n);
+ lua_pushvalue(L, -1);
+ } else {
+ halfword current;
+ halfword head = lmt_check_isnode(L, 1);
+ if (lua_isnil(L, 2)) {
+ current = head;
+ while (node_next(current)) {
+ current = node_next(current);
+ }
+ } else {
+ current = lmt_check_isnode(L, 2);
+ }
+ tex_try_couple_nodes(n, node_next(current));
+ tex_couple_nodes(current, n);
+ lua_pop(L, 2);
+ lmt_push_node_fast(L, n);
+ }
+ }
+ return 2;
+}
+
+/* node.direct.insertafter */
+
+static int nodelib_direct_insertafter(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword n = nodelib_valid_direct_from_index(L, 3);
+ if (n) {
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword current = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ if (! current) {
+ current = head;
+ while (node_next(current)) {
+ current = node_next(current);
+ }
+ }
+ tex_try_couple_nodes(n, node_next(current)); /* nice but incompatible: try_couple_nodes(tail_of_list(n), node_next(current)); */
+ tex_couple_nodes(current, n);
+ lua_pop(L, 2);
+ lua_pushinteger(L, n);
+ } else {
+ /* no head, ignore current */
+ node_next(n) = null;
+ node_prev(n) = null;
+ lua_pushinteger(L, n);
+ lua_pushvalue(L, -1);
+ /* n, n */
+ }
+ } else {
+ lua_settop(L, 2);
+ }
+ return 2;
+}
+
+/* */
+
+static int nodelib_direct_appendaftertail(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (h && n) {
+ tex_couple_nodes(tex_tail_of_node_list(h), n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_prependbeforehead(lua_State *L)
+{
+ /*[head][current][new]*/
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (h && n) {
+ tex_couple_nodes(n, tex_head_of_node_list(h));
+ }
+ return 0;
+}
+
+/* node.copylist */
+
+/*tex
+
+ We need to use an intermediate variable as otherwise target is used in the loop and subfields
+ get overwritten (or something like that) which results in crashes and unexpected side effects.
+
+*/
+
+static int nodelib_userdata_copylist(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ return 1; /* the nil itself */
+ } else {
+ halfword m;
+ halfword s = null;
+ halfword n = lmt_check_isnode(L, 1);
+ if ((lua_gettop(L) > 1) && (! lua_isnil(L, 2))) {
+ s = lmt_check_isnode(L, 2);
+ }
+ m = tex_copy_node_list(n, s);
+ lmt_push_node_fast(L, m);
+ return 1;
+ }
+}
+
+/* node.direct.copylist */
+
+static int nodelib_direct_copylist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ halfword s = nodelib_valid_direct_from_index(L, 2);
+ if (n) {
+ halfword m = tex_copy_node_list(n, s);
+ lua_pushinteger(L, m);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.show (node, threshold, max) */
+/* node.direct.show */
+
+static int nodelib_userdata_show(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
+ }
+ return 0;
+}
+
+static int nodelib_direct_show(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
+ }
+ return 0;
+}
+
+/* node.serialize(node, details, threshold, max) */
+/* node.direct.serialize */
+
+static int nodelib_aux_showlist(lua_State* L, halfword box)
+{
+ if (box) {
+ luaL_Buffer buffer;
+ int saved_selector = lmt_print_state.selector;
+ halfword levels = tracing_levels_par;
+ halfword online = tracing_online_par;
+ halfword details = show_node_details_par;
+ halfword depth = lmt_opthalfword(L, 3, show_box_depth_par);
+ halfword breadth = lmt_opthalfword(L, 4, show_box_breadth_par);
+ tracing_levels_par = 0;
+ tracing_online_par = 0;
+ show_node_details_par = lmt_opthalfword(L, 2, details);
+ lmt_print_state.selector = luabuffer_selector_code;
+ lmt_lua_state.used_buffer = &buffer;
+ luaL_buffinit(L, &buffer);
+ tex_show_node_list(box, depth, breadth);
+ tex_print_ln();
+ luaL_pushresult(&buffer);
+ lmt_lua_state.used_buffer = NULL;
+ lmt_print_state.selector = saved_selector;
+ show_node_details_par = details;
+ tracing_levels_par = levels;
+ tracing_online_par = online;
+ } else {
+ lua_pushliteral(L, "");
+ }
+ return 1;
+}
+
+static int nodelib_common_serialized(lua_State *L, halfword n)
+{
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ return nodelib_aux_showlist(L, n);
+ default:
+ {
+ halfword prv = null;
+ halfword nxt = null;
+ if (tex_nodetype_has_prev(n)) {
+ prv = node_prev(n);
+ node_prev(n) = null;
+ }
+ if (tex_nodetype_has_next(n)) {
+ nxt = node_next(n);
+ node_next(n) = null;
+ }
+ nodelib_aux_showlist(L, n);
+ if (prv) {
+ node_prev(n) = prv;
+ }
+ if (nxt) {
+ node_next(n) = nxt;
+ }
+ return 1;
+ }
+ }
+ }
+ lua_pushliteral(L, "");
+ return 1;
+}
+
+static int nodelib_userdata_serialized(lua_State *L)
+{
+ return nodelib_common_serialized(L, lmt_check_isnode(L, 1));
+}
+
+/* node.direct.show */
+
+static int nodelib_direct_serialized(lua_State *L)
+{
+ return nodelib_common_serialized(L, nodelib_valid_direct_from_index(L, 1));
+}
+
+
+/* node.copy (deep copy) */
+
+static int nodelib_userdata_copy(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ n = tex_copy_node(n);
+ lmt_push_node_fast(L, n);
+ }
+ return 1;
+}
+
+/* node.direct.copy (deep copy) */
+
+static int nodelib_direct_copy(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ /* beware, a glue node can have number 0 (zeropt) so we cannot test for null) */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_copy_node(n);
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ return 1;
+}
+
+/* node.direct.copyonly (use with care) */
+
+static int nodelib_direct_copyonly(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_copy_node_only(n);
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+ return 1;
+}
+
+/* node.write (output a node to tex's processor) */
+/* node.append (idem but no attributes) */
+
+static int nodelib_userdata_write(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = lmt_check_isnode(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ while (m) {
+ tex_tail_append(m);
+ if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
+ attach_current_attribute_list(m);
+ }
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+static int nodelib_userdata_append(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = lmt_check_isnode(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tail_append(n);
+ while (m) {
+ tex_tail_append(m);
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+*/
+
+/* node.direct.write (output a node to tex's processor) */
+/* node.direct.append (idem no attributes) */
+
+static int nodelib_direct_write(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = nodelib_valid_direct_from_index(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ while (m) {
+ tex_tail_append(m);
+ if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
+ attach_current_attribute_list(m);
+ }
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+static int nodelib_direct_appendtocurrentlist(lua_State *L)
+{
+ int j = lua_gettop(L);
+ for (int i = 1; i <= j; i++) {
+ halfword n = nodelib_valid_direct_from_index(L, i);
+ if (n) {
+ halfword m = node_next(n);
+ tex_tail_append(n);
+ while (m) {
+ tex_tail_append(m);
+ m = node_next(m);
+ }
+ }
+ }
+ return 0;
+}
+*/
+
+/* node.direct.last */
+
+static int nodelib_direct_lastnode(lua_State *L)
+{
+ halfword m = tex_pop_tail();
+ lua_pushinteger(L, m);
+ return 1;
+}
+
+/* node.direct.hpack */
+
+static int nodelib_aux_packing(lua_State *L, int slot)
+{
+ switch (lua_type(L, slot)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, slot);
+ if (lua_key_eq(s, exactly)) {
+ return packing_exactly;
+ } else if (lua_key_eq(s, additional)) {
+ return packing_additional;
+ } else if (lua_key_eq(s, expanded)) {
+ return packing_expanded;
+ } else if (lua_key_eq(s, substitute)) {
+ return packing_substitute;
+ } else if (lua_key_eq(s, adapted)) {
+ return packing_adapted;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ int m = (int) lua_tointeger(L, slot);
+ if (m >= packing_exactly && m <= packing_adapted) {
+ return m;
+ }
+ break;
+ }
+ }
+ return packing_additional;
+}
+
+static int nodelib_direct_hpack(lua_State *L)
+{
+ halfword p;
+ int w = 0;
+ int m = packing_additional;
+ singleword d = direction_def_value;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ if (top > 1) {
+ w = lmt_roundnumber(L, 2);
+ if (top > 2) {
+ m = nodelib_aux_packing(L, 3);
+ if (top > 3) {
+ d = nodelib_getdirection(L, 4);
+ }
+ }
+ }
+ } else {
+ n = null;
+ }
+ p = tex_hpack(n, w, m, d, holding_none_option);
+ lua_pushinteger(L, p);
+ lua_pushinteger(L, lmt_packaging_state.last_badness);
+ lua_pushinteger(L, lmt_packaging_state.last_overshoot);
+ return 3;
+}
+
+static int nodelib_direct_repack(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ int top = lua_gettop(L);
+ int w = top > 1 ? lmt_roundnumber(L, 2) : 0;
+ int m = top > 2 ? nodelib_aux_packing(L, 3) : packing_additional;
+ tex_repack(n, w, m);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_freeze(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ tex_freeze(n, lua_toboolean(L, 2));
+ break;
+ }
+ }
+ return 0;
+}
+
+
+/* node.direct.vpack */
+
+static int nodelib_direct_verticalbreak(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ scaled ht = lmt_roundnumber(L, 2);
+ scaled dp = lmt_roundnumber(L, 3);
+ n = tex_vert_break(n, ht, dp);
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+static int nodelib_direct_vpack(lua_State *L)
+{
+ halfword p;
+ int w = 0;
+ int m = packing_additional;
+ singleword d = direction_def_value;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ if (top > 1) {
+ w = lmt_roundnumber(L, 2);
+ if (top > 2) {
+ switch (lua_type(L, 3)) {
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 3);
+ if (lua_key_eq(s, additional)) {
+ m = packing_additional;
+ } else if (lua_key_eq(s, exactly)) {
+ m = packing_exactly;
+ }
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ m = (int) lua_tointeger(L, 3);
+ if (m != packing_exactly && m != packing_additional) {
+ m = packing_additional;
+ }
+ break;
+ }
+ }
+ if (top > 3) {
+ d = nodelib_getdirection(L, 4);
+ }
+ }
+ }
+ } else {
+ n = null;
+ }
+ p = tex_vpack(n, w, m, max_dimen, d, holding_none_option);
+ lua_pushinteger(L, p);
+ lua_pushinteger(L, lmt_packaging_state.last_badness);
+ return 2;
+}
+
+/* node.direct.dimensions */
+/* node.direct.rangedimensions */
+/* node.direct.naturalwidth */
+
+static int nodelib_direct_dimensions(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top > 0) {
+ scaledwhd siz = { 0, 0, 0 };
+ glueratio g_mult = normal_glue_multiplier;
+ int vertical = 0;
+ int g_sign = normal_glue_sign;
+ int g_order = normal_glue_order;
+ int i = 1;
+ halfword n = null;
+ halfword p = null;
+ if (top > 3) {
+ i += 3;
+ g_mult = (glueratio) lua_tonumber(L, 1); /* integer or float */
+ g_sign = tex_checked_glue_sign(lmt_tohalfword(L, 2));
+ g_order = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ }
+ n = nodelib_valid_direct_from_index(L, i);
+ if (lua_type(L, i + 1) == LUA_TBOOLEAN) {
+ vertical = lua_toboolean(L, i + 1);
+ } else {
+ p = nodelib_valid_direct_from_index(L, i + 1);
+ vertical = lua_toboolean(L, i + 2);
+ }
+ if (n) {
+ if (vertical) {
+ siz = tex_natural_vsizes(n, p, g_mult, g_sign, g_order);
+ } else {
+ siz = tex_natural_hsizes(n, p, g_mult, g_sign, g_order);
+ }
+ }
+ lua_pushinteger(L, siz.wd);
+ lua_pushinteger(L, siz.ht);
+ lua_pushinteger(L, siz.dp);
+ return 3;
+ } else {
+ return luaL_error(L, "missing argument to 'dimensions' (direct node expected)");
+ }
+}
+
+static int nodelib_direct_rangedimensions(lua_State *L) /* parent, first, last */
+{
+ int top = lua_gettop(L);
+ if (top > 1) {
+ scaledwhd siz = { 0, 0, 0 };
+ int vertical = 0;
+ halfword l = nodelib_valid_direct_from_index(L, 1); /* parent */
+ halfword n = nodelib_valid_direct_from_index(L, 2); /* first */
+ halfword p = n;
+ if (lua_type(L, 3) == LUA_TBOOLEAN) {
+ vertical = lua_toboolean(L, 3);
+ } else {
+ p = nodelib_valid_direct_from_index(L, 3); /* last */
+ vertical = lua_toboolean(L, 4);
+ }
+ if (l && n) {
+ if (vertical) {
+ siz = tex_natural_vsizes(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ } else {
+ siz = tex_natural_hsizes(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ }
+ }
+ lua_pushinteger(L, siz.wd);
+ lua_pushinteger(L, siz.ht);
+ lua_pushinteger(L, siz.dp);
+ return 3;
+ } else {
+ return luaL_error(L, "missing argument to 'rangedimensions' (2 or more direct nodes expected)");
+ }
+}
+
+static int nodelib_direct_naturalwidth(lua_State *L) /* parent, first, [last] */
+{
+ int top = lua_gettop(L);
+ if (top > 1) {
+ scaled wd = 0;
+ halfword l = nodelib_valid_direct_from_index(L, 1); /* parent */
+ halfword n = nodelib_valid_direct_from_index(L, 2); /* first */
+ halfword p = nodelib_valid_direct_from_index(L, 3); /* last */
+ if (l && n) {
+ wd = tex_natural_width(n, p, (glueratio) box_glue_set(l), box_glue_sign(l), box_glue_order(l));
+ }
+ lua_pushinteger(L, wd);
+ return 1;
+ } else {
+ return luaL_error(L, "missing argument to 'naturalwidth' (2 or more direct nodes expected)");
+ }
+}
+
+static int nodelib_direct_naturalhsize(lua_State *L)
+{
+ scaled wd = 0;
+ halfword c = null;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ wd = tex_natural_hsize(n, &c);
+ }
+ lua_pushinteger(L, wd);
+ lua_pushinteger(L, c ? glue_amount(c) : 0);
+ nodelib_push_direct_or_nil(L, c);
+ return 3;
+}
+
+static int nodelib_direct_mlisttohlist(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int style = lmt_get_math_style(L, 2, text_style);
+ int penalties = lua_toboolean(L, 3);
+ int beginclass = lmt_optinteger(L, 4, unset_noad_class);
+ int endclass = lmt_optinteger(L, 5, unset_noad_class);
+ if (! valid_math_class_code(beginclass)) {
+ beginclass = unset_noad_class;
+ }
+ if (! valid_math_class_code(endclass)) {
+ endclass = unset_noad_class;
+ }
+ n = tex_mlist_to_hlist(n, penalties, style, beginclass, endclass, NULL);
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+/*tex
+
+ This function is similar to |get_node_type_id|, for field identifiers. It has to do some more
+ work, because not all identifiers are valid for all types of nodes. We can make this faster if
+ needed but when this needs to be called often something is wrong with the code.
+
+*/
+
+static int nodelib_aux_get_node_field_id(lua_State *L, int n, int node)
+{
+ int t = node_type(node);
+ const char *s = lua_tostring(L, n);
+ if (! s) {
+ return -2;
+ } else if (lua_key_eq(s, next)) {
+ return 0;
+ } else if (lua_key_eq(s, id)) {
+ return 1;
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ return 2;
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ return 3;
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ return -1;
+ }
+ } else {
+ value_info *fields = lmt_interface.node_data[t].fields;
+ if (fields) {
+ if (lua_key_eq(s, list)) {
+ const char *sh = lua_key(head);
+ for (int j = 0; fields[j].lua; j++) {
+ if (fields[j].name == s || fields[j].name == sh) {
+ return j + 3;
+ }
+ }
+ } else {
+ for (int j = 0; fields[j].lua; j++) {
+ if (fields[j].name == s) {
+ return j + 3;
+ }
+ }
+ }
+ }
+ }
+ return -2;
+}
+
+/* node.hasfield */
+
+static int nodelib_userdata_hasfield(lua_State *L)
+{
+ int i = -2;
+ if (! lua_isnil(L, 1)) {
+ i = nodelib_aux_get_node_field_id(L, 2, lmt_check_isnode(L, 1));
+ }
+ lua_pushboolean(L, (i != -2));
+ return 1;
+}
+
+/* node.direct.hasfield */
+
+static int nodelib_direct_hasfield(lua_State *L)
+{
+ int i = -2;
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ i = nodelib_aux_get_node_field_id(L, 2, n);
+ }
+ lua_pushboolean(L, (i != -2));
+ return 1;
+}
+
+/* node.types */
+
+static int nodelib_shared_types(lua_State *L)
+{
+ lua_newtable(L);
+ for (int i = 0; lmt_interface.node_data[i].id != -1; i++) {
+ if (lmt_interface.node_data[i].visible) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_rawseti(L, -2, lmt_interface.node_data[i].id);
+ }
+ }
+ return 1;
+}
+
+/* node.fields (fetch the list of valid fields) */
+
+static int nodelib_shared_fields(lua_State *L)
+{
+ int offset = 2;
+ int t = nodelib_aux_get_valid_node_type_id(L, 1);
+ int f = lua_toboolean(L, 2);
+ value_info *fields = lmt_interface.node_data[t].fields;
+ lua_newtable(L);
+ if (f) {
+ lua_push_key(next);
+ lua_push_key(node);
+ lua_rawset(L, -3);
+ lua_push_key(id)
+ lua_push_key(integer);
+ lua_rawset(L, -3);
+ if (tex_nodetype_has_subtype(t)) {
+ lua_push_key(subtype);
+ lua_push_key(integer);
+ lua_rawset(L, -3);
+ offset++;
+ }
+ if (tex_nodetype_has_prev(t)) {
+ lua_push_key(prev);
+ lua_push_key(node);
+ lua_rawset(L, -3);
+ }
+ if (fields) {
+ for (lua_Integer i = 0; fields[i].lua != 0; i++) {
+ /* todo: use other macros */
+ lua_push_key_by_index(fields[i].lua);
+ lua_push_key_by_index(lmt_interface.field_type_values[fields[i].type].lua);
+ // lua_pushinteger(L, fields[i].type);
+ lua_rawset(L, -3);
+ }
+ }
+ } else {
+ lua_push_key(next);
+ lua_rawseti(L, -2, 0);
+ lua_push_key(id);
+ lua_rawseti(L, -2, 1);
+ if (tex_nodetype_has_subtype(t)) {
+ lua_push_key(subtype);
+ lua_rawseti(L, -2, 2);
+ offset++;
+ }
+ if (tex_nodetype_has_prev(t)) {
+ lua_push_key(prev);
+ lua_rawseti(L, -2, -1);
+ }
+ if (fields) {
+ for (lua_Integer i = 0; fields[i].lua != 0; i++) {
+ // lua_push_key_by_index(L, fields[i].lua);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, fields[i].lua);
+ lua_rawseti(L, -2, i + offset);
+ }
+ }
+ }
+ return 1;
+}
+
+/* These should move to texlib ... which might happen. */
+
+static int nodelib_shared_values(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ /*
+ delimiter options (bit set)
+ delimiter modes (bit set)
+ */
+ const char *s = lua_tostring(L, 1);
+ if (lua_key_eq(s, glue) || lua_key_eq(s, fill)) {
+ return lmt_push_info_values(L, lmt_interface.node_fill_values);
+ } else if (lua_key_eq(s, dir)) {
+ return lmt_push_info_values(L, lmt_interface.direction_values);
+ } else if (lua_key_eq(s, math)) {
+ /*tex A bit strange place, so moved to lmttexlib. */
+ return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
+ } else if (lua_key_eq(s, style)) {
+ /*tex A bit strange place, so moved to lmttexlib. */
+ return lmt_push_info_values(L, lmt_interface.math_style_values);
+ } else if (lua_key_eq(s, page)) {
+ /*tex These are never used, whatsit related. */
+ return lmt_push_info_values(L, lmt_interface.page_contribute_values);
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_shared_subtypes(lua_State *L)
+{
+ value_info *subtypes = NULL;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ {
+ /* official accessors */
+ const char *s = lua_tostring(L,1);
+ if (lua_key_eq(s, glyph)) subtypes = lmt_interface.node_data[glyph_node] .subtypes;
+ else if (lua_key_eq(s, glue)) subtypes = lmt_interface.node_data[glue_node] .subtypes;
+ else if (lua_key_eq(s, dir)) subtypes = lmt_interface.node_data[dir_node] .subtypes;
+ else if (lua_key_eq(s, mark)) subtypes = lmt_interface.node_data[mark_node] .subtypes;
+ else if (lua_key_eq(s, boundary)) subtypes = lmt_interface.node_data[boundary_node] .subtypes;
+ else if (lua_key_eq(s, penalty)) subtypes = lmt_interface.node_data[penalty_node] .subtypes;
+ else if (lua_key_eq(s, kern)) subtypes = lmt_interface.node_data[kern_node] .subtypes;
+ else if (lua_key_eq(s, rule)) subtypes = lmt_interface.node_data[rule_node] .subtypes;
+ else if (lua_key_eq(s, list)
+ || lua_key_eq(s, hlist)
+ || lua_key_eq(s, vlist)) subtypes = lmt_interface.node_data[hlist_node] .subtypes; /* too many but ok as reserved */
+ else if (lua_key_eq(s, adjust)) subtypes = lmt_interface.node_data[adjust_node] .subtypes;
+ else if (lua_key_eq(s, disc)) subtypes = lmt_interface.node_data[disc_node] .subtypes;
+ else if (lua_key_eq(s, math)) subtypes = lmt_interface.node_data[math_node] .subtypes;
+ else if (lua_key_eq(s, noad)) subtypes = lmt_interface.node_data[simple_noad] .subtypes;
+ else if (lua_key_eq(s, radical)) subtypes = lmt_interface.node_data[radical_noad] .subtypes;
+ else if (lua_key_eq(s, accent)) subtypes = lmt_interface.node_data[accent_noad] .subtypes;
+ else if (lua_key_eq(s, fence)) subtypes = lmt_interface.node_data[fence_noad] .subtypes;
+ else if (lua_key_eq(s, choice)) subtypes = lmt_interface.node_data[choice_node] .subtypes;
+ else if (lua_key_eq(s, par)) subtypes = lmt_interface.node_data[par_node] .subtypes;
+ else if (lua_key_eq(s, attribute)) subtypes = lmt_interface.node_data[attribute_node].subtypes;
+ }
+ break;
+ case LUA_TNUMBER:
+ switch (lua_tointeger(L, 1)) {
+ case glyph_node: subtypes = lmt_interface.node_data[glyph_node] .subtypes; break;
+ case glue_node: subtypes = lmt_interface.node_data[glue_node] .subtypes; break;
+ case dir_node: subtypes = lmt_interface.node_data[dir_node] .subtypes; break;
+ case boundary_node: subtypes = lmt_interface.node_data[boundary_node] .subtypes; break;
+ case penalty_node: subtypes = lmt_interface.node_data[penalty_node] .subtypes; break;
+ case kern_node: subtypes = lmt_interface.node_data[kern_node] .subtypes; break;
+ case rule_node: subtypes = lmt_interface.node_data[rule_node] .subtypes; break;
+ case hlist_node: subtypes = lmt_interface.node_data[hlist_node] .subtypes; break;
+ case vlist_node: subtypes = lmt_interface.node_data[vlist_node] .subtypes; break;
+ case adjust_node: subtypes = lmt_interface.node_data[adjust_node] .subtypes; break;
+ case disc_node: subtypes = lmt_interface.node_data[disc_node] .subtypes; break;
+ case math_node: subtypes = lmt_interface.node_data[math_node] .subtypes; break;
+ case simple_noad: subtypes = lmt_interface.node_data[simple_noad] .subtypes; break;
+ case radical_noad: subtypes = lmt_interface.node_data[radical_noad] .subtypes; break;
+ case accent_noad: subtypes = lmt_interface.node_data[accent_noad] .subtypes; break;
+ case fence_noad: subtypes = lmt_interface.node_data[fence_noad] .subtypes; break;
+ case choice_node: subtypes = lmt_interface.node_data[choice_node] .subtypes; break;
+ case par_node: subtypes = lmt_interface.node_data[par_node] .subtypes; break;
+ case attribute_node: subtypes = lmt_interface.node_data[attribute_node].subtypes; break;
+ }
+ break;
+ }
+ if (subtypes) {
+ lua_newtable(L);
+ for (int i = 0; subtypes[i].name; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, subtypes[i].lua);
+ lua_rawseti(L, -2, subtypes[i].id);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.slide */
+
+static int nodelib_direct_slide(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ node_prev(node_next(n)) = n;
+ n = node_next(n);
+ }
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.tail (find the end of a list) */
+
+static int nodelib_userdata_tail(lua_State *L)
+{
+ if (! lua_isnil(L, 1)) {
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ n = node_next(n);
+ }
+ lmt_push_node_fast(L, n);
+ } else {
+ /*tex We keep the old userdata. */
+ }
+ }
+ return 1;
+}
+
+/* node.direct.tail */
+
+static int nodelib_direct_tail(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (node_next(n)) {
+ n = node_next(n);
+ }
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.endofmath */
+
+static int nodelib_direct_endofmath(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) == math_node && node_subtype(n) == end_inline_math) {
+ lua_pushinteger(L, n);
+ return 1;
+ } else {
+ int level = 1;
+ while (node_next(n)) {
+ n = node_next(n);
+ if (n && node_type(n) == math_node) {
+ switch (node_subtype(n)) {
+ case begin_inline_math:
+ ++level;
+ break;
+ case end_inline_math:
+ --level;
+ if (level > 0) {
+ break;
+ } else {
+ lua_pushinteger(L, n);
+ return 1;
+ }
+
+ }
+ }
+ }
+ // if (level > 0) {
+ // /* something is wrong */
+ // }
+ }
+ }
+ return 0;
+}
+
+/* node.hasattribute (gets attribute) */
+
+static int nodelib_userdata_hasattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ int key = lmt_tointeger(L, 2);
+ int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
+ if (val > unused_attribute_value) {
+ lua_pushinteger(L, val);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.has_attribute */
+
+static int nodelib_direct_hasattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int key = nodelib_valid_direct_from_index(L, 2);
+ int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
+ if (val > unused_attribute_value) {
+ lua_pushinteger(L, val);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.get_attribute */
+
+static int nodelib_userdata_getattribute(lua_State *L)
+{
+ halfword p = lmt_check_isnode(L, 1);
+ if (tex_nodetype_has_attributes(node_type(p))) {
+ p = node_attr(p);
+ if (p) {
+ p = node_next(p);
+ if (p) {
+ int i = lmt_optinteger(L, 2, 0);
+ while (p) {
+ if (attribute_index(p) == i) {
+ int v = attribute_value(p);
+ if (v == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, v);
+ return 1;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_findattributerange(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword i = lmt_tohalfword(L, 2);
+ while (h) {
+ if (tex_nodetype_has_attributes(node_type(h))) {
+ halfword p = node_attr(h);
+ if (p) {
+ p = node_next(p);
+ while (p) {
+ if (attribute_index(p) == i) {
+ if (attribute_value(p) == unused_attribute_value) {
+ break;
+ } else {
+ halfword t = h;
+ while (node_next(t)) {
+ t = node_next(t);
+ }
+ while (t != h) {
+ if (tex_nodetype_has_attributes(node_type(t))) {
+ halfword a = node_attr(t);
+ if (a) {
+ a = node_next(a);
+ while (a) {
+ if (attribute_index(a) == i) {
+ if (attribute_value(a) == unused_attribute_value) {
+ break;
+ } else {
+ goto FOUND;
+ }
+ } else if (attribute_index(a) > i) {
+ break;
+ }
+ a = node_next(a);
+ }
+ }
+ }
+ t = node_prev(t);
+ }
+ FOUND:
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, t);
+ return 2;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ h = node_next(h);
+ }
+ }
+ return 0;
+}
+
+/* node.direct.getattribute */
+/* node.direct.setattribute */
+/* node.direct.unsetattribute */
+/* node.direct.findattribute */
+
+static int nodelib_direct_getattribute(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) {
+ if (node_type(p) != attribute_node) {
+ p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
+ }
+ if (p) {
+ if (node_subtype(p) == attribute_list_subtype) {
+ p = node_next(p);
+ }
+ if (p) {
+ halfword index = lmt_opthalfword(L, 2, 0);
+ while (p) {
+ halfword i = attribute_index(p);
+ if (i == index) {
+ int v = attribute_value(p);
+ if (v == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, v);
+ return 1;
+ }
+ } else if (i > index) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_getattributes(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) {
+ if (node_type(p) != attribute_node) {
+ p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
+ }
+ if (p) {
+ if (node_subtype(p) == attribute_list_subtype) {
+ p = node_next(p);
+ }
+ if (p) {
+ int top = lua_gettop(L);
+ for (int i = 2; i <= top; i++) {
+ halfword a = lmt_tohalfword(L, i);
+ halfword n = p;
+ halfword v = unused_attribute_value;
+ while (n) {
+ halfword id = attribute_index(n);
+ if (id == a) {
+ v = attribute_value(n);
+ break;
+ } else if (id > a) {
+ break;
+ } else {
+ n = node_next(n);
+ }
+ }
+ if (v == unused_attribute_value) {
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, v);
+ }
+ }
+ return top - 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n))) { // already checked
+ halfword index = lmt_tohalfword(L, 2);
+ halfword value = lmt_optinteger(L, 3, unused_attribute_value);
+ // if (value == unused_attribute_value) {
+ // tex_unset_attribute(n, index, value);
+ // } else {
+ tex_set_attribute(n, index, value);
+ // }
+ }
+ return 0;
+}
+
+/* set_attributes(n,[initial,]key1,val1,key2,val2,...) */
+
+static int nodelib_direct_setattributes(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && tex_nodetype_has_attributes(node_type(n))) {
+ int top = lua_gettop(L);
+ int ini = 2;
+ if (lua_type(L, 2) == LUA_TBOOLEAN) {
+ ++ini;
+ if (lua_toboolean(L, 2) && ! node_attr(n)) {
+ attach_current_attribute_list(n);
+ }
+ }
+ for (int i = ini; i <= top; i += 2) {
+ halfword key = lmt_tohalfword(L, i);
+ halfword val = lmt_optinteger(L, i + 1, unused_attribute_value);
+ // if (val == unused_attribute_value) {
+ // tex_unset_attribute(p, key, val);
+ // } else {
+ tex_set_attribute(n, key, val);
+ // }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_patchattributes(lua_State *L)
+{
+ halfword p = nodelib_valid_direct_from_index(L, 1);
+ if (p) { /* todo: check if attributes */
+ halfword att = null;
+ int top = lua_gettop(L);
+ for (int i = 2; i <= top; i += 2) {
+ halfword index = lmt_tohalfword(L, i);
+ halfword value = lua_type(L, i + 1) == LUA_TNUMBER ? lmt_tohalfword(L, i + 1) : unused_attribute_value;
+ if (att) {
+ att = tex_patch_attribute_list(att, index, value);
+ } else {
+ att = tex_copy_attribute_list_set(node_attr(p), index, value);
+ }
+ }
+ tex_attach_attribute_list_attribute(p, att);
+ }
+ return 0;
+}
+
+static int nodelib_direct_findattribute(lua_State *L) /* returns attr value and node */
+{
+ halfword c = nodelib_valid_direct_from_index(L, 1);
+ if (c) {
+ halfword i = lmt_tohalfword(L, 2);
+ while (c) {
+ if (tex_nodetype_has_attributes(node_type(c))) {
+ halfword p = node_attr(c);
+ if (p) {
+ p = node_next(p);
+ while (p) {
+ if (attribute_index(p) == i) {
+ halfword ret = attribute_value(p);
+ if (ret == unused_attribute_value) {
+ break;
+ } else {
+ lua_pushinteger(L, ret);
+ lua_pushinteger(L, c);
+ return 2;
+ }
+ } else if (attribute_index(p) > i) {
+ break;
+ }
+ p = node_next(p);
+ }
+ }
+ }
+ c = node_next(c);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_unsetattribute(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword key = lmt_checkhalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ halfword ret = tex_unset_attribute(n, key, val);
+ if (ret > unused_attribute_value) { /* != */
+ lua_pushinteger(L, ret);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+static int nodelib_direct_unsetattributes(lua_State *L)
+{
+ halfword key = lmt_checkhalfword(L, 1);
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ halfword last = nodelib_valid_direct_from_index(L, 3);
+ if (first) {
+ tex_unset_attributes(first, last, key);
+ }
+ return 0;
+}
+
+/* node.set_attribute */
+/* node.unset_attribute */
+
+static int nodelib_userdata_setattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ halfword key = lmt_tohalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ tex_unset_attribute(n, key, val);
+ } else {
+ tex_set_attribute(n, key, val);
+ }
+ }
+ return 0;
+}
+
+static int nodelib_userdata_unsetattribute(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ halfword key = lmt_checkhalfword(L, 2);
+ halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
+ halfword ret = tex_unset_attribute(n, key, val);
+ if (ret > unused_attribute_value) {
+ lua_pushinteger(L, ret);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.getglue */
+/* node.direct.setglue */
+/* node.direct.iszeroglue */
+
+static int nodelib_direct_getglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ lua_pushinteger(L, glue_amount(n));
+ lua_pushinteger(L, glue_stretch(n));
+ lua_pushinteger(L, glue_shrink(n));
+ lua_pushinteger(L, glue_stretch_order(n));
+ lua_pushinteger(L, glue_shrink_order(n));
+ return 5;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushnumber(L, (double) box_glue_set(n)); /* float */
+ lua_pushinteger(L, box_glue_order(n));
+ lua_pushinteger(L, box_glue_sign(n));
+ return 3;
+ case math_node:
+ lua_pushinteger(L, math_amount(n));
+ lua_pushinteger(L, math_stretch(n));
+ lua_pushinteger(L, math_shrink(n));
+ lua_pushinteger(L, math_stretch_order(n));
+ lua_pushinteger(L, math_shrink_order(n));
+ return 5;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int top = lua_gettop(L);
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ glue_amount(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
+ glue_stretch(n) = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
+ glue_shrink(n) = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
+ glue_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
+ glue_shrink_order(n) = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_glue_set(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (glueratio) lua_tonumber(L, 2) : 0;
+ box_glue_order(n) = tex_checked_glue_sign((top > 2 && lua_type(L, 3) == LUA_TNUMBER) ? (halfword) lua_tointeger(L, 3) : 0);
+ box_glue_sign(n) = tex_checked_glue_order((top > 3 && lua_type(L, 4) == LUA_TNUMBER) ? (halfword) lua_tointeger(L, 4) : 0);
+ break;
+ case math_node:
+ math_amount(n) = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
+ math_stretch(n) = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
+ math_shrink(n) = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
+ math_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
+ math_shrink_order(n) = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_iszeroglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ case glue_spec_node:
+ lua_pushboolean(L, glue_amount(n) == 0 && glue_stretch(n) == 0 && glue_shrink(n) == 0);
+ return 1;
+ case hlist_node:
+ case vlist_node:
+ lua_pushboolean(L, box_glue_set(n) == 0.0 && box_glue_order(n) == 0 && box_glue_sign(n) == 0);
+ return 1;
+ case math_node:
+ lua_pushboolean(L, math_amount(n) == 0 && math_stretch(n) == 0 && math_shrink(n) == 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* direct.startofpar */
+
+static int nodelib_direct_startofpar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ lua_pushboolean(L, n && tex_is_start_of_par_node(n));
+ return 1;
+}
+
+/* iteration */
+
+static int nodelib_aux_nil(lua_State *L)
+{
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.traverse */
+/* node.direct.traverse_id */
+/* node.direct.traverse_char */
+/* node.direct.traverse_glyph */
+/* node.direct.traverse_list */
+/* node.direct.traverse_leader */
+
+static int nodelib_direct_aux_next(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverse(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseid(lua_State *L)
+{
+ if (lua_isnil(L, 2)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 2);
+ if (n) {
+ if (lua_toboolean(L, 3)) {
+ if (lua_toboolean(L, 4)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_direct_aux_prev_filtered, 1);
+ } else {
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_direct_aux_next_filtered, 1);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ return 0;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_char(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ lua_pushinteger(L, glyph_data(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_char(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ lua_pushinteger(L, glyph_data(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traversechar(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_char, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_char, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_glyph(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != glyph_node) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_glyph(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != glyph_node) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, glyph_character(t));
+ lua_pushinteger(L, glyph_font(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseglyph(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_glyph, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_glyph, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+static int nodelib_direct_aux_next_list(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_list(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverselist(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_list, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_list, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+/*tex This is an experiment. */
+
+static int nodelib_direct_aux_next_leader(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
+ t = node_next(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_aux_prev_leader(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
+ t = node_prev(t);
+ }
+ if (t) {
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ nodelib_push_direct_or_nil(L, box_list(t));
+ return 4;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_direct_traverseleader(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_leader, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_leader, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+
+/*tex This is an experiment. */
+
+static int nodelib_direct_aux_next_content(lua_State *L)
+{
+ halfword t;
+ halfword l = null;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ while (t) {
+ switch (node_type(t)) {
+ case glyph_node:
+ case disc_node:
+ case rule_node:
+ goto FOUND;
+ case glue_node:
+ l = glue_leader_ptr(t);
+ if (l) {
+ goto FOUND;
+ } else {
+ break;
+ }
+ case hlist_node:
+ case vlist_node:
+ l = box_list(t);
+ goto FOUND;
+ }
+ t = node_next(t);
+ }
+ lua_pushnil(L);
+ return 1;
+ FOUND:
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ if (l) {
+ nodelib_push_direct_or_nil(L, l);
+ return 4;
+ } else {
+ return 3;
+ }
+}
+
+static int nodelib_direct_aux_prev_content(lua_State *L)
+{
+ halfword t;
+ halfword l = null;
+ if (lua_isnil(L, 2)) {
+ t = lmt_tohalfword(L, 1) ;
+ lua_settop(L, 1);
+ } else {
+ t = lmt_tohalfword(L, 2) ;
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ while (t) {
+ switch (node_type(t)) {
+ case glyph_node:
+ case disc_node:
+ case rule_node:
+ goto FOUND;
+ case glue_node:
+ l = glue_leader_ptr(t);
+ if (l) {
+ goto FOUND;
+ } else {
+ break;
+ }
+ case hlist_node:
+ case vlist_node:
+ l = box_list(t);
+ goto FOUND;
+ }
+ t = node_prev(t);
+ }
+ lua_pushnil(L);
+ return 1;
+ FOUND:
+ lua_pushinteger(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ if (l) {
+ nodelib_push_direct_or_nil(L, l);
+ return 4;
+ } else {
+ return 3;
+ }
+}
+
+static int nodelib_direct_traversecontent(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_direct_aux_prev_content, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_direct_aux_next_content, 0);
+ }
+ lua_pushinteger(L, n);
+ lua_pushnil(L);
+ return 3;
+ } else {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ }
+ }
+}
+
+/* node.traverse */
+/* node.traverse_id */
+/* node.traverse_char */
+/* node.traverse_glyph */
+/* node.traverse_list */
+
+static int nodelib_aux_next(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L, 1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_next(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_aux_prev(lua_State *L)
+{
+ halfword t;
+ if (lua_isnil(L, 2)) {
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L, 1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_prev(t);
+ lua_settop(L, 2);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_type(t));
+ lua_pushinteger(L, node_subtype(t));
+ return 3;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_traverse(lua_State *L)
+{
+ if (lua_isnil(L, 1)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = lmt_check_isnode(L, 1);
+ if (lua_toboolean(L, 2)) {
+ if (lua_toboolean(L, 3)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_pushcclosure(L, nodelib_aux_prev, 0);
+ } else {
+ lua_pushcclosure(L, nodelib_aux_next, 0);
+ }
+ lmt_push_node_fast(L, n);
+ lua_pushnil(L);
+ return 3;
+ }
+}
+
+static int nodelib_aux_next_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ /* first call */
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L,1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_next(t);
+ lua_settop(L,2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_next(t);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_aux_prev_filtered(lua_State *L)
+{
+ halfword t;
+ int i = (int) lua_tointeger(L, lua_upvalueindex(1));
+ if (lua_isnil(L, 2)) {
+ /* first call */
+ t = lmt_check_isnode(L, 1);
+ lua_settop(L,1);
+ } else {
+ t = lmt_check_isnode(L, 2);
+ t = node_prev(t);
+ lua_settop(L,2);
+ }
+ while (t && node_type(t) != i) {
+ t = node_prev(t);
+ }
+ if (t) {
+ nodelib_push_node_on_top(L, t);
+ lua_pushinteger(L, node_subtype(t));
+ return 2;
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_traverse_id(lua_State *L)
+{
+ if (lua_isnil(L, 2)) {
+ lua_pushcclosure(L, nodelib_aux_nil, 0);
+ return 1;
+ } else {
+ halfword n = lmt_check_isnode(L, 2);
+ if (lua_toboolean(L, 3)) {
+ if (lua_toboolean(L, 4)) {
+ n = tex_tail_of_node_list(n);
+ }
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_aux_prev_filtered, 1);
+ } else {
+ lua_settop(L, 1);
+ lua_pushcclosure(L, nodelib_aux_next_filtered, 1);
+ }
+ lmt_push_node_fast(L, n);
+ lua_pushnil(L);
+ return 3;
+ }
+}
+
+/* node.direct.length */
+/* node.direct.count */
+
+/*tex As with some other function that have a |last| we don't take that one into account. */
+
+static int nodelib_direct_length(lua_State *L)
+{
+ halfword first = nodelib_valid_direct_from_index(L, 1);
+ halfword last = nodelib_valid_direct_from_index(L, 2);
+ int count = 0;
+ if (first) {
+ while (first != last) {
+ count++;
+ first = node_next(first);
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+static int nodelib_direct_count(lua_State *L)
+{
+ quarterword id = lmt_toquarterword(L, 1);
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ halfword last = nodelib_valid_direct_from_index(L, 3);
+ int count = 0;
+ if (first) {
+ while (first != last) {
+ if (node_type(first) == id) {
+ count++;
+ }
+ first = node_next(first);
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+/*tex A few helpers for later usage: */
+
+inline static int nodelib_getattribute_value(lua_State *L, halfword n, int index)
+{
+ halfword key = (halfword) lua_tointeger(L, index);
+ halfword val = tex_has_attribute(n, key, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, val);
+ }
+ return 1;
+}
+
+inline static void nodelib_setattribute_value(lua_State *L, halfword n, int kindex, int vindex)
+{
+ if (lua_gettop(L) >= kindex) {
+ halfword key = lmt_tohalfword(L, kindex);
+ halfword val = lmt_opthalfword(L, vindex, unused_attribute_value);
+ if (val == unused_attribute_value) {
+ tex_unset_attribute(n, key, val);
+ } else {
+ tex_set_attribute(n, key, val);
+ }
+ } else {
+ luaL_error(L, "incorrect number of arguments");
+ }
+}
+
+/* node.direct.getfield */
+/* node.getfield */
+
+/*tex
+
+ The order is somewhat determined by the occurance of nodes and importance of fields. We use
+ |somenode[9]| as interface to attributes ... 30\% faster than has_attribute (1) because there
+ is no \LUA\ function overhead, and (2) because we already know that we deal with a node so no
+ checking is needed. The fast typecheck is needed (lua_check... is a slow down actually).
+
+ This is just a reminder for me: when used in the build page routine the |last_insert_ptr| and
+ |best_insert_ptr| are sort of tricky as the first in a list can be a fake node (zero zero list
+ being next). Because no properties are accessed this works ok. In the getfield routines we
+ can assume that these nodes are never seen (the pagebuilder constructs insert nodes using that
+ data). But it is something to keep an eye on when we open up more or add callbacks. So there
+ is a comment below.
+
+*/
+
+static int nodelib_common_getfield(lua_State *L, int direct, halfword n)
+{
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ {
+ return nodelib_getattribute_value(L, n, 2);
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 2);
+ int t = node_type(n);
+ if (lua_key_eq(s, id)) {
+ lua_pushinteger(L, t);
+ } else if (lua_key_eq(s, next)) {
+ if (tex_nodetype_has_next(t)) {
+ nodelib_push_direct_or_node(L, direct, node_next(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ nodelib_push_direct_or_node(L, direct, node_prev(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ nodelib_push_direct_or_node(L, direct, node_attr(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ lua_pushinteger(L, node_subtype(n));
+ } else {
+ /* nodelib_invalid_field_error(L, s, n); */
+ lua_pushnil(L);
+ }
+ } else {
+ switch(t) {
+ case glyph_node:
+ if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, glyph_font(n));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, glyph_character(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L, glyph_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L, glyph_y_offset(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L, glyph_data(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, tex_glyph_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, tex_glyph_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ // lua_pushinteger(L, char_depth_from_glyph(n));
+ lua_pushinteger(L, tex_glyph_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ // lua_pushinteger(L, char_total_from_glyph(n));
+ lua_pushinteger(L, tex_glyph_total(n));
+ } else if (lua_key_eq(s, scale)) {
+ lua_pushinteger(L, glyph_scale(n));
+ } else if (lua_key_eq(s, xscale)) {
+ lua_pushinteger(L, glyph_x_scale(n));
+ } else if (lua_key_eq(s, yscale)) {
+ lua_pushinteger(L, glyph_y_scale(n));
+ } else if (lua_key_eq(s, expansion)) {
+ lua_pushinteger(L, glyph_expansion(n));
+ } else if (lua_key_eq(s, state)) {
+ lua_pushinteger(L, get_glyph_state(n));
+ } else if (lua_key_eq(s, script)) {
+ lua_pushinteger(L, get_glyph_script(n));
+ } else if (lua_key_eq(s, language)) {
+ lua_pushinteger(L, get_glyph_language(n));
+ } else if (lua_key_eq(s, lhmin)) {
+ lua_pushinteger(L, get_glyph_lhmin(n));
+ } else if (lua_key_eq(s, rhmin)) {
+ lua_pushinteger(L, get_glyph_rhmin(n));
+ } else if (lua_key_eq(s, left)) {
+ lua_pushinteger(L, get_glyph_left(n));
+ } else if (lua_key_eq(s, right)) {
+ lua_pushinteger(L, get_glyph_right(n));
+ } else if (lua_key_eq(s, uchyph)) {
+ lua_pushinteger(L, get_glyph_uchyph(n));
+ } else if (lua_key_eq(s, hyphenate)) {
+ lua_pushinteger(L, get_glyph_hyphenate(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, get_glyph_options(n));
+ } else if (lua_key_eq(s, discpart)) {
+ lua_pushinteger(L, get_glyph_discpart(n));
+ } else if (lua_key_eq(s, protected)) {
+ lua_pushinteger(L, glyph_protected(n));
+ } else if (lua_key_eq(s, properties)) {
+ lua_pushinteger(L, glyph_properties(n));
+ } else if (lua_key_eq(s, group)) {
+ lua_pushinteger(L, glyph_group(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, glyph_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ /* candidates: whd (width,height,depth) */
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, box_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, box_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, box_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, box_total(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ } else if (lua_key_eq(s, shift)) {
+ lua_pushinteger(L, box_shift_amount(n));
+ } else if (lua_key_eq(s, glueorder)) {
+ lua_pushinteger(L, box_glue_order(n));
+ } else if (lua_key_eq(s, gluesign)) {
+ lua_pushinteger(L, box_glue_sign(n));
+ } else if (lua_key_eq(s, glueset)) {
+ lua_pushnumber(L, (double) box_glue_set(n)); /* float */
+ } else if (lua_key_eq(s, geometry)) {
+ lua_pushinteger(L, box_geometry(n));
+ } else if (lua_key_eq(s, orientation)) {
+ lua_pushinteger(L, box_orientation(n));
+ } else if (lua_key_eq(s, anchor)) {
+ lua_pushinteger(L, box_anchor(n));
+ } else if (lua_key_eq(s, source)) {
+ lua_pushinteger(L, box_source_anchor(n));
+ } else if (lua_key_eq(s, target)) {
+ lua_pushinteger(L, box_target_anchor(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L, box_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L, box_y_offset(n));
+ } else if (lua_key_eq(s, woffset)) {
+ lua_pushinteger(L, box_w_offset(n));
+ } else if (lua_key_eq(s, hoffset)) {
+ lua_pushinteger(L, box_h_offset(n));
+ } else if (lua_key_eq(s, doffset)) {
+ lua_pushinteger(L, box_d_offset(n));
+ } else if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, box_pre_migrated(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, box_post_migrated(n));
+ } else if (lua_key_eq(s, state)) {
+ lua_pushinteger(L, box_package_state(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, box_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case disc_node:
+ if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, disc_pre_break_head(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, disc_post_break_head(n));
+ } else if (lua_key_eq(s, replace)) {
+ nodelib_push_direct_or_node(L, direct, disc_no_break_head(n));
+ } else if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, disc_penalty(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, disc_options(n));
+ } else if (lua_key_eq(s, class)) {
+ lua_pushinteger(L, disc_class(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case glue_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, glue_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, glue_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, glue_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, glue_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, glue_shrink_order(n));
+ } else if (lua_key_eq(s, leader)) {
+ nodelib_push_direct_or_node(L, direct, glue_leader_ptr(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, glue_font(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L, glue_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case kern_node:
+ if (lua_key_eq(s, kern)) {
+ lua_pushinteger(L, kern_amount(n));
+ } else if (lua_key_eq(s, expansion)) {
+ lua_pushinteger(L, kern_expansion(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case penalty_node:
+ if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, penalty_amount(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case rule_node:
+ /* candidates: whd (width,height,depth) */
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, rule_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, rule_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, rule_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, rule_total(n));
+ } else if (lua_key_eq(s, xoffset)) {
+ lua_pushinteger(L,rule_x_offset(n));
+ } else if (lua_key_eq(s, yoffset)) {
+ lua_pushinteger(L,rule_y_offset(n));
+ } else if (lua_key_eq(s, left)) {
+ lua_pushinteger(L,rule_left(n));
+ } else if (lua_key_eq(s, right)) {
+ lua_pushinteger(L,rule_right(n));
+ } else if (lua_key_eq(s, data)) {
+ lua_pushinteger(L,rule_data(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ } else if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, tex_get_rule_font(n, text_style));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, rule_character(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case dir_node:
+ if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, dir_direction(n));
+ } else if (lua_key_eq(s, level)) {
+ lua_pushinteger(L, dir_level(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case whatsit_node:
+ lua_pushnil(L);
+ break;
+ case par_node:
+ /* not all of them here */
+ if (lua_key_eq(s, interlinepenalty)) {
+ lua_pushinteger(L, tex_get_local_interline_penalty(n));
+ } else if (lua_key_eq(s, brokenpenalty)) {
+ lua_pushinteger(L, tex_get_local_broken_penalty(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, par_dir(n));
+ } else if (lua_key_eq(s, leftbox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_left(n));
+ } else if (lua_key_eq(s, leftboxwidth)) {
+ lua_pushinteger(L, tex_get_local_left_width(n));
+ } else if (lua_key_eq(s, rightbox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_right(n));
+ } else if (lua_key_eq(s, rightboxwidth)) {
+ lua_pushinteger(L, tex_get_local_right_width(n));
+ } else if (lua_key_eq(s, middlebox)) {
+ nodelib_push_direct_or_node(L, direct, par_box_middle(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case math_char_node:
+ case math_text_char_node:
+ if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, kernel_math_family(n));
+ } else if (lua_key_eq(s, char)) {
+ lua_pushinteger(L, kernel_math_character(n));
+ } else if (lua_key_eq(s, font)) {
+ lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, kernel_math_options(n));
+ } else if (lua_key_eq(s, properties)) {
+ lua_pushinteger(L, kernel_math_properties(n));
+ } else if (lua_key_eq(s, group)) {
+ lua_pushinteger(L, kernel_math_group(n));
+ } else if (lua_key_eq(s, index)) {
+ lua_pushinteger(L, kernel_math_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case mark_node:
+ if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ lua_pushinteger(L, mark_index(n));
+ } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
+ if (lua_toboolean(L, 3)) {
+ lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0);
+ } else {
+ lmt_token_list_to_lua(L, mark_ptr(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case insert_node:
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, cost)) {
+ lua_pushinteger(L, insert_float_cost(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, insert_max_depth(n));
+ } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
+ lua_pushinteger(L, insert_total_height(n));
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, insert_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case math_node:
+ if (lua_key_eq(s, surround)) {
+ lua_pushinteger(L, math_surround(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, math_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, math_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, math_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, math_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, math_shrink_order(n));
+ } else if (lua_key_eq(s, penalty)) {
+ lua_pushinteger(L, math_penalty(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case style_node:
+ if (lua_key_eq(s, style)) {
+ lmt_push_math_style_name(L, style_style(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case parameter_node:
+ if (lua_key_eq(s, style)) {
+ lmt_push_math_style_name(L, parameter_style(n));
+ } else if (lua_key_eq(s, name)) {
+ lmt_push_math_parameter(L, parameter_name(n));
+ } else if (lua_key_eq(s, value)) {
+ halfword code = parameter_name(n);
+ if (code < 0 || code >= math_parameter_last) {
+ /* error */
+ lua_pushnil(L);
+ } else if (math_parameter_value_type(code)) {
+ /* todo, see tex_getmathparm */
+ lua_pushnil(L);
+ } else {
+ lua_pushinteger(L, parameter_value(n));
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ if (lua_key_eq(s, nucleus)) {
+ nodelib_push_direct_or_nil(L, noad_nucleus(n));
+ } else if (lua_key_eq(s, sub)) {
+ nodelib_push_direct_or_nil(L, noad_subscr(n));
+ } else if (lua_key_eq(s, sup)) {
+ nodelib_push_direct_or_nil(L, noad_supscr(n));
+ } else if (lua_key_eq(s, prime)) {
+ nodelib_push_direct_or_nil(L, noad_prime(n));
+ } else if (lua_key_eq(s, subpre)) {
+ nodelib_push_direct_or_nil(L, noad_subprescr(n));
+ } else if (lua_key_eq(s, suppre)) {
+ nodelib_push_direct_or_nil(L, noad_supprescr(n));
+ } else if (lua_key_eq(s, options)) {
+ lua_pushinteger(L, noad_options(n));
+ } else if (lua_key_eq(s, source)) {
+ lua_pushinteger(L, noad_source(n));
+ } else if (lua_key_eq(s, scriptorder)) {
+ lua_pushinteger(L, noad_script_order(n));
+ } else if (lua_key_eq(s, class)) {
+ lua_pushinteger(L, get_noad_main_class(n));
+ lua_pushinteger(L, get_noad_left_class(n));
+ lua_pushinteger(L, get_noad_right_class(n));
+ return 3;
+ } else {
+ switch(t) {
+ case simple_noad:
+ lua_pushnil(L);
+ break;
+ case radical_noad:
+ if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
+ nodelib_push_direct_or_node(L, direct, radical_left_delimiter(n));
+ } else if (lua_key_eq(s, right)) {
+ nodelib_push_direct_or_node(L, direct, radical_right_delimiter(n));
+ } else if (lua_key_eq(s, degree)) {
+ nodelib_push_direct_or_node(L, direct, radical_degree(n));
+ } else if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, noad_width(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case fraction_noad:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, fraction_rule_thickness(n));
+ } else if (lua_key_eq(s, numerator)) {
+ nodelib_push_direct_or_nil(L, fraction_numerator(n));
+ } else if (lua_key_eq(s, denominator)) {
+ nodelib_push_direct_or_nil(L, fraction_denominator(n));
+ } else if (lua_key_eq(s, left)) {
+ nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
+ } else if (lua_key_eq(s, right)) {
+ nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
+ } else if (lua_key_eq(s, middle)) {
+ nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
+ } else if (lua_key_eq(s, fam)) {
+ lua_pushinteger(L, noad_family(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case accent_noad:
+ if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_top_character(n));
+ } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_bottom_character(n));
+ } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
+ nodelib_push_direct_or_node(L, direct, accent_middle_character(n));
+ } else if (lua_key_eq(s, fraction)) {
+ lua_pushinteger(L, accent_fraction(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case fence_noad:
+ if (lua_key_eq(s, delimiter)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_list(n));
+ } else if (lua_key_eq(s, top)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_top(n));
+ } else if (lua_key_eq(s, bottom)) {
+ nodelib_push_direct_or_node(L, direct, fence_delimiter_bottom(n));
+ } else if (lua_key_eq(s, italic)) {
+ lua_pushinteger(L, noad_italic(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, noad_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, noad_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, noad_total(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ }
+ }
+ break;
+ case delimiter_node:
+ if (lua_key_eq(s, smallfamily)) {
+ lua_pushinteger(L, delimiter_small_family(n));
+ } else if (lua_key_eq(s, smallchar)) {
+ lua_pushinteger(L, delimiter_small_character(n));
+ } else if (lua_key_eq(s, largefamily)) {
+ lua_pushinteger(L, delimiter_large_family(n));
+ } else if (lua_key_eq(s, largechar)) {
+ lua_pushinteger(L, delimiter_large_character(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, kernel_math_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case split_node:
+ if (lua_key_eq(s, index)) {
+ lua_push_integer(L, split_insert_index(n));
+ } else if (lua_key_eq(s, lastinsert)) {
+ nodelib_push_direct_or_node(L, direct, split_last_insert(n)); /* see comment */
+ } else if (lua_key_eq(s, bestinsert)) {
+ nodelib_push_direct_or_node(L, direct, split_best_insert(n)); /* see comment */
+ } else if (lua_key_eq(s, broken)) {
+ nodelib_push_direct_or_node(L, direct, split_broken(n));
+ } else if (lua_key_eq(s, brokeninsert)) {
+ nodelib_push_direct_or_node(L, direct, split_broken_insert(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case choice_node:
+ /*tex We could check and combine some here but who knows how things evolve. */
+ if (lua_key_eq(s, display)) {
+ nodelib_push_direct_or_node(L, direct, choice_display_mlist(n));
+ } else if (lua_key_eq(s, text)) {
+ nodelib_push_direct_or_node(L, direct, choice_text_mlist(n));
+ } else if (lua_key_eq(s, script)) {
+ nodelib_push_direct_or_node(L, direct, choice_script_mlist(n));
+ } else if (lua_key_eq(s, scriptscript)) {
+ nodelib_push_direct_or_node(L, direct, choice_script_script_mlist(n));
+ } else if (lua_key_eq(s, pre)) {
+ nodelib_push_direct_or_node(L, direct, choice_pre_break(n));
+ } else if (lua_key_eq(s, post)) {
+ nodelib_push_direct_or_node(L, direct, choice_post_break(n));
+ } else if (lua_key_eq(s, replace)) {
+ nodelib_push_direct_or_node(L, direct, choice_no_break(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ if (lua_key_eq(s, count)) {
+ lua_pushinteger(L, attribute_count(n));
+ } else if (lua_key_eq(s, data)) {
+ nodelib_push_attribute_data(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case attribute_value_subtype:
+ if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
+ lua_pushinteger(L, attribute_index(n));
+ } else if (lua_key_eq(s, value)) {
+ lua_pushinteger(L, attribute_value(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ break;
+ case adjust_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, adjust_list(n));
+ } else if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ lua_pushinteger(L, adjust_index(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case unset_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, box_width(n));
+ } else if (lua_key_eq(s, height)) {
+ lua_pushinteger(L, box_height(n));
+ } else if (lua_key_eq(s, depth)) {
+ lua_pushinteger(L, box_depth(n));
+ } else if (lua_key_eq(s, total)) {
+ lua_pushinteger(L, box_total(n));
+ } else if (lua_key_eq(s, direction)) {
+ lua_pushinteger(L, checked_direction_value(box_dir(n)));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, box_glue_shrink(n));
+ } else if (lua_key_eq(s, glueorder)) {
+ lua_pushinteger(L, box_glue_order(n));
+ } else if (lua_key_eq(s, gluesign)) {
+ lua_pushinteger(L, box_glue_sign(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, box_glue_stretch(n));
+ } else if (lua_key_eq(s, count)) {
+ lua_pushinteger(L, box_span_count(n));
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ /*
+ case attribute_list_node:
+ lua_pushnil(L);
+ break;
+ */
+ case boundary_node:
+ if (lua_key_eq(s, data) || lua_key_eq(s, value)) {
+ lua_pushinteger(L, boundary_data(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case glue_spec_node:
+ if (lua_key_eq(s, width)) {
+ lua_pushinteger(L, glue_amount(n));
+ } else if (lua_key_eq(s, stretch)) {
+ lua_pushinteger(L, glue_stretch(n));
+ } else if (lua_key_eq(s, shrink)) {
+ lua_pushinteger(L, glue_shrink(n));
+ } else if (lua_key_eq(s, stretchorder)) {
+ lua_pushinteger(L, glue_stretch_order(n));
+ } else if (lua_key_eq(s, shrinkorder)) {
+ lua_pushinteger(L, glue_shrink_order(n));
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ lua_pushnil(L);
+ break;
+ }
+ }
+ return 1;
+}
+
+static int nodelib_direct_getfield(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ return nodelib_common_getfield(L, 1, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_index(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ if (n) {
+ return nodelib_common_getfield(L, 0, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+static int nodelib_userdata_getfield(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ return nodelib_common_getfield(L, 0, n);
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+/* node.setfield */
+/* node.direct.setfield */
+
+/*
+ We used to check for glue_spec nodes in some places but if you do such a you have it coming
+ anyway.
+*/
+
+static int nodelib_common_setfield(lua_State *L, int direct, halfword n)
+{
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ {
+ nodelib_setattribute_value(L, n, 2, 3);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ const char *s = lua_tostring(L, 2);
+ int t = node_type(n);
+ if (lua_key_eq(s, next)) {
+ if (tex_nodetype_has_next(t)) {
+ node_next(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, prev)) {
+ if (tex_nodetype_has_prev(t)) {
+ node_prev(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, attr)) {
+ if (tex_nodetype_has_attributes(t)) {
+ tex_attach_attribute_list_attribute(n, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else {
+ goto CANTSET;
+ }
+ } else if (lua_key_eq(s, subtype)) {
+ if (tex_nodetype_has_subtype(t)) {
+ node_subtype(n) = lmt_toquarterword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ } else {
+ switch(t) {
+ case glyph_node:
+ if (lua_key_eq(s, font)) {
+ glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, char)) {
+ glyph_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, xoffset)) {
+ glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, yoffset)) {
+ glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, scale)) {
+ glyph_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_scale(n)) {
+ glyph_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, xscale)) {
+ glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_x_scale(n)) {
+ glyph_x_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, yscale)) {
+ glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 3);
+ if (! glyph_y_scale(n)) {
+ glyph_y_scale(n) = 1000;
+ }
+ } else if (lua_key_eq(s, data)) {
+ glyph_data(n) = lmt_opthalfword(L, 3, unused_attribute_value);
+ } else if (lua_key_eq(s, expansion)) {
+ glyph_expansion(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, state)) {
+ set_glyph_state(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, script)) {
+ set_glyph_script(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, language)) {
+ set_glyph_language(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, left)) {
+ set_glyph_left(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, right)) {
+ set_glyph_right(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, lhmin)) {
+ set_glyph_lhmin(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, rhmin)) {
+ set_glyph_rhmin(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, uchyph)) {
+ set_glyph_uchyph(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, hyphenate)) {
+ set_glyph_hyphenate(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, options)) {
+ set_glyph_options(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, discpart)) {
+ set_glyph_discpart(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, protected)) {
+ glyph_protected(n) = lmt_tosingleword(L, 3);
+ } else if (lua_key_eq(s, width)) {
+ /* not yet */
+ } else if (lua_key_eq(s, height)) {
+ /* not yet */
+ } else if (lua_key_eq(s, depth)) {
+ /* not yet */
+ } else if (lua_key_eq(s, properties)) {
+ glyph_properties(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, group)) {
+ glyph_group(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ glyph_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case hlist_node:
+ case vlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, width)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, direction)) {
+ box_dir(n) = (singleword) nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, shift)) {
+ box_shift_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, glueorder)) {
+ box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, gluesign)) {
+ box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, glueset)) {
+ box_glue_set(n) = (glueratio) lua_tonumber(L, 3); /* integer or float */
+ } else if (lua_key_eq(s, geometry)) {
+ box_geometry(n) = (singleword) lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, orientation)) {
+ box_orientation(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, anchor)) {
+ box_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, source)) {
+ box_source_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, target)) {
+ box_target_anchor(n) = lmt_tohalfword(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, xoffset)) {
+ box_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, yoffset)) {
+ box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, woffset)) {
+ box_w_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, hoffset)) {
+ box_h_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, doffset)) {
+ box_d_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ tex_check_box_geometry(n);
+ } else if (lua_key_eq(s, pre)) {
+ box_pre_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
+ } else if (lua_key_eq(s, post)) {
+ box_post_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
+ } else if (lua_key_eq(s, state)) {
+ box_package_state(n) = (singleword) lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ box_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case disc_node:
+ if (lua_key_eq(s, pre)) {
+ tex_set_disc_field(n, pre_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, post)) {
+ tex_set_disc_field(n, post_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, replace)) {
+ tex_set_disc_field(n, no_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
+ } else if (lua_key_eq(s, penalty)) {
+ disc_penalty(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ disc_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, class)) {
+ disc_class(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case glue_node:
+ if (lua_key_eq(s, width)) {
+ glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ glue_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ glue_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, leader)) {
+ glue_leader_ptr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, font)) {
+ glue_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, data)) {
+ glue_data(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case kern_node:
+ if (lua_key_eq(s, kern)) {
+ kern_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, expansion)) {
+ kern_expansion(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case penalty_node:
+ if (lua_key_eq(s, penalty)) {
+ penalty_amount(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case rule_node:
+ if (lua_key_eq(s, width)) {
+ rule_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ rule_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ rule_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, xoffset)) {
+ rule_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, yoffset)) {
+ rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, left)) {
+ rule_left(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, right)) {
+ rule_right(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, data)) {
+ rule_data(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, font)) {
+ tex_set_rule_font(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, fam)) {
+ tex_set_rule_family(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, char)) {
+ rule_character(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case dir_node:
+ if (lua_key_eq(s, direction)) {
+ dir_direction(n) = nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, level)) {
+ dir_level(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case whatsit_node:
+ return 0;
+ case par_node:
+ /* not all of them here */
+ if (lua_key_eq(s, interlinepenalty)) {
+ tex_set_local_interline_penalty(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, brokenpenalty)) {
+ tex_set_local_broken_penalty(n, lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, direction)) {
+ par_dir(n) = nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, leftbox)) {
+ par_box_left(n) = nodelib_getlist(L, 3);
+ } else if (lua_key_eq(s, leftboxwidth)) {
+ tex_set_local_left_width(n, lmt_roundnumber(L, 3));
+ } else if (lua_key_eq(s, rightbox)) {
+ par_box_right(n) = nodelib_getlist(L, 3);
+ } else if (lua_key_eq(s, rightboxwidth)) {
+ tex_set_local_right_width(n, lmt_roundnumber(L, 3));
+ } else if (lua_key_eq(s, middlebox)) {
+ par_box_middle(n) = nodelib_getlist(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case math_char_node:
+ case math_text_char_node:
+ if (lua_key_eq(s, fam)) {
+ kernel_math_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, char)) {
+ kernel_math_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ kernel_math_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, properties)) {
+ kernel_math_properties(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, group)) {
+ kernel_math_group(n) = lmt_toquarterword(L, 3);
+ } else if (lua_key_eq(s, index)) {
+ kernel_math_index(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case mark_node:
+ if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
+ halfword m = lmt_tohalfword(L, 3);
+ if (tex_valid_mark(m)) {
+ mark_index(n) = m;
+ }
+ } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
+ tex_delete_token_reference(mark_ptr(n));
+ mark_ptr(n) = lmt_token_list_from_lua(L, 3); /* check ref */
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case insert_node:
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, cost)) {
+ insert_float_cost(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ insert_max_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
+ insert_total_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ insert_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case math_node:
+ if (lua_key_eq(s, surround)) {
+ math_surround(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, width)) {
+ math_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ math_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ math_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ math_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ math_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, penalty)) {
+ math_penalty(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case style_node:
+ if (lua_key_eq(s, style)) {
+ style_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
+ } else {
+ /* return nodelib_cantset(L, n, s); */
+ }
+ return 0;
+ case parameter_node:
+ if (lua_key_eq(s, style)) {
+ parameter_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
+ } else if (lua_key_eq(s, name)) {
+ parameter_name(n) = lmt_get_math_parameter(L, 2, parameter_name(n));
+ } else if (lua_key_eq(s, value)) {
+ halfword code = parameter_name(n);
+ if (code < 0 || code >= math_parameter_last) {
+ /* error */
+ } else if (math_parameter_value_type(code)) {
+ /* todo, see tex_setmathparm */
+ } else {
+ parameter_value(n) = lmt_tohalfword(L, 3);
+ }
+ }
+ return 0;
+ case simple_noad:
+ case radical_noad:
+ case fraction_noad:
+ case accent_noad:
+ case fence_noad:
+ /* fence has less */
+ if (lua_key_eq(s, nucleus)) {
+ noad_nucleus(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, sub)) {
+ noad_subscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, sup)) {
+ noad_supscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, subpre)) {
+ noad_subprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, suppre)) {
+ noad_supprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, prime)) {
+ noad_prime(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, source)) {
+ noad_source(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, options)) {
+ noad_options(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, scriptorder)) {
+ noad_script_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, class)) {
+ halfword c = lmt_tohalfword(L, 3);
+ set_noad_main_class(n, c);
+ set_noad_left_class(n, lmt_opthalfword(L, 4, c));
+ set_noad_right_class(n, lmt_opthalfword(L, 5, c));
+ } else {
+ switch (t) {
+ case simple_noad:
+ // return nodelib_cantset(L, n, s);
+ break;
+ case radical_noad:
+ if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
+ radical_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, right)) {
+ radical_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, degree)) {
+ radical_degree(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, width)) {
+ noad_width(n) = lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case fraction_noad:
+ if (lua_key_eq(s, width)) {
+ fraction_rule_thickness(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, numerator)) {
+ fraction_numerator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, denominator)) {
+ fraction_denominator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, left)) {
+ fraction_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, right)) {
+ fraction_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, middle)) {
+ fraction_middle_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, fam)) {
+ set_noad_family(n, lmt_tohalfword(L, 3));
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case accent_noad:
+ if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
+ accent_top_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
+ accent_bottom_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
+ accent_middle_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, fraction)) {
+ accent_fraction(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case fence_noad:
+ if (lua_key_eq(s, delimiter)) {
+ fence_delimiter_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, top)) {
+ fence_delimiter_top(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bottom)) {
+ fence_delimiter_bottom(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, italic)) {
+ noad_italic(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ noad_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ noad_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ }
+ }
+ return 0;
+ case delimiter_node:
+ if (lua_key_eq(s, smallfamily)) {
+ delimiter_small_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, smallchar)) {
+ delimiter_small_character(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, largefamily)) {
+ delimiter_large_family(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, largechar)) {
+ delimiter_large_character(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case sub_box_node:
+ case sub_mlist_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ kernel_math_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case split_node: /* might go away */
+ if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_insert_id(index)) {
+ split_insert_index(n) = index;
+ }
+ } else if (lua_key_eq(s, lastinsert)) {
+ split_last_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, bestinsert)) {
+ split_best_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, broken)) {
+ split_broken(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, brokeninsert)) {
+ split_broken_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case choice_node:
+ if (lua_key_eq(s, display)) {
+ choice_display_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, text)) {
+ choice_text_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, script)) {
+ choice_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, scriptscript)) {
+ choice_script_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case attribute_node:
+ switch (node_subtype(n)) {
+ case attribute_list_subtype:
+ if (lua_key_eq(s, count)) {
+ attribute_count(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case attribute_value_subtype:
+ if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
+ attribute_index(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, value)) {
+ attribute_value(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ default:
+ /* just ignored */
+ return 0;
+ }
+ break;
+ case adjust_node:
+ if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ adjust_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else if (lua_key_eq(s, index)) {
+ halfword index = lmt_tohalfword(L, 3);
+ if (tex_valid_adjust_index(index)) {
+ adjust_index(n) = index;
+ }
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case unset_node:
+ if (lua_key_eq(s, width)) {
+ box_width(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, height)) {
+ box_height(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, depth)) {
+ box_depth(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, direction)) {
+ box_dir(n) = (singleword) nodelib_getdirection(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ box_glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, glueorder)) {
+ box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, gluesign)) {
+ box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
+ } else if (lua_key_eq(s, stretch)) {
+ box_glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, count)) {
+ box_span_count(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
+ box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case boundary_node:
+ if (lua_key_eq(s, value)) {
+ boundary_data(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ case glue_spec_node:
+ if (lua_key_eq(s, width)) {
+ glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretch)) {
+ glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, shrink)) {
+ glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
+ } else if (lua_key_eq(s, stretchorder)) {
+ glue_stretch_order(n) = lmt_tohalfword(L, 3);
+ } else if (lua_key_eq(s, shrinkorder)) {
+ glue_shrink_order(n) = lmt_tohalfword(L, 3);
+ } else {
+ goto CANTSET;
+ }
+ return 0;
+ default:
+ return luaL_error(L, "you can't assign to a %s node (%d)\n", lmt_interface.node_data[t].name, n);
+ }
+ CANTSET:
+ return luaL_error(L,"you can't set field %s in a %s node (%d)", s, lmt_interface.node_data[t].name, n);
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_setfield(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_common_setfield(L, 1, n);
+ }
+ return 0;
+}
+
+static int nodelib_userdata_newindex(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ if (n) {
+ nodelib_common_setfield(L, 0, n);
+ }
+ return 0;
+}
+
+static int nodelib_userdata_setfield(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ nodelib_common_setfield(L, 0, n);
+ }
+ return 0;
+}
+
+/* tex serializing */
+
+static int verbose = 1; /* This might become an option (then move this in a state)! */
+
+static void nodelib_tostring(lua_State *L, halfword n, const char *tag)
+{
+ char msg[256];
+ char a[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
+ char v[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
+ halfword t = node_type(n);
+ halfword s = node_subtype(n);
+ node_info nd = lmt_interface.node_data[t];
+ if (tex_nodetype_has_prev(t) && node_prev(n)) {
+ snprintf(a, 7, "%6d", (int) node_prev(n));
+ }
+ if (node_next(n)) {
+ snprintf(v, 7, "%6d", (int) node_next(n));
+ }
+ if (t == whatsit_node) {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ } else if (! tex_nodetype_has_subtype(n)) {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s>", tag, a, (int) n, v, nd.name);
+ } else if (verbose) {
+ /*tex Sloooow! But subtype lists can have holes. */
+ value_info *sd = nd.subtypes;
+ int j = -1;
+ if (sd) {
+ // if (t == glyph_node) {
+ // s = tex_subtype_of_glyph(n);
+ // }
+ if (s >= nd.first && s <= nd.last) {
+ for (int i = 0; ; i++) {
+ if (sd[i].id == s) {
+ j = i;
+ break ;
+ } else if (sd[i].id < 0) {
+ break;
+ }
+ }
+ }
+ }
+ if (j < 0) {
+ snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ } else {
+ snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %s>", tag, a, (int) n, v, nd.name, sd[j].name);
+ }
+ } else {
+ snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
+ }
+ lua_pushstring(L, (const char *) msg);
+}
+
+/* __tostring node.tostring */
+
+static int nodelib_userdata_tostring(lua_State *L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ nodelib_tostring(L, n, lua_key(node));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.tostring */
+
+static int nodelib_direct_tostring(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_tostring(L, n, lua_key(direct));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* __eq */
+
+static int nodelib_userdata_equal(lua_State *L)
+{
+ halfword n = *((halfword *) lua_touserdata(L, 1));
+ halfword m = *((halfword *) lua_touserdata(L, 2));
+ lua_pushboolean(L, (n == m));
+ return 1;
+}
+
+/* node.ligaturing */
+
+static int nodelib_direct_ligaturing(lua_State *L)
+{
+ if (lua_gettop(L) >= 1) {
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
+ halfword p = node_prev(h);
+ tex_couple_nodes(tmp_head, h);
+ node_tail(tmp_head) = t;
+ t = tex_handle_ligaturing(tmp_head, t);
+ if (p) {
+ node_next(p) = node_next(tmp_head) ;
+ }
+ node_prev(node_next(tmp_head)) = p ;
+ lua_pushinteger(L, node_next(tmp_head));
+ lua_pushinteger(L, t);
+ lua_pushboolean(L, 1);
+ tex_flush_node(tmp_head);
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ lua_pushboolean(L, 0);
+ return 2;
+}
+
+/* node.kerning */
+
+static int nodelib_direct_kerning(lua_State *L)
+{
+ if (lua_gettop(L) >= 1) {
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
+ halfword p = node_prev(h);
+ tex_couple_nodes(tmp_head, h);
+ node_tail(tmp_head) = t;
+ t = tex_handle_kerning(tmp_head, t);
+ if (p) {
+ node_next(p) = node_next(tmp_head) ;
+ }
+ node_prev(node_next(tmp_head)) = p ;
+ lua_pushinteger(L, node_next(tmp_head));
+ if (t) {
+ lua_pushinteger(L, t);
+ } else {
+ lua_pushnil(L);
+ }
+ lua_pushboolean(L, 1);
+ tex_flush_node(tmp_head);
+ return 3;
+ }
+ }
+ lua_pushnil(L);
+ lua_pushboolean(L, 0);
+ return 2;
+}
+
+/*tex
+ It's more consistent to have it here (so we will alias in lang later). Todo: if no glyph then
+ quit.
+*/
+
+static int nodelib_direct_hyphenating(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ if (! t) {
+ t = h;
+ while (node_next(t)) {
+ t = node_next(t);
+ }
+ }
+ tex_hyphenate_list(h, t); /* todo: grab new tail */
+ } else {
+ /*tex We could consider setting |h| and |t| to |null|. */
+ }
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, t);
+ lua_pushboolean(L, 1);
+ return 3;
+}
+
+static int nodelib_direct_collapsing(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword c1 = lmt_optinteger(L, 2, ex_hyphen_char_par);
+ halfword c2 = lmt_optinteger(L, 3, 0x2013);
+ halfword c3 = lmt_optinteger(L, 4, 0x2014);
+ tex_collapse_list(h, c1, c2, c3);
+ }
+ lua_pushinteger(L, h);
+ return 1;
+}
+
+/* node.protect_glyphs */
+/* node.unprotect_glyphs */
+
+inline static void nodelib_aux_protect_all(halfword h)
+{
+ while (h) {
+ if (node_type(h) == glyph_node) {
+ glyph_protected(h) = glyph_protected_text_code;
+ }
+ h = node_next(h);
+ }
+}
+inline static void nodelib_aux_unprotect_all(halfword h)
+{
+ while (h) {
+ if (node_type(h) == glyph_node) {
+ glyph_protected(h) = glyph_unprotected_code;
+ }
+ h = node_next(h);
+ }
+}
+
+inline static void nodelib_aux_protect_node(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_protected(n) = glyph_protected_text_code;
+ break;
+ case disc_node:
+ nodelib_aux_protect_all(disc_no_break_head(n));
+ nodelib_aux_protect_all(disc_pre_break_head(n));
+ nodelib_aux_protect_all(disc_post_break_head(n));
+ break;
+ }
+}
+
+inline static void nodelib_aux_unprotect_node(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_protected(n) = glyph_unprotected_code;
+ break;
+ case disc_node:
+ nodelib_aux_unprotect_all(disc_no_break_head(n));
+ nodelib_aux_unprotect_all(disc_pre_break_head(n));
+ nodelib_aux_unprotect_all(disc_post_break_head(n));
+ break;
+ }
+}
+
+/* node.direct.protect_glyphs */
+/* node.direct.unprotect_glyphs */
+
+static int nodelib_direct_protectglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_aux_protect_node(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_unprotectglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_aux_unprotect_node(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_protectglyphs(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword tail = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ while (head) {
+ nodelib_aux_protect_node(head);
+ if (head == tail) {
+ break;
+ } else {
+ head = node_next(head);
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_unprotectglyphs(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ halfword tail = nodelib_valid_direct_from_index(L, 2);
+ if (head) {
+ while (head) {
+ nodelib_aux_unprotect_node(head);
+ if (head == tail) {
+ break;
+ } else {
+ head = node_next(head);
+ }
+ }
+ }
+ return 0;
+}
+
+/* node.direct.first_glyph */
+
+static int nodelib_direct_firstglyph(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ halfword t = nodelib_valid_direct_from_index(L, 2);
+ if (h) {
+ halfword savetail = null;
+ if (t) {
+ savetail = node_next(t);
+ node_next(t) = null;
+ }
+ /*tex
+ We go to the first unprocessed character so that is one with a value <= 0xFF and we
+ don't care about what the value is.
+ */
+ while (h && (node_type(h) != glyph_node || glyph_protected(h))) {
+ h = node_next(h);
+ }
+ if (savetail) {
+ node_next(t) = savetail;
+ }
+ lua_pushinteger(L, h);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.find_node(head) : node, subtype*/
+/* node.direct.find_node(head,subtype) : node */
+
+static int nodelib_direct_findnode(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ if (h) {
+ halfword t = lmt_tohalfword(L, 2);
+ if (lua_gettop(L) > 2) {
+ halfword s = lmt_tohalfword(L, 3);
+ while (h) {
+ if (node_type(h) == t && node_subtype(h) == s) {
+ lua_pushinteger(L, h);
+ return 1;
+ } else {
+ h = node_next(h);
+ }
+ }
+ } else {
+ while (h) {
+ if (node_type(h) == t) {
+ lua_pushinteger(L, h);
+ lua_pushinteger(L, node_subtype(h));
+ return 2;
+ } else {
+ h = node_next(h);
+ }
+ }
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.direct.has_glyph */
+
+static int nodelib_direct_hasglyph(lua_State *L)
+{
+ halfword h = nodelib_valid_direct_from_index(L, 1);
+ while (h) {
+ switch (node_type(h)) {
+ case glyph_node:
+ case disc_node:
+ nodelib_push_direct_or_nil(L, h);
+ return 1;
+ default:
+ h = node_next(h);
+ break;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/* node.getword */
+
+static inline int nodelib_aux_in_word(halfword n)
+{
+ switch (node_type(n)) {
+ case glyph_node:
+ case disc_node:
+ return 1;
+ case kern_node:
+ return node_subtype(n) == font_kern_subtype;
+ default:
+ return 0;
+ }
+}
+
+static int nodelib_direct_getwordrange(lua_State *L)
+{
+ halfword m = nodelib_valid_direct_from_index(L, 1);
+ if (m) {
+ /*tex We don't check on type if |m|. */
+ halfword l = m;
+ halfword r = m;
+ while (node_prev(l) && nodelib_aux_in_word(node_prev(l))) {
+ l = node_prev(l);
+ }
+ while (node_next(r) && nodelib_aux_in_word(node_next(r))) {
+ r = node_next(r);
+ }
+ nodelib_push_direct_or_nil(L, l);
+ nodelib_push_direct_or_nil(L, r);
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+/* node.inuse */
+
+static int nodelib_userdata_inuse(lua_State *L)
+{
+ int counts[max_node_type + 1] = { 0 };
+ int n = tex_n_of_used_nodes(&counts[0]);
+ lua_createtable(L, 0, max_node_type);
+ for (int i = 0; i < max_node_type; i++) {
+ if (counts[i]) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_pushinteger(L, counts[i]);
+ lua_rawset(L, -3);
+ }
+ }
+ lua_pushinteger(L, n);
+ return 2;
+}
+
+/*tex A bit of a cheat: some nodes can turn into another one due to the same size. */
+
+static int nodelib_userdata_instock(lua_State *L)
+{
+ int counts[max_node_type + 1] = { 0 };
+ int n = 0;
+ lua_createtable(L, 0, max_node_type);
+ for (int i = 1; i < max_chain_size; i++) {
+ halfword p = lmt_node_memory_state.free_chain[i];
+ while (p) {
+ if (node_type(p) <= max_node_type) {
+ ++counts[node_type(p)];
+ }
+ p = node_next(p);
+ }
+ }
+ for (int i = 0; i < max_node_type; i++) {
+ if (counts[i]) {
+ lua_pushstring(L, lmt_interface.node_data[i].name);
+ lua_pushinteger(L, counts[i]);
+ lua_rawset(L, -3);
+ n += counts[i];
+ }
+ }
+ lua_pushinteger(L, n);
+ return 2;
+}
+
+
+/* node.usedlist */
+
+static int nodelib_userdata_usedlist(lua_State *L)
+{
+ lmt_push_node_fast(L, tex_list_node_mem_usage());
+ return 1;
+}
+
+/* node.direct.usedlist */
+
+static int nodelib_direct_usedlist(lua_State *L)
+{
+ lua_pushinteger(L, tex_list_node_mem_usage());
+ return 1;
+}
+
+/* node.direct.protrusionskipable(node m) */
+
+static int nodelib_direct_protrusionskipable(lua_State *L)
+{
+ halfword n = lmt_tohalfword(L, 1);
+ if (n) {
+ lua_pushboolean(L, tex_protrusion_skipable(n));
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.currentattributes(node m) */
+
+static int nodelib_userdata_currentattributes(lua_State* L)
+{
+ halfword n = tex_current_attribute_list();
+ if (n) {
+ lmt_push_node_fast(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.currentattributes(node m) */
+
+static int nodelib_direct_currentattributes(lua_State* L)
+{
+ halfword n = tex_current_attribute_list();
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.todirect */
+
+static int nodelib_direct_todirect(lua_State* L)
+{
+ if (lua_type(L, 1) != LUA_TNUMBER) {
+ /* assume node, no further testing, used in known situations */
+ void* n = lua_touserdata(L, 1);
+ if (n) {
+ lua_pushinteger(L, *((halfword*)n));
+ }
+ else {
+ lua_pushnil(L);
+ }
+ } /* else assume direct and returns argument */
+ return 1;
+}
+
+static int nodelib_direct_tovaliddirect(lua_State* L)
+{
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* node.direct.tonode */
+
+static int nodelib_direct_tonode(lua_State* L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword* a = (halfword*) lua_newuserdatauv(L, sizeof(halfword), 0);
+ *a = n;
+ lua_get_metatablelua(node_instance);
+ lua_setmetatable(L, -2);
+ } /* else assume node and return argument */
+ return 1;
+}
+
+/* direct.ischar */
+/* direct.isglyph */
+
+/*tex
+
+ This can save a lookup call, but although there is a little benefit it doesn't pay of in the end
+ as we have to simulate it in \MKIV.
+
+ \starttyping
+ if (glyph_data(n) != unused_attribute_value) {
+ lua_pushinteger(L, glyph_data(n));
+ return 2;
+ }
+ \stoptyping
+
+ possible return values:
+
+ \starttyping
+ <nil when no node>
+ <nil when no glyph> <id of node>
+ <false when glyph and already marked as done or when not>
+ <character code when font matches or when no font passed>
+ \stoptyping
+
+ data : when checked should be equal, false or nil is zero
+ state : when checked should be equal, unless false or zero
+
+*/
+
+static int nodelib_direct_check_char(lua_State* L, halfword n)
+{
+ if (! glyph_protected(n)) {
+ halfword b = 0;
+ halfword f = (halfword) lua_tointegerx(L, 2, &b);
+ if (! b) {
+ goto OKAY;
+ } else if (f == glyph_font(n)) {
+ switch (lua_gettop(L)) {
+ case 2:
+ /* (node,font) */
+ goto OKAY;
+ case 3:
+ /* (node,font,data) */
+ if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ case 4:
+ /* (node,font,data,state) */
+ if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
+ halfword state = (halfword) lua_tointegerx(L, 4, NULL);
+ if (! state || state == glyph_state(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ case 5:
+ /* (node,font,data,scale,xscale,yscale) */
+ if (lua_tointeger(L, 3) == glyph_scale(n) && lua_tointeger(L, 4) == glyph_x_scale(n) && lua_tointeger(L, 5) == glyph_y_scale(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ case 6:
+ /* (node,font,data,scale,xscale,yscale) */
+ if (lua_tointegerx(L, 3, NULL) == glyph_data(n) && lua_tointeger(L, 4) == glyph_scale(n) && lua_tointeger(L, 5) == glyph_x_scale(n) && lua_tointeger(L, 6) == glyph_y_scale(n)) {
+ goto OKAY;
+ } else {
+ break;
+ }
+ /* case 7: */
+ /* (node,font,data,scale,scale,xscale,yscale)*/
+ }
+ }
+ }
+ return -1;
+ OKAY:
+ return glyph_character(n);
+}
+
+static int nodelib_direct_ischar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) != glyph_node) {
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 2;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+ }
+ } else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+/*
+ This one is kind of special and is a way to quickly test what we are at now and what is
+ coming. It saves some extra calls but has a rather hybrid set of return values, depending
+ on the situation:
+
+ \starttyping
+ isnextchar(n,[font],[data],[state],[scale,xscale,yscale])
+ isprevchar(n,[font],[data],[state],[scale,xscale,yscale])
+
+ glyph : nil | next false | next char | next char nextchar
+ otherwise : nil | next false id
+ \stoptyping
+
+ Beware: it is not always measurable faster than multiple calls but it can make code look a
+ bit better (at least in \CONTEXT\ where we can use it a few times). There are more such
+ hybrid helpers where the return value depends on the node type.
+
+ The second glyph is okay when the most meaningful properties are the same. We assume that
+ states can differ so we don't check for that. One of the few assumptions when using
+ \CONTEXT.
+
+*/
+
+inline static int nodelib_aux_similar_glyph(halfword first, halfword second)
+{
+ return
+ node_type(second) == glyph_node
+ && glyph_font(second) == glyph_font(first)
+ && glyph_data(second) == glyph_data(first)
+ /* && glyph_state(second) == glyph_state(first) */
+ && glyph_scale(second) == glyph_scale(first)
+ && glyph_x_scale(second) == glyph_x_scale(first)
+ && glyph_y_scale(second) == glyph_y_scale(first)
+ ;
+}
+
+static int nodelib_direct_isnextchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* beware, don't mix push and pop */
+ halfword nxt = node_next(n);
+ if (node_type(n) != glyph_node) {
+ nodelib_push_direct_or_nil(L, nxt);
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 3;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ nodelib_push_direct_or_nil(L, nxt);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ if (nxt && nodelib_aux_similar_glyph(n, nxt)) {
+ lua_pushinteger(L, glyph_character(nxt));
+ return 3;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 2;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isprevchar(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* beware, don't mix push and pop */
+ halfword prv = node_prev(n);
+ if (node_type(n) != glyph_node) {
+ nodelib_push_direct_or_nil(L, prv);
+ lua_pushnil(L);
+ lua_pushinteger(L, node_type(n));
+ return 3;
+ } else {
+ halfword chr = nodelib_direct_check_char(L, n);
+ nodelib_push_direct_or_nil(L, prv);
+ if (chr >= 0) {
+ lua_pushinteger(L, chr);
+ if (prv && nodelib_aux_similar_glyph(n, prv)) {
+ lua_pushinteger(L, glyph_character(prv));
+ return 3;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 2;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ } else {
+ lua_pushnil(L); /* no glyph at all */
+ lua_pushnil(L); /* no glyph at all */
+ }
+ return 2;
+}
+
+static int nodelib_direct_isnextglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_next(n));
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+static int nodelib_direct_isprevglyph(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ nodelib_push_direct_or_nil(L, node_prev(n));
+ if (node_type(n) != glyph_node) {
+ lua_pushboolean(L, 0);
+ lua_pushinteger(L, node_type(n));
+ } else {
+ /* protected as well as unprotected */
+ lua_pushinteger(L, glyph_character(n));
+ lua_pushinteger(L, glyph_font(n));
+ }
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+
+/* direct.usesfont */
+
+inline static int nodelib_aux_uses_font_disc(lua_State *L, halfword n, halfword font)
+{
+ while (n) {
+ if ((node_type(n) == glyph_node) && (glyph_font(n) == font)) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ n = node_next(n);
+ }
+ return 0;
+}
+
+static int nodelib_direct_usesfont(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ halfword f = lmt_tohalfword(L, 2);
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushboolean(L, glyph_font(n) == f);
+ return 1;
+ case disc_node:
+ if (nodelib_aux_uses_font_disc(L, disc_pre_break_head(n), f)) {
+ return 1;
+ } else if (nodelib_aux_uses_font_disc(L, disc_post_break_head(n), f)) {
+ return 1;
+ } else if (nodelib_aux_uses_font_disc(L, disc_no_break_head(n), f)) {
+ return 1;
+ }
+ /*
+ {
+ halfword c = disc_pre_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ c = disc_post_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ c = disc_no_break_head(n);
+ while (c) {
+ if (type(c) == glyph_node && font(c) == f) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ c = node_next(c);
+ }
+ }
+ */
+ break;
+ /* todo: other node types */
+ }
+ }
+ lua_pushboolean(L, 0);
+ return 1;
+}
+
+/* boxes */
+
+/* node.getbox = tex.getbox */
+/* node.setbox = tex.setbox */
+
+/* node.direct.getbox */
+/* node.direct.setbox */
+
+static int nodelib_direct_getbox(lua_State *L)
+{
+ int id = lmt_get_box_id(L, 1, 1);
+ if (id >= 0) {
+ int t = tex_get_tex_box_register(id, 0);
+ if (t) {
+ lua_pushinteger(L, t);
+ return 1;
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int nodelib_direct_setbox(lua_State *L)
+{
+ int flags = 0;
+ int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
+ int id = lmt_get_box_id(L, slot++, 1);
+ if (id >= 0) {
+ int n;
+ switch (lua_type(L, slot)) {
+ case LUA_TBOOLEAN:
+ {
+ n = lua_toboolean(L, slot);
+ if (n == 0) {
+ n = null;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ case LUA_TNIL:
+ n = null;
+ break;
+ default:
+ {
+ n = nodelib_valid_direct_from_index(L, slot);
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ break;
+ default:
+ /*tex Alternatively we could |hpack|. */
+ return luaL_error(L, "setbox: incompatible node type (%s)\n",get_node_name(node_type(n)));
+ }
+ }
+ }
+ break;
+ }
+ tex_set_tex_box_register(id, n, flags, 0);
+ }
+ return 0;
+}
+
+/* node.isnode(n) */
+
+static int nodelib_userdata_isnode(lua_State *L)
+{
+ halfword n = lmt_maybe_isnode(L, 1);
+ if (n) {
+ lua_pushinteger (L, n);
+ } else {
+ lua_pushboolean (L, 0);
+ }
+ return 1;
+}
+
+/* node.direct.isdirect(n) (handy for mixed usage testing) */
+
+static int nodelib_direct_isdirect(lua_State *L)
+{
+ if (lua_type(L, 1) != LUA_TNUMBER) {
+ lua_pushboolean(L, 0); /* maybe valid test too */
+ }
+ /* else return direct */
+ return 1;
+}
+
+/* node.direct.isnode(n) (handy for mixed usage testing) */
+
+static int nodelib_direct_isnode(lua_State *L)
+{
+ if (! lmt_maybe_isnode(L, 1)) {
+ lua_pushboolean(L, 0);
+ } else {
+ /*tex Assume and return node. */
+ }
+ return 1;
+}
+
+/*tex Maybe we should allocate a proper index |0 .. var_mem_max| but not now. */
+
+static int nodelib_userdata_getproperty(lua_State *L)
+{ /* <node> */
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lua_rawgeti(L, -1, n); /* actually it is a hash */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_direct_getproperty(lua_State *L)
+{ /* <direct> */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lua_rawgeti(L, -1, n); /* actually it is a hash */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_userdata_setproperty(lua_State *L)
+{
+ /* <node> <value> */
+ halfword n = lmt_check_isnode(L, 1);
+ if (n) {
+ lua_settop(L, 2);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <node> <value> <propertytable> */
+ lua_replace(L, -3);
+ /* <propertytable> <value> */
+ lua_rawseti(L, -2, n); /* actually it is a hash */
+ }
+ return 0;
+}
+
+static int nodelib_direct_setproperty(lua_State *L)
+{
+ /* <direct> <value> */
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ lua_settop(L, 2);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <node> <value> <propertytable> */
+ lua_replace(L, 1);
+ /* <propertytable> <value> */
+ lua_rawseti(L, 1, n); /* actually it is a hash */
+ }
+ return 0;
+}
+
+/*tex
+
+ These two getters are kind of tricky as they can mess up the otherwise hidden table. But
+ normally these are under control of the macro package so we can control it somewhat.
+
+*/
+
+static int nodelib_direct_getpropertiestable(lua_State *L)
+{ /* <node|direct> */
+ if (lua_toboolean(L, lua_gettop(L))) {
+ /*tex Beware: this can have side effects when used without care. */
+ lmt_initialize_properties(1);
+ }
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ return 1;
+}
+
+static int nodelib_userdata_getpropertiestable(lua_State *L)
+{ /* <node|direct> */
+ lua_get_metatablelua(node_properties_indirect);
+ return 1;
+}
+
+/* extra helpers */
+
+static void nodelib_direct_effect_done(lua_State *L, halfword amount, halfword stretch, halfword shrink, halfword stretch_order, halfword shrink_order)
+{
+ halfword parent = nodelib_valid_direct_from_index(L, 2);
+ if (parent) {
+ halfword sign = box_glue_sign(parent);
+ if (sign != normal_glue_sign) {
+ switch (node_type(parent)) {
+ case hlist_node:
+ case vlist_node:
+ {
+ double w = (double) amount;
+ switch (sign) {
+ case stretching_glue_sign:
+ if (stretch_order == box_glue_order(parent)) {
+ w += stretch * (double) box_glue_set(parent);
+ }
+ break;
+ case shrinking_glue_sign:
+ if (shrink_order == box_glue_order(parent)) {
+ w -= shrink * (double) box_glue_set(parent);
+ }
+ break;
+ }
+ if (lua_toboolean(L, 3)) {
+ lua_pushinteger(L, lmt_roundedfloat(w));
+ } else {
+ lua_pushnumber(L, w);
+ }
+ return;
+ }
+ }
+ }
+ }
+ lua_pushinteger(L, amount);
+}
+
+static int nodelib_direct_effectiveglue(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glue_node:
+ nodelib_direct_effect_done(L, glue_amount(n), glue_stretch(n), glue_shrink(n),glue_stretch_order(n), glue_shrink_order(n));
+ break;
+ case math_node:
+ if (math_surround(n)) {
+ lua_pushinteger(L, math_surround(n));
+ } else {
+ nodelib_direct_effect_done(L, math_amount(n), math_stretch(n), math_shrink(n), math_stretch_order(n), math_shrink_order(n));
+ }
+ break;
+ default:
+ lua_pushinteger(L, 0);
+ break;
+ }
+ } else {
+ lua_pushinteger(L, 0);
+ }
+ return 1;
+}
+
+/*tex
+
+ Disc nodes are kind of special in the sense that their head is not the head as we see it, but
+ a special node that has status info of which head and tail are part. Normally when proper
+ set/get functions are used this status node is all right but if a macro package permits
+ arbitrary messing around, then it can at some point call the following cleaner, just before
+ linebreaking kicks in. This one is not called automatically because if significantly slows down
+ the line break routing.
+
+*/
+
+static int nodelib_direct_checkdiscretionaries(lua_State *L) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ while (n) {
+ if (node_type(n) == disc_node) {
+ tex_check_disc_field(n);
+ }
+ n = node_next(n) ;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_checkdiscretionary(lua_State *L) {
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == disc_node) {
+ halfword p = disc_pre_break_head(n);
+ disc_pre_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ p = disc_post_break_head(n);
+ disc_post_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ p = disc_no_break_head(n);
+ disc_no_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
+ }
+ return 0;
+}
+
+static int nodelib_direct_flattendiscretionaries(lua_State *L)
+{
+ int count = 0;
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ head = tex_flatten_discretionaries(head, &count, lua_toboolean(L, 2)); /* nest */
+ } else {
+ head = null;
+ }
+ nodelib_push_direct_or_nil(L, head);
+ lua_pushinteger(L, count);
+ return 2;
+}
+
+static int nodelib_direct_softenhyphens(lua_State *L)
+{
+ int found = 0;
+ int replaced = 0;
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ tex_soften_hyphens(head, &found, &replaced);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ lua_pushinteger(L, found);
+ lua_pushinteger(L, replaced);
+ return 3;
+}
+
+/*tex The fields related to input tracking: */
+
+static int nodelib_direct_setinputfields(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ /* there is no need to test for tag and line as two arguments are mandate */
+ halfword tag = lmt_tohalfword(L, 2);
+ halfword line = lmt_tohalfword(L, 3);
+ switch (node_type(n)) {
+ case glyph_node:
+ glyph_input_file(n) = tag;
+ glyph_input_line(n) = line;
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ box_input_file(n) = tag;
+ box_input_line(n) = line;
+ break;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_getinputfields(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ switch (node_type(n)) {
+ case glyph_node:
+ lua_pushinteger(L, glyph_input_file(n));
+ lua_pushinteger(L, glyph_input_line(n));
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ lua_pushinteger(L, box_input_file(n));
+ lua_pushinteger(L, box_input_line(n));
+ break;
+ default:
+ return 0;
+ }
+ return 2;
+ }
+ return 0;
+}
+
+static int nodelib_direct_makeextensible(lua_State *L)
+{
+ int top = lua_gettop(L);
+ if (top >= 3) {
+ halfword fnt = lmt_tohalfword(L, 1);
+ halfword chr = lmt_tohalfword(L, 2);
+ halfword target = lmt_tohalfword(L, 3);
+ halfword size = lmt_opthalfword(L, 4, 0);
+ halfword overlap = lmt_opthalfword(L, 5, 65536);
+ halfword attlist = null;
+ halfword b = null;
+ int horizontal = 0;
+ if (top >= 4) {
+ overlap = lmt_tohalfword(L, 4);
+ if (top >= 5) {
+ horizontal = lua_toboolean(L, 5);
+ if (top >= 6) {
+ attlist = nodelib_valid_direct_from_index(L, 6);
+ }
+ }
+ }
+ b = tex_make_extensible(fnt, chr, target, overlap, horizontal, attlist, size);
+ lua_pushinteger(L, b);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/*tex experiment */
+
+static int nodelib_direct_flattenleaders(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ int count = 0;
+ if (n) {
+ switch (node_type(n)) {
+ case hlist_node:
+ case vlist_node:
+ tex_flatten_leaders(n, &count);
+ break;
+ }
+ }
+ lua_pushinteger(L, count);
+ return 1;
+}
+
+/*tex test */
+
+static int nodelib_direct_isvalid(lua_State *L)
+{
+ lua_pushboolean(L, nodelib_valid_direct_from_index(L, 1));
+ return 1;
+}
+
+/* getlinestuff : LS RS LH RH ID PF FIRST LAST */
+
+inline static halfword set_effective_width(halfword source, halfword sign, halfword order, double glue)
+{
+ halfword amount = glue_amount(source);
+ switch (sign) {
+ case stretching_glue_sign:
+ if (glue_stretch_order(source) == order) {
+ return amount + scaledround((double) glue_stretch(source) * glue);
+ } else {
+ break;
+ }
+ case shrinking_glue_sign:
+ if (glue_shrink_order(source) == order) {
+ return amount + scaledround((double) glue_shrink(source) * glue);
+ } else {
+ break;
+ }
+ }
+ return amount;
+}
+
+static int nodelib_direct_getnormalizedline(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == hlist_node && node_subtype(n) == line_list) {
+ halfword head = box_list(n);
+ halfword tail = head;
+ halfword first = head;
+ halfword last = tail;
+ halfword current = head;
+ halfword ls = 0;
+ halfword rs = 0;
+ halfword is = 0;
+ halfword pr = 0;
+ halfword pl = 0;
+ halfword ir = 0;
+ halfword il = 0;
+ halfword lh = 0;
+ halfword rh = 0;
+ halfword sign = box_glue_sign(n);
+ halfword order = box_glue_order(n);
+ double glue = box_glue_set(n);
+ while (current) {
+ tail = current ;
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case left_skip_glue : ls = set_effective_width(current, sign, order, glue); break;
+ case right_skip_glue : rs = set_effective_width(current, sign, order, glue); break;
+ case par_fill_left_skip_glue : pl = set_effective_width(current, sign, order, glue); break;
+ case par_fill_right_skip_glue : pr = set_effective_width(current, sign, order, glue); break;
+ case par_init_left_skip_glue : il = set_effective_width(current, sign, order, glue); break;
+ case par_init_right_skip_glue : ir = set_effective_width(current, sign, order, glue); break;
+ case indent_skip_glue : is = set_effective_width(current, sign, order, glue); break;
+ case left_hang_skip_glue : lh = set_effective_width(current, sign, order, glue); break;
+ case right_hang_skip_glue : rh = set_effective_width(current, sign, order, glue); break;
+ }
+ }
+ current = node_next(current);
+ }
+ current = head;
+ while (current) {
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case left_skip_glue:
+ case par_fill_left_skip_glue:
+ case par_init_left_skip_glue:
+ case indent_skip_glue:
+ case left_hang_skip_glue:
+ first = current;
+ current = node_next(current);
+ break;
+ default:
+ current = null;
+ break;
+ }
+ } else {
+ current = null;
+ }
+ }
+ current = tail;
+ while (current) {
+ if (node_type(current) == glue_node) {
+ switch (node_subtype(current)) {
+ case right_skip_glue:
+ case par_fill_right_skip_glue:
+ case par_init_right_skip_glue:
+ case right_hang_skip_glue:
+ last = current;
+ current = node_prev(current);
+ break;
+ default:
+ current = null;
+ break;
+ }
+ } else {
+ current = null;
+ }
+ }
+ lua_createtable(L, 0, 14); /* we could add some more */
+ lua_push_integer_at_key(L, leftskip, ls);
+ lua_push_integer_at_key(L, rightskip, rs);
+ lua_push_integer_at_key(L, lefthangskip, lh);
+ lua_push_integer_at_key(L, righthangskip, rh);
+ lua_push_integer_at_key(L, indent, is);
+ lua_push_integer_at_key(L, parfillleftskip, pl);
+ lua_push_integer_at_key(L, parfillrightskip, pr);
+ lua_push_integer_at_key(L, parinitleftskip, il);
+ lua_push_integer_at_key(L, parinitrightskip, ir);
+ lua_push_integer_at_key(L, first, first); /* points to a skip */
+ lua_push_integer_at_key(L, last, last); /* points to a skip */
+ lua_push_integer_at_key(L, head, head);
+ lua_push_integer_at_key(L, tail, tail);
+ // lua_push_integer_at_key(L, width, box_width(n));
+ return 1;
+ }
+ return 0;
+}
+
+/*tex new */
+
+static int nodelib_direct_ignoremathskip(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n && node_type(n) == math_node) {
+ lua_pushboolean(L, tex_ignore_math_skip(n));
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+static int nodelib_direct_reverse(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ n = tex_reversed_node_list(n);
+ }
+ nodelib_push_direct_or_nil(L, n);
+ return 1;
+}
+
+static int nodelib_direct_exchange(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ halfword first = nodelib_valid_direct_from_index(L, 2);
+ if (first) {
+ halfword second = nodelib_valid_direct_from_index(L, 3);
+ if (! second) {
+ second = node_next(first);
+ }
+ if (second) {
+ halfword pf = node_prev(first);
+ halfword ns = node_next(second);
+ if (first == head) {
+ head = second;
+ } else if (second == head) {
+ head = first;
+ }
+ if (second == node_next(first)) {
+ node_prev(first) = second;
+ node_next(second) = first;
+ } else {
+ halfword nf = node_next(first);
+ halfword ps = node_prev(second);
+ node_prev(first) = ps;
+ if (ps) {
+ node_next(ps) = first;
+ }
+ node_next(second) = nf;
+ if (nf) {
+ node_prev(nf) = second;
+ }
+ }
+ node_next(first) = ns;
+ node_prev(second) = pf;
+ if (pf) {
+ node_next(pf) = second;
+ }
+ if (ns) {
+ node_prev(ns) = first;
+ }
+ }
+ }
+ }
+ nodelib_push_direct_or_nil(L, head);
+ return 1;
+}
+
+/*tex experiment */
+
+inline static halfword nodelib_aux_migrate_decouple(halfword head, halfword current, halfword next, halfword *first, halfword *last)
+{
+ halfword prev = node_prev(current);
+ tex_uncouple_node(current);
+ if (current == head) {
+ node_prev(next) = null;
+ head = next;
+ } else {
+ tex_try_couple_nodes(prev, next);
+ }
+ if (*first) {
+ tex_couple_nodes(*last, current);
+ } else {
+ *first = current;
+ }
+ *last = current;
+ return head;
+}
+
+static halfword lmt_direct_migrate_locate(halfword head, halfword *first, halfword *last, int inserts, int marks)
+{
+ halfword current = head;
+ while (current) {
+ halfword next = node_next(current);
+ switch (node_type(current)) {
+ case vlist_node:
+ case hlist_node:
+ {
+ halfword list = box_list(current);
+ if (list) {
+ box_list(current) = lmt_direct_migrate_locate(list, first, last, inserts, marks);
+ }
+ break;
+ }
+ case insert_node:
+ {
+ if (inserts) {
+ head = nodelib_aux_migrate_decouple(head, current, next, first, last);
+ halfword list = insert_list(current);
+ if (list) {
+ insert_list(current) = lmt_direct_migrate_locate(list, first, last, inserts, marks);
+ }
+ }
+ break;
+ }
+ case mark_node:
+ {
+ if (marks) {
+ head = nodelib_aux_migrate_decouple(head, current, next, first, last);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ current = next;
+ }
+ return head;
+}
+
+static int nodelib_direct_migrate(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ if (head) {
+ int inserts = lua_type(L, 3) == LUA_TBOOLEAN ? lua_toboolean(L, 2) : 1;
+ int marks = lua_type(L, 2) == LUA_TBOOLEAN ? lua_toboolean(L, 3) : 1;
+ halfword first = null;
+ halfword last = null;
+ halfword current = head;
+ while (current) {
+ switch (node_type(current)) {
+ case vlist_node:
+ case hlist_node:
+ {
+ halfword list = box_list(current);
+ if (list) {
+ box_list(current) = lmt_direct_migrate_locate(list, &first, &last, inserts, marks);
+ }
+ break;
+ }
+ case insert_node:
+ if (inserts) {
+ halfword list = insert_list(current);
+ if (list) {
+ insert_list(current) = lmt_direct_migrate_locate(list, &first, &last, inserts, marks);
+ }
+ break;
+ }
+ }
+ current = node_next(current);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, first);
+ nodelib_push_direct_or_nil(L, last);
+ return 3;
+ }
+ return 0;
+}
+
+/*tex experiment */
+
+static int nodelib_aux_no_left(halfword n, halfword l, halfword r)
+{
+ if (tex_has_glyph_option(n, (singleword) l)) {
+ return 1;
+ } else {
+ n = node_prev(n);
+ if (n) {
+ if (node_type(n) == disc_node) {
+ n = disc_no_break_tail(n);
+ }
+ if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) r)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int nodelib_aux_no_right(halfword n, halfword r, halfword l)
+{
+ if (tex_has_glyph_option(n, (singleword) r)) {
+ return 1;
+ } else {
+ n = node_next(n);
+ if (node_type(n) == disc_node) {
+ n = disc_no_break_head(n);
+ }
+ if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) l)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int nodelib_direct_hasglyphoption(lua_State *L)
+{
+ halfword current = nodelib_valid_direct_from_index(L, 1);
+ int result = 0;
+ if (current && node_type(current) == glyph_node) {
+ int option = lua_tointeger(L, 2);
+ switch (option) {
+ case glyph_option_normal_glyph: // 0x00
+ break;
+ case glyph_option_no_left_ligature: // 0x01
+ result = nodelib_aux_no_left(current, glyph_option_no_left_ligature, glyph_option_no_right_ligature);
+ break;
+ case glyph_option_no_right_ligature: // 0x02
+ result = nodelib_aux_no_right(current, glyph_option_no_right_ligature, glyph_option_no_left_ligature);
+ break;
+ case glyph_option_no_left_kern: // 0x04
+ result = nodelib_aux_no_left(current, glyph_option_no_left_kern, glyph_option_no_right_kern);
+ break;
+ case glyph_option_no_right_kern: // 0x08
+ result = nodelib_aux_no_right(current, glyph_option_no_right_kern, glyph_option_no_left_kern);
+ break;
+ case glyph_option_no_expansion: // 0x10
+ /* some day */
+ break;
+ case glyph_option_no_protrusion: // 0x20
+ /* some day */
+ break;
+ case glyph_option_no_italic_correction:
+ case glyph_option_math_discretionary:
+ case glyph_option_math_italics_too:
+ result = tex_has_glyph_option(current, option);
+ break;
+ }
+ }
+ lua_pushboolean(L, result);
+ return 1;
+}
+
+static int nodelib_direct_getspeciallist(lua_State *L)
+{
+ const char *s = lua_tostring(L, 1);
+ halfword head = null;
+ halfword tail = null;
+ if (! s) {
+ /* error */
+ } else if (lua_key_eq(s, pageinserthead)) {
+ head = tex_get_special_node_list(page_insert_list_type, &tail);
+ } else if (lua_key_eq(s, contributehead)) {
+ head = tex_get_special_node_list(contribute_list_type, &tail);
+ } else if (lua_key_eq(s, pagehead)) {
+ head = tex_get_special_node_list(page_list_type, &tail);
+ } else if (lua_key_eq(s, temphead)) {
+ head = tex_get_special_node_list(temp_list_type, &tail);
+ } else if (lua_key_eq(s, holdhead)) {
+ head = tex_get_special_node_list(hold_list_type, &tail);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ head = tex_get_special_node_list(post_adjust_list_type, &tail);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ head = tex_get_special_node_list(pre_adjust_list_type, &tail);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ head = tex_get_special_node_list(post_migrate_list_type, &tail);
+ } else if (lua_key_eq(s, premigratehead)) {
+ head = tex_get_special_node_list(pre_migrate_list_type, &tail);
+ } else if (lua_key_eq(s, alignhead)) {
+ head = tex_get_special_node_list(align_list_type, &tail);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ head = tex_get_special_node_list(page_discards_list_type, &tail);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ head = tex_get_special_node_list(split_discards_list_type, &tail);
+ }
+ nodelib_push_direct_or_nil(L, head);
+ nodelib_push_direct_or_nil(L, tail);
+ return 2;
+}
+
+static int nodelib_direct_isspeciallist(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 1);
+ int istail = 0;
+ int checked = tex_is_special_node_list(head, &istail);
+ if (checked >= 0) {
+ lua_pushinteger(L, checked);
+ if (istail) {
+ lua_pushboolean(L, 1);
+ return 2;
+ }
+ } else {
+ lua_pushboolean(L, 0);
+ }
+ return 1;
+}
+
+static int nodelib_direct_setspeciallist(lua_State *L)
+{
+ halfword head = nodelib_valid_direct_from_index(L, 2);
+ const char *s = lua_tostring(L, 1);
+ if (! s) {
+ /* error */
+ } else if (lua_key_eq(s, pageinserthead)) {
+ tex_set_special_node_list(page_insert_list_type, head);
+ } else if (lua_key_eq(s, contributehead)) {
+ tex_set_special_node_list(contribute_list_type, head);
+ } else if (lua_key_eq(s, pagehead)) {
+ tex_set_special_node_list(page_list_type, head);
+ } else if (lua_key_eq(s, temphead)) {
+ tex_set_special_node_list(temp_list_type, head);
+ } else if (lua_key_eq(s, holdhead)) {
+ tex_set_special_node_list(hold_list_type, head);
+ } else if (lua_key_eq(s, postadjusthead)) {
+ tex_set_special_node_list(post_adjust_list_type, head);
+ } else if (lua_key_eq(s, preadjusthead)) {
+ tex_set_special_node_list(pre_adjust_list_type, head);
+ } else if (lua_key_eq(s, postmigratehead)) {
+ tex_set_special_node_list(post_migrate_list_type, head);
+ } else if (lua_key_eq(s, premigratehead)) {
+ tex_set_special_node_list(pre_migrate_list_type, head);
+ } else if (lua_key_eq(s, alignhead)) {
+ tex_set_special_node_list(align_list_type, head);
+ } else if (lua_key_eq(s, pagediscardshead)) {
+ tex_set_special_node_list(page_discards_list_type, head);
+ } else if (lua_key_eq(s, splitdiscardshead)) {
+ tex_set_special_node_list(split_discards_list_type, head);
+ }
+ return 0;
+}
+
+/*tex
+ This is just an experiment, so it might go away. Using a list can be a bit faster that traverse
+ (2-4 times) but you only see a difference on very last lists and even then one need some 10K
+ loops to notice it. If that gain is needed, I bet that the document takes a while to process
+ anyway.
+*/
+
+static int nodelib_direct_getnodes(lua_State *L)
+{
+ halfword n = nodelib_valid_direct_from_index(L, 1);
+ if (n) {
+ int i = 0;
+ /* maybe count */
+ lua_newtable(L);
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ int t = lua_tonumber(L, 2);
+ if (lua_type(L, 3) == LUA_TNUMBER) {
+ int s = lua_tonumber(L, 3);
+ while (n) {
+ if (node_type(n) == t && node_subtype(n) == s) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ }
+ n = node_next(n);
+ }
+ } else {
+ while (n) {
+ if (node_type(n) == t) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ }
+ n = node_next(n);
+ }
+ }
+ } else {
+ while (n) {
+ lua_pushinteger(L, n);
+ lua_rawseti(L, -2, ++i);
+ n = node_next(n);
+ }
+ }
+ if (i) {
+ return 1;
+ } else {
+ lua_pop(L, 1);
+ }
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+/*tex experiment */
+
+static int nodelib_direct_getusedattributes(lua_State* L)
+{
+ lua_newtable(L); /* todo: preallocate */
+ for (int current = lmt_node_memory_state.nodes_data.top; current > lmt_node_memory_state.reserved; current--) {
+ if (lmt_node_memory_state.nodesizes[current] > 0 && (node_type(current) == attribute_node && node_subtype(current) != attribute_list_subtype)) {
+ if (lua_rawgeti(L, -1, attribute_index(current)) == LUA_TTABLE) {
+ lua_pushboolean(L, 1);
+ lua_rawseti(L, -2, attribute_value(current));
+ lua_pop(L, 1);
+ /* not faster: */
+ // if (lua_rawgeti(L, -1, attribute_value(current)) != LUA_TBOOLEAN) {
+ // lua_pushboolean(L, 1);
+ // lua_rawseti(L, -3, attribute_value(current));
+ // }
+ // lua_pop(L, 2);
+ } else {
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushboolean(L, 1);
+ lua_rawseti(L, -2, attribute_value(current));
+ lua_rawseti(L, -2, attribute_index(current));
+ }
+ }
+ }
+ return 1;
+}
+
+static int nodelib_shared_getcachestate(lua_State *L)
+{
+ lua_pushboolean(L, attribute_cache_disabled);
+ return 1;
+}
+
+/*tex done */
+
+static int nodelib_get_property_t(lua_State *L)
+{ /* <table> <node> */
+ halfword n = lmt_check_isnode(L, 2);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <table> <node> <properties> */
+ lua_rawgeti(L, -1, n);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int nodelib_set_property_t(lua_State *L)
+{
+ /* <table> <node> <value> */
+ halfword n = lmt_check_isnode(L, 2);
+ if (n) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <table> <node> <value> <properties> */
+ lua_insert(L, -2);
+ /* <table> <node> <properties> <value> */
+ lua_rawseti(L, -2, n);
+ }
+ return 0;
+}
+
+/* */
+
+static int nodelib_hybrid_gluetostring(lua_State *L)
+{
+ halfword glue = lua_type(L, 1) == LUA_TNUMBER ? nodelib_valid_direct_from_index(L, 1): lmt_maybe_isnode(L, 1);
+ if (glue) {
+ switch (node_type(glue)) {
+ case glue_node:
+ case glue_spec_node:
+ {
+ int saved_selector = lmt_print_state.selector;
+ char *str = NULL;
+ lmt_print_state.selector = new_string_selector_code;
+ tex_print_spec(glue, pt_unit);
+ str = tex_take_string(NULL);
+ lmt_print_state.selector = saved_selector;
+ lua_pushstring(L, str);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static const struct luaL_Reg nodelib_p[] = {
+ { "__index", nodelib_get_property_t },
+ { "__newindex", nodelib_set_property_t },
+ { NULL, NULL },
+};
+
+void lmt_initialize_properties(int set_size)
+{
+ lua_State *L = lmt_lua_state.lua_instance;
+ if (lmt_node_memory_state.node_properties_id) {
+ /*tex
+ We should clean up but for now we accept a leak because these tables are still empty,
+ and when you do this once again you're probably messing up. This should actually be
+ enough:
+ */
+ luaL_unref(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ lmt_node_memory_state.node_properties_id = 0;
+ }
+ if (set_size) {
+ tex_engine_get_config_number("propertiessize", &lmt_node_memory_state.node_properties_table_size);
+ if (lmt_node_memory_state.node_properties_table_size < 0) {
+ lmt_node_memory_state.node_properties_table_size = 0;
+ }
+ /*tex It's a hash, not an array because we jump by size. */
+ lua_createtable(L, 0, lmt_node_memory_state.node_properties_table_size);
+ } else {
+ lua_newtable(L);
+ }
+ /* <properties table> */
+ lmt_node_memory_state.node_properties_id = luaL_ref(L, LUA_REGISTRYINDEX);
+ /* not needed, so unofficial */
+ lua_pushstring(L, NODE_PROPERTIES_DIRECT);
+ /* <direct identifier> */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
+ /* <direct identifier> <properties table> */
+ lua_settable(L, LUA_REGISTRYINDEX);
+ /* */
+ lua_pushstring(L, NODE_PROPERTIES_INDIRECT);
+ /* <indirect identifier> */
+ lua_newtable(L);
+ /* <indirect identifier> <stub table> */
+ luaL_newmetatable(L, NODE_PROPERTIES_INSTANCE);
+ /* <indirect identifier> <stub table> <metatable> */
+ luaL_setfuncs(L, nodelib_p, 0);
+ /* <indirect identifier> <stub table> <metatable> */
+ lua_setmetatable(L, -2);
+ /* <indirect identifier> <stub table> */
+ lua_settable(L, LUA_REGISTRYINDEX);
+ /* */
+}
+
+/* node.direct.* */
+
+static const struct luaL_Reg nodelib_direct_function_list[] = {
+ { "checkdiscretionaries", nodelib_direct_checkdiscretionaries },
+ { "checkdiscretionary", nodelib_direct_checkdiscretionary },
+ { "copy", nodelib_direct_copy },
+ { "copylist", nodelib_direct_copylist },
+ { "copyonly", nodelib_direct_copyonly },
+ { "count", nodelib_direct_count },
+ { "currentattributes", nodelib_direct_currentattributes },
+ { "dimensions", nodelib_direct_dimensions },
+ { "effectiveglue", nodelib_direct_effectiveglue },
+ { "endofmath", nodelib_direct_endofmath },
+ { "findattribute", nodelib_direct_findattribute },
+ { "findattributerange", nodelib_direct_findattributerange },
+ { "findnode", nodelib_direct_findnode },
+ { "firstglyph", nodelib_direct_firstglyph },
+ { "flattendiscretionaries", nodelib_direct_flattendiscretionaries },
+ { "softenhyphens", nodelib_direct_softenhyphens },
+ { "flushlist", nodelib_direct_flushlist },
+ { "flushnode", nodelib_direct_flushnode },
+ { "free", nodelib_direct_free },
+ { "getattribute", nodelib_direct_getattribute },
+ { "getattributes", nodelib_direct_getattributes },
+ { "getpropertiestable", nodelib_direct_getpropertiestable },
+ { "getinputfields", nodelib_direct_getinputfields },
+ { "getattributelist", nodelib_direct_getattributelist },
+ { "getboth", nodelib_direct_getboth },
+ { "getbottom", nodelib_direct_getbottom },
+ { "getbox", nodelib_direct_getbox },
+ { "getchar", nodelib_direct_getchar },
+ { "getchardict", nodelib_direct_getchardict },
+ { "getcharspec", nodelib_direct_getcharspec },
+ { "getchoice", nodelib_direct_getchoice },
+ { "getclass", nodelib_direct_getclass },
+ { "getstate", nodelib_direct_getstate },
+ { "getscript", nodelib_direct_getscript },
+ { "getdata", nodelib_direct_getdata },
+ { "getleftdelimiter", nodelib_direct_getleftdelimiter },
+ { "getrightdelimiter", nodelib_direct_getrightdelimiter },
+ { "getdelimiter", nodelib_direct_getdelimiter },
+ { "getdenominator", nodelib_direct_getdenominator },
+ { "getdegree", nodelib_direct_getdegree },
+ { "getdepth", nodelib_direct_getdepth },
+ { "getdirection", nodelib_direct_getdirection },
+ { "getdisc", nodelib_direct_getdisc },
+ { "getdiscpart", nodelib_direct_getdiscpart },
+ { "getexpansion", nodelib_direct_getexpansion },
+ { "getfam", nodelib_direct_getfam },
+ { "getfield", nodelib_direct_getfield },
+ { "getfont", nodelib_direct_getfont },
+ { "getglue", nodelib_direct_getglue },
+ { "getglyphdata", nodelib_direct_getglyphdata },
+ { "getheight", nodelib_direct_getheight },
+ { "getindex", nodelib_direct_getindex },
+ { "getid", nodelib_direct_getid },
+ { "getkern", nodelib_direct_getkern },
+ { "getlanguage", nodelib_direct_getlanguage },
+ { "getleader", nodelib_direct_getleader },
+ { "getlist", nodelib_direct_getlist },
+ { "getnext", nodelib_direct_getnext },
+ { "getnormalizedline", nodelib_direct_getnormalizedline },
+ { "getnodes", nodelib_direct_getnodes },
+ { "getnucleus", nodelib_direct_getnucleus },
+ { "getnumerator", nodelib_direct_getnumerator },
+ { "getoffsets", nodelib_direct_getoffsets },
+ { "getanchors", nodelib_direct_getanchors },
+ { "gettop", nodelib_direct_gettop },
+ { "getscales", nodelib_direct_getscales },
+ { "getscale", nodelib_direct_getscale },
+ { "getxscale", nodelib_direct_getxscale },
+ { "getyscale", nodelib_direct_getyscale },
+ { "xscaled", nodelib_direct_xscaled },
+ { "yscaled", nodelib_direct_yscaled },
+ { "getxyscales", nodelib_direct_getxyscales },
+ { "getoptions", nodelib_direct_getoptions },
+ { "hasgeometry", nodelib_direct_hasgeometry },
+ { "getgeometry", nodelib_direct_getgeometry },
+ { "setgeometry", nodelib_direct_setgeometry },
+ { "getorientation", nodelib_direct_getorientation },
+ { "getpenalty", nodelib_direct_getpenalty },
+ { "getpost", nodelib_direct_getpost },
+ { "getpre", nodelib_direct_getpre },
+ { "getprev", nodelib_direct_getprev },
+ { "getproperty", nodelib_direct_getproperty },
+ { "getreplace", nodelib_direct_getreplace },
+ { "getshift", nodelib_direct_getshift },
+ { "getsub", nodelib_direct_getsub },
+ { "getsubpre", nodelib_direct_getsubpre },
+ { "getsubtype", nodelib_direct_getsubtype },
+ { "getsup", nodelib_direct_getsup },
+ { "getsuppre", nodelib_direct_getsuppre },
+ { "getprime", nodelib_direct_getprime },
+ { "gettotal" , nodelib_direct_gettotal },
+ { "getwhd", nodelib_direct_getwhd },
+ { "getwidth", nodelib_direct_getwidth },
+ { "getwordrange", nodelib_direct_getwordrange },
+ { "getparstate", nodelib_direct_getparstate },
+ { "hasattribute", nodelib_direct_hasattribute },
+ { "hasdimensions", nodelib_direct_hasdimensions },
+ { "hasfield", nodelib_direct_hasfield },
+ { "hasglyph", nodelib_direct_hasglyph },
+ { "hasglyphoption", nodelib_direct_hasglyphoption },
+ { "hpack", nodelib_direct_hpack },
+ { "hyphenating", nodelib_direct_hyphenating },
+ { "collapsing", nodelib_direct_collapsing }, /*tex A funny name but like |ligaturing| and |hyphenating|. */
+ { "ignoremathskip", nodelib_direct_ignoremathskip },
+ { "insertafter", nodelib_direct_insertafter },
+ { "insertbefore", nodelib_direct_insertbefore },
+ { "appendaftertail", nodelib_direct_appendaftertail },
+ { "prependbeforehead", nodelib_direct_prependbeforehead },
+ { "ischar", nodelib_direct_ischar },
+ { "isnextchar", nodelib_direct_isnextchar },
+ { "isprevchar", nodelib_direct_isprevchar },
+ { "isnextglyph", nodelib_direct_isnextglyph },
+ { "isprevglyph", nodelib_direct_isprevglyph },
+ { "isdirect", nodelib_direct_isdirect },
+ { "isglyph", nodelib_direct_isglyph },
+ { "isnode", nodelib_direct_isnode },
+ { "isvalid", nodelib_direct_isvalid },
+ { "iszeroglue", nodelib_direct_iszeroglue },
+ { "isnext", nodelib_direct_isnext },
+ { "isprev", nodelib_direct_isprev },
+ { "isboth", nodelib_direct_isboth },
+ { "kerning", nodelib_direct_kerning },
+ { "lastnode", nodelib_direct_lastnode },
+ { "length", nodelib_direct_length },
+ { "ligaturing", nodelib_direct_ligaturing },
+ { "makeextensible", nodelib_direct_makeextensible },
+ { "mlisttohlist", nodelib_direct_mlisttohlist },
+ { "naturalwidth", nodelib_direct_naturalwidth },
+ { "naturalhsize", nodelib_direct_naturalhsize },
+ { "new", nodelib_direct_new },
+ { "newtextglyph", nodelib_direct_newtextglyph },
+ { "newmathglyph", nodelib_direct_newmathglyph },
+ { "protectglyph", nodelib_direct_protectglyph },
+ { "protectglyphs", nodelib_direct_protectglyphs },
+ { "protrusionskippable", nodelib_direct_protrusionskipable },
+ { "rangedimensions", nodelib_direct_rangedimensions }, /* maybe get... */
+ { "getglyphdimensions", nodelib_direct_getglyphdimensions },
+ { "getkerndimension", nodelib_direct_getkerndimension },
+ { "patchattributes", nodelib_direct_patchattributes },
+ { "remove", nodelib_direct_remove },
+ { "repack", nodelib_direct_repack },
+ { "freeze", nodelib_direct_freeze },
+ { "setattribute", nodelib_direct_setattribute },
+ { "setattributes", nodelib_direct_setattributes },
+ { "setinputfields", nodelib_direct_setinputfields },
+ { "setattributelist", nodelib_direct_setattributelist },
+ { "setboth", nodelib_direct_setboth },
+ { "setbottom", nodelib_direct_setbottom },
+ { "setbox", nodelib_direct_setbox },
+ { "setchar", nodelib_direct_setchar },
+ { "setchardict", nodelib_direct_setchardict },
+ { "setchoice", nodelib_direct_setchoice },
+ { "setclass", nodelib_direct_setclass },
+ { "setstate", nodelib_direct_setstate },
+ { "setscript", nodelib_direct_setscript },
+ { "setdata", nodelib_direct_setdata },
+ { "setleftdelimiter", nodelib_direct_setleftdelimiter },
+ { "setrightdelimiter", nodelib_direct_setrightdelimiter },
+ { "setdelimiter", nodelib_direct_setdelimiter },
+ { "setdenominator", nodelib_direct_setdenominator },
+ { "setdegree", nodelib_direct_setdegree },
+ { "setdepth", nodelib_direct_setdepth },
+ { "setdirection", nodelib_direct_setdirection },
+ { "setdisc", nodelib_direct_setdisc },
+ { "setdiscpart", nodelib_direct_setdiscpart },
+ { "setexpansion", nodelib_direct_setexpansion },
+ { "setfam", nodelib_direct_setfam },
+ { "setfield", nodelib_direct_setfield },
+ { "setfont", nodelib_direct_setfont },
+ { "setglue", nodelib_direct_setglue },
+ { "setglyphdata", nodelib_direct_setglyphdata },
+ { "setheight", nodelib_direct_setheight },
+ { "setindex", nodelib_direct_setindex },
+ { "setkern", nodelib_direct_setkern },
+ { "setlanguage", nodelib_direct_setlanguage },
+ { "setleader", nodelib_direct_setleader },
+ { "setlink", nodelib_direct_setlink },
+ { "setlist", nodelib_direct_setlist },
+ { "setnext", nodelib_direct_setnext },
+ { "setnucleus", nodelib_direct_setnucleus },
+ { "setnumerator", nodelib_direct_setnumerator },
+ { "setoffsets", nodelib_direct_setoffsets },
+ { "addxoffset", nodelib_direct_addxoffset },
+ { "addyoffset", nodelib_direct_addyoffset },
+ { "addmargins", nodelib_direct_addmargins },
+ { "addxymargins", nodelib_direct_addxymargins },
+ { "setscales", nodelib_direct_setscales },
+ { "setanchors", nodelib_direct_setanchors },
+ { "setorientation", nodelib_direct_setorientation },
+ { "setoptions", nodelib_direct_setoptions },
+ { "setpenalty", nodelib_direct_setpenalty },
+ { "setpost", nodelib_direct_setpost },
+ { "setpre", nodelib_direct_setpre },
+ { "setprev", nodelib_direct_setprev },
+ { "setproperty", nodelib_direct_setproperty },
+ { "setreplace", nodelib_direct_setreplace },
+ { "setshift", nodelib_direct_setshift },
+ { "setsplit", nodelib_direct_setsplit },
+ { "setsub", nodelib_direct_setsub },
+ { "setsubpre", nodelib_direct_setsubpre },
+ { "setsubtype", nodelib_direct_setsubtype },
+ { "setsup", nodelib_direct_setsup },
+ { "setsuppre", nodelib_direct_setsuppre },
+ { "setprime" , nodelib_direct_setprime },
+ { "settotal" , nodelib_direct_settotal },
+ { "settop" , nodelib_direct_settop },
+ { "setwhd", nodelib_direct_setwhd },
+ { "setwidth", nodelib_direct_setwidth },
+ { "slide", nodelib_direct_slide },
+ { "startofpar", nodelib_direct_startofpar },
+ { "tail", nodelib_direct_tail },
+ { "todirect", nodelib_direct_todirect },
+ { "tonode", nodelib_direct_tonode },
+ { "tostring", nodelib_direct_tostring },
+ { "tovaliddirect", nodelib_direct_tovaliddirect },
+ { "traverse", nodelib_direct_traverse },
+ { "traversechar", nodelib_direct_traversechar },
+ { "traverseglyph", nodelib_direct_traverseglyph },
+ { "traverseid", nodelib_direct_traverseid },
+ { "traverselist", nodelib_direct_traverselist },
+ { "traversecontent", nodelib_direct_traversecontent },
+ { "traverseleader", nodelib_direct_traverseleader },
+ { "unprotectglyph", nodelib_direct_unprotectglyph },
+ { "unprotectglyphs", nodelib_direct_unprotectglyphs },
+ { "unsetattribute", nodelib_direct_unsetattribute },
+ { "unsetattributes", nodelib_direct_unsetattributes },
+ { "usedlist", nodelib_direct_usedlist },
+ { "usesfont", nodelib_direct_usesfont },
+ { "vpack", nodelib_direct_vpack },
+ { "flattenleaders", nodelib_direct_flattenleaders },
+ { "write", nodelib_direct_write },
+ /* { "appendtocurrentlist", nodelib_direct_appendtocurrentlist }, */ /* beware, we conflict in ctx */
+ { "verticalbreak", nodelib_direct_verticalbreak },
+ { "reverse", nodelib_direct_reverse },
+ { "exchange", nodelib_direct_exchange },
+ { "migrate", nodelib_direct_migrate },
+ { "getspeciallist", nodelib_direct_getspeciallist },
+ { "setspeciallist", nodelib_direct_setspeciallist },
+ { "isspeciallist", nodelib_direct_isspeciallist },
+ { "getusedattributes", nodelib_direct_getusedattributes },
+ /* dual node and direct */
+ { "type", nodelib_hybrid_type },
+ { "types", nodelib_shared_types },
+ { "fields", nodelib_shared_fields },
+ { "subtypes", nodelib_shared_subtypes },
+ { "values", nodelib_shared_values },
+ { "id", nodelib_shared_id },
+ { "show", nodelib_direct_show },
+ { "gluetostring", nodelib_hybrid_gluetostring },
+ { "serialized", nodelib_direct_serialized },
+ { "getcachestate", nodelib_shared_getcachestate },
+ { NULL, NULL },
+};
+
+/* node.* */
+
+static const struct luaL_Reg nodelib_function_list[] = {
+ /* the bare minimum for reasonable performance */
+ { "copy", nodelib_userdata_copy },
+ { "copylist", nodelib_userdata_copylist },
+ { "new", nodelib_userdata_new },
+ { "flushlist", nodelib_userdata_flushlist },
+ { "flushnode", nodelib_userdata_flushnode },
+ { "free", nodelib_userdata_free },
+ { "currentattributes", nodelib_userdata_currentattributes },
+ { "hasattribute", nodelib_userdata_hasattribute },
+ { "getattribute", nodelib_userdata_getattribute },
+ { "setattribute", nodelib_userdata_setattribute },
+ { "unsetattribute", nodelib_userdata_unsetattribute },
+ { "getpropertiestable", nodelib_userdata_getpropertiestable },
+ { "getproperty", nodelib_userdata_getproperty },
+ { "setproperty", nodelib_userdata_setproperty },
+ { "getfield", nodelib_userdata_getfield },
+ { "setfield", nodelib_userdata_setfield },
+ { "hasfield", nodelib_userdata_hasfield },
+ { "tail", nodelib_userdata_tail },
+ { "write", nodelib_userdata_write },
+ /* { "appendtocurrentlist", nodelib_userdata_append }, */ /* beware, we conflict in ctx */
+ { "isnode", nodelib_userdata_isnode },
+ { "tostring", nodelib_userdata_tostring },
+ { "usedlist", nodelib_userdata_usedlist },
+ { "inuse", nodelib_userdata_inuse },
+ { "instock", nodelib_userdata_instock },
+ { "traverse", nodelib_userdata_traverse },
+ { "traverseid", nodelib_userdata_traverse_id },
+ { "insertafter", nodelib_userdata_insertafter },
+ { "insertbefore", nodelib_userdata_insertbefore },
+ { "remove", nodelib_userdata_remove },
+ /* shared between userdata and direct */
+ { "type", nodelib_hybrid_type },
+ { "types", nodelib_shared_types },
+ { "fields", nodelib_shared_fields },
+ { "subtypes", nodelib_shared_subtypes },
+ { "values", nodelib_shared_values },
+ { "id", nodelib_shared_id },
+ { "show", nodelib_userdata_show },
+ { "gluetostring", nodelib_hybrid_gluetostring },
+ { "serialized", nodelib_userdata_serialized },
+ { "getcachestate", nodelib_shared_getcachestate },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg nodelib_metatable[] = {
+ { "__index", nodelib_userdata_index },
+ { "__newindex", nodelib_userdata_newindex },
+ { "__tostring", nodelib_userdata_tostring },
+ { "__eq", nodelib_userdata_equal },
+ { NULL, NULL },
+};
+
+int luaopen_node(lua_State *L)
+{
+ /*tex the main metatable of node userdata */
+ luaL_newmetatable(L, NODE_METATABLE_INSTANCE);
+ /* node.* */
+ luaL_setfuncs(L, nodelib_metatable, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, nodelib_function_list, 0);
+ /* node.direct */
+ lua_pushstring(L, lua_key(direct));
+ lua_newtable(L);
+ luaL_setfuncs(L, nodelib_direct_function_list, 0);
+ lua_rawset(L, -3);
+ return 1;
+}
+
+void lmt_node_list_to_lua(lua_State *L, halfword n)
+{
+ lmt_push_node_fast(L, n);
+}
+
+halfword lmt_node_list_from_lua(lua_State *L, int n)
+{
+ if (lua_isnil(L, n)) {
+ return null;
+ } else {
+ halfword list = lmt_check_isnode(L, n);
+ return list ? list : null;
+ }
+}
+
+/*tex
+ Here come the callbacks that deal with node lists. Some are called in multiple locations and
+ then get additional information passed concerning the whereabouts.
+
+ The begin paragraph callback first got |cmd| and |chr| but in the end it made more sense to
+ do it like the rest and pass a string. There is no need for more granularity.
+ */
+
+void lmt_begin_paragraph_callback(
+ int invmode,
+ int *indented,
+ int context
+)
+{
+ int callback_id = lmt_callback_defined(begin_paragraph_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lua_pushboolean(L, invmode);
+ lua_pushboolean(L, *indented);
+ lmt_push_par_begin(L, context);
+ i = lmt_callback_call(L, 3, 1, top);
+ /* done */
+ if (i) {
+ lmt_callback_error(L, top, i);
+ }
+ else {
+ *indented = lua_toboolean(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+void lmt_paragraph_context_callback(
+ int context,
+ int *ignore
+)
+{
+ int callback_id = lmt_callback_defined(paragraph_context_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_push_par_context(L, context);
+ i = lmt_callback_call(L, 1, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ }
+ else {
+ *ignore = lua_toboolean(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+void lmt_page_filter_callback(
+ int context,
+ halfword boundary
+)
+{
+ int callback_id = lmt_callback_defined(buildpage_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_push_page_context(L, context);
+ lua_push_halfword(L, boundary);
+ i = lmt_callback_call(L, 2, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+}
+
+/*tex This one gets |tail| and optionally gets back |head|. */
+
+void lmt_append_line_filter_callback(
+ halfword context,
+ halfword index /* class */
+)
+{
+ if (cur_list.tail) {
+ int callback_id = lmt_callback_defined(append_line_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, node_next(cur_list.head));
+ lmt_node_list_to_lua(L, cur_list.tail);
+ lmt_push_append_line_context(L, context);
+ lua_push_halfword(L, index);
+ i = lmt_callback_call(L, 4, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ int a = lmt_node_list_from_lua(L, -1);
+ node_next(cur_list.head) = a;
+ cur_list.tail = tex_tail_of_node_list(a);
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+}
+
+/*tex
+
+ Eventually the optional fixing of lists will go away because we assume that proper double linked
+ lists get returned. Keep in mind that \TEX\ itself never looks back (we didn't change that bit,
+ at least not until now) so it's only callbacks that suffer from bad |prev| fields.
+
+*/
+
+void lmt_node_filter_callback(
+ int filterid,
+ int extrainfo,
+ halfword head_node,
+ halfword *tail_node
+)
+{
+ if (head_node) {
+ /*tex We start after head (temp). */
+ halfword start_node = node_next(head_node);
+ if (start_node) {
+ int callback_id = lmt_callback_defined(filterid);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ /*tex We make sure we have no prev */
+ node_prev(start_node) = null;
+ /*tex the action */
+ lmt_node_list_to_lua(L, start_node);
+ lmt_push_group_code(L, extrainfo);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ /*tex append to old head */
+ halfword start_done = lmt_node_list_from_lua(L, -1);
+ tex_try_couple_nodes(head_node, start_done);
+ /*tex redundant as we set top anyway */
+ lua_pop(L, 2);
+ /*tex find tail in order to update tail */
+ start_node = node_next(head_node);
+ if (start_node) {
+ /*tex maybe just always slide (harmless and fast) */
+ halfword last_node = node_next(start_node);
+ while (last_node) {
+ start_node = last_node;
+ last_node = node_next(start_node);
+ }
+ /*tex we're at the end now */
+ *tail_node = start_node;
+ } else {
+ /*tex we're already at the end */
+ *tail_node = head_node;
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+/*tex
+ Maybe this one will get extended a bit in due time.
+*/
+
+int lmt_linebreak_callback(
+ int is_broken,
+ halfword head_node,
+ halfword *new_head
+)
+{
+ if (head_node) {
+ halfword start_node = node_next(head_node);
+ if (start_node) {
+ int callback_id = lmt_callback_defined(linebreak_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (callback_id > 0 && lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ int ret = 0;
+ node_prev(start_node) = null;
+ lmt_node_list_to_lua(L, start_node);
+ lua_pushboolean(L, is_broken);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ halfword *p = lua_touserdata(L, -1);
+ if (p) {
+ int a = lmt_node_list_from_lua(L, -1);
+ tex_try_couple_nodes(*new_head, a);
+ ret = 1;
+ }
+ lmt_callback_wrapup(L, top);
+ }
+ return ret;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void lmt_alignment_callback(
+ halfword head_node,
+ halfword context,
+ halfword attr_list,
+ halfword preamble
+)
+{
+ if (head_node || preamble) {
+ int callback_id = lmt_callback_defined(alignment_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_alignment_context(L, context);
+ lmt_node_list_to_lua(L, attr_list);
+ lmt_node_list_to_lua(L, preamble);
+ i = lmt_callback_call(L, 4, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return;
+}
+
+void lmt_local_box_callback(
+ halfword linebox,
+ halfword leftbox,
+ halfword rightbox,
+ halfword middlebox,
+ halfword linenumber,
+ scaled leftskip,
+ scaled rightskip,
+ scaled lefthang,
+ scaled righthang,
+ scaled indentation,
+ scaled parinitleftskip,
+ scaled parinitrightskip,
+ scaled parfillleftskip,
+ scaled parfillrightskip,
+ scaled overshoot
+)
+{
+ if (linebox) {
+ int callback_id = lmt_callback_defined(local_box_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, linebox);
+ lmt_node_list_to_lua(L, leftbox);
+ lmt_node_list_to_lua(L, rightbox);
+ lmt_node_list_to_lua(L, middlebox);
+ lua_pushinteger(L, linenumber);
+ lua_pushinteger(L, leftskip);
+ lua_pushinteger(L, rightskip);
+ lua_pushinteger(L, lefthang);
+ lua_pushinteger(L, righthang);
+ lua_pushinteger(L, indentation);
+ lua_pushinteger(L, parinitleftskip);
+ lua_pushinteger(L, parinitrightskip);
+ lua_pushinteger(L, parfillleftskip);
+ lua_pushinteger(L, parfillrightskip);
+ lua_pushinteger(L, overshoot);
+ i = lmt_callback_call(L, 15, 0, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ /* todo: check if these boxes are still okay (defined) */
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+}
+
+/*tex
+ This one is a bit different from the \LUATEX\ variant. The direction parameter has been dropped
+ and prevdepth correction can be controlled.
+*/
+
+int lmt_append_to_vlist_callback(
+ halfword box,
+ int location,
+ halfword prev_depth,
+ halfword *result,
+ int *next_depth,
+ int *prev_set,
+ int *check_depth
+)
+{
+ if (box) {
+ int callback_id = lmt_callback_defined(append_to_vlist_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, box);
+ lua_push_key_by_index(location);
+ lua_pushinteger(L, (int) prev_depth);
+ i = lmt_callback_call(L, 3, 3, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ switch (lua_type(L, -3)) {
+ case LUA_TUSERDATA:
+ *result = lmt_check_isnode(L, -3);
+ break;
+ case LUA_TNIL:
+ *result = null;
+ break;
+ default:
+ tex_normal_warning("append to vlist callback", "node or nil expected");
+ break;
+ }
+ if (lua_type(L, -2) == LUA_TNUMBER) {
+ *next_depth = lmt_roundnumber(L, -2);
+ *prev_set = 1;
+ }
+ if (*result && lua_type(L, -1) == LUA_TBOOLEAN) {
+ *check_depth = lua_toboolean(L, -1);
+ }
+ lmt_callback_wrapup(L, top);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*tex
+ Here we keep the directions although they play no real role in the
+ packing process.
+ */
+
+halfword lmt_hpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ int extrainfo,
+ int pack_direction,
+ halfword attr
+)
+{
+ if (head_node) {
+ int callback_id = lmt_callback_defined(hpack_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ node_prev(head_node) = null;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_group_code(L, extrainfo);
+ lua_pushinteger(L, size);
+ lmt_push_pack_type(L, pack_type);
+ if (pack_direction >= 0) {
+ lua_pushinteger(L, pack_direction);
+ } else {
+ lua_pushnil(L);
+ }
+ /* maybe: (attr && attr != cache_disabled) */
+ lmt_node_list_to_lua(L, attr);
+ i = lmt_callback_call(L, 6, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ head_node = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return head_node;
+}
+
+extern halfword lmt_packed_vbox_filter_callback(
+ halfword box,
+ int extrainfo
+)
+{
+ if (box) {
+ int callback_id = lmt_callback_defined(packed_vbox_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ lmt_node_list_to_lua(L, box);
+ lmt_push_group_code(L, extrainfo);
+ i = lmt_callback_call(L, 2, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ box = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return box;
+}
+
+halfword lmt_vpack_filter_callback(
+ halfword head_node,
+ scaled size,
+ int pack_type,
+ scaled maxd,
+ int extrainfo,
+ int pack_direction,
+ halfword attr
+)
+{
+ if (head_node) {
+ int callback_id = lmt_callback_defined(extrainfo == output_group ? pre_output_filter_callback : vpack_filter_callback);
+ if (callback_id > 0) {
+ lua_State *L = lmt_lua_state.lua_instance;
+ int top = 0;
+ if (lmt_callback_okay(L, callback_id, &top)) {
+ int i;
+ node_prev(head_node) = null;
+ lmt_node_list_to_lua(L, head_node);
+ lmt_push_group_code(L, extrainfo);
+ lua_pushinteger(L, size);
+ lmt_push_pack_type(L, pack_type);
+ lua_pushinteger(L, maxd);
+ if (pack_direction >= 0) {
+ lua_pushinteger(L, pack_direction);
+ } else {
+ lua_pushnil(L);
+ }
+ lmt_node_list_to_lua(L, attr);
+ i = lmt_callback_call(L, 7, 1, top);
+ if (i) {
+ lmt_callback_error(L, top, i);
+ } else {
+ head_node = lmt_node_list_from_lua(L, -1);
+ lmt_callback_wrapup(L, top);
+ }
+ }
+ }
+ }
+ return head_node;
+}