summaryrefslogtreecommitdiff
path: root/source/luametatex/source/tex/texnodes.h
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/tex/texnodes.h')
-rw-r--r--source/luametatex/source/tex/texnodes.h2728
1 files changed, 2728 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texnodes.h b/source/luametatex/source/tex/texnodes.h
new file mode 100644
index 000000000..f0d20e1e9
--- /dev/null
+++ b/source/luametatex/source/tex/texnodes.h
@@ -0,0 +1,2728 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+# ifndef LMT_TEXNODES_H
+# define LMT_TEXNODES_H
+
+/*tex
+
+ We can probably ditch |volatile| so that the compiler can optimize access a bit better. We
+ only need to make sure that we create nodes before we use their pointers. So, beware: a
+ newnode has to go via an intermediate variable because the |varmem| array can have been be
+ reallocated. I need to (re)check all cases! In case of a copy we use a intermediate volatile
+ variable.
+
+ Anyway, we now have only a few |quarterwords| in use, most noticeably the type and subtype.
+ Eventually I might go for a consistent
+
+ type subtype
+ prev next
+ attribute data
+ etc
+
+ model. Or maybe even just a flat list, no need for memoryword, but just all halfwords. However,
+ it will demand all kind of tiny adaptations and we don't gain much. We'd also loose some charm
+ of traditional \TEX. Also, we now have a double glue related field and that would then become
+ a float. So, not now.
+
+ There are a few more node types than in standard \TEX, but less than we have in e.g.\ \PDFTEX\
+ or stock \LUATEX. For instance margin nodes are now just kern nodes, some whatits are first
+ class nodes and we have only one generic whatsit left. We also have more subtypes which makes
+ a more detailed tracking of where nodes come from possible. Other nodes, like the |inserting|
+ and |split_up| nodes are ot both |inserting| but with a subtype because the register index is
+ no longer the subtype.
+
+ Not all nodes can end up in a node list. Some are used for housekeeping (stack, expressions,
+ conditional nesting, etc.) or show up in the process of breaking paragraphs into lines. When
+ we talk of nodes with users in the perspective of \TEX\ we normally refer to the ones in
+ horizontal and vertical lists or math lists, not to those more obscure housekeeping nodes. It
+ just happens that they share the same memory model and management.
+
+ A complication is that some nodes have pointers that themselves point to a (often smaller)
+ node but use the same accessors. This means that (1) their layout should be the same with
+ respect to the pointer, which happens with span nodes or (2) that there is some offset in play,
+ which happens with ins pointers and break nodes that are embedded in a disc node.
+
+ Now that we no longer have variable nodes, we can consider a different allocation model, like
+ a chain of malloced nodes but on the other hand storing them might be more work. We also cannot
+ longer share the accessors so again more work is needed. But ... maybe attributes might end up
+ as allocated lists some day, but that also demands storage. The current memory management is
+ very efficient and we don't gain anything with redoing that, apart maybe from nodes becoming
+ structs. Even then we will have an array of pointers instead of what we have now, but without
+ the jumps by side in the indices. So, given the constraints of offsets and overlap it makes no
+ sense to waste time on this.
+
+ Instead of |var_mem| we use |nodes| and related names. This better complements the additional
+ variables that we have for dynamic management. Some more names have been changed (also in order
+ to avoid side effect in syntax highlighting). Too common names also result in too many matches
+ when searching the source tree.
+
+ Soo, eventually most fields now have the type of the node in their name, which makes it more
+ clear what they are. As mentioned, it makes the syntax highlighted source look better as some
+ generic names are used elsewhere too. Another reason is that we have more fields in nodes and
+ when browsing the source it helps to know that a |width| is actually the |glue_amount| which
+ when we go down is actually a height anyway. It also makes it possible at some point to make
+ some nodes smaller when we don't need these \quote {shared by name} fields. We also need this
+ transition in order to get better interfacing to the \LUA\ end, one reason being that we need
+ to distinguish between fields that overlap (as in lists, unset nodes and and alignment
+ records).
+
+ Todo: all subtype related constants will become |_subtype| so that also means a couple more
+ _code ones for commands. It's all about consistency but that will happen stepwise. A typical
+ rainy day with some newly acquired music in background kind of activity:
+
+ - discretionary
+ - adjust
+ - noad
+ - fence
+ - radical
+ - boundary
+
+*/
+
+typedef enum node_types {
+ hlist_node,
+ vlist_node,
+ rule_node,
+ insert_node,
+ mark_node,
+ adjust_node,
+ boundary_node,
+ disc_node,
+ whatsit_node,
+ /*tex The last_preceding_break_node: */
+ par_node,
+ dir_node,
+ /*tex The last_non_discardable_node: */
+ math_node,
+ glue_node,
+ kern_node,
+ penalty_node,
+ style_node,
+ choice_node,
+ parameter_node,
+ simple_noad,
+ radical_noad,
+ fraction_noad,
+ accent_noad,
+ fence_noad,
+ math_char_node,
+ math_text_char_node,
+ sub_box_node,
+ sub_mlist_node,
+ delimiter_node,
+ glyph_node,
+ /*tex This was the last node with attributes, except unset nodes. */
+ unset_node,
+ specification_node,
+ align_record_node,
+ attribute_node,
+ glue_spec_node,
+ temp_node,
+ split_node,
+ /*tex The next set of nodes is invisible from the \LUA\ (but nesting nodes can show up). */
+ expression_node,
+ math_spec_node,
+ font_spec_node,
+ nesting_node,
+ span_node,
+ align_stack_node,
+ noad_state_node,
+ if_node,
+ unhyphenated_node, /*tex These are both active nodes. */
+ hyphenated_node, /*tex These are both active nodes. */
+ delta_node,
+ passive_node,
+} node_types;
+
+# define max_chain_size 32
+
+# define unknown_node_type -1
+# define unknown_node_subtype -1
+
+/* Todo: [type] [subtype|size] [index] -> nodes : advantage is no holes in node id's */
+
+typedef struct node_memory_state_info {
+ memoryword *nodes;
+ // memoryword *volatile nodes;
+ char *nodesizes;
+ halfword free_chain[max_chain_size];
+ memory_data nodes_data;
+ memory_data extra_data;
+ int reserved; /*tex There are some predefined nodes. */
+ int padding;
+ int node_properties_id;
+ int lua_properties_level;
+ halfword attribute_cache;
+ halfword max_used_attribute;
+ int node_properties_table_size;
+} node_memory_state_info;
+
+extern node_memory_state_info lmt_node_memory_state;
+
+typedef enum field_types {
+ nil_field,
+ integer_field,
+ dimension_field,
+ glue_field,
+ number_field,
+ string_field,
+ boolean_field,
+ function_field,
+ node_field,
+ node_list_field,
+ token_field,
+ token_list_field,
+ attribute_field,
+} field_types;
+
+extern halfword tex_get_node (int size);
+extern void tex_free_node (halfword p, int size);
+extern void tex_dump_node_mem (dumpstream f);
+extern void tex_undump_node_mem (dumpstream f);
+extern void tex_initialize_node_mem (void);
+extern void tex_initialize_nodes (void);
+
+extern void lmt_nodelib_initialize (void); /* name ? */
+
+/*tex
+
+ Most fields are integers (halfwords) that get aliased to |vinfo| and |vlink| for traditional
+ reasons. The |vlink| name is actually representing a next pointer. Only the type and subtype
+ remain quarterwords, the rest are just halfwords which wastes space for directions,
+ orientation, glue orders and glue signs but so be it.
+
+ A memory word has two 32 bit integers so 8 bytes. A glueratio is a double which is 8 bytes so
+ there we waste some space. There is actually no need now to pack (node) data in pairs so maybe
+ some day I'll change that. When we make glue ration a float again we can go flat (and with most
+ node fields now being fully qualified that is easier).
+
+ The first memoryword contains the |type| and |subtype| that are both 16 bit integers (unsigned)
+ as well as the |vlink| (next) pointer. After that comes the word that keeps the |attr| and
+ |alink| (prev) fields. Instead of the link names we use more meaningful ones. The |next|, |prev|
+ and |attr| fields all are halfwords representing a node index.
+
+ The |node_size| field is used in managing freed nodes (mostly as a check) and it overwrites the
+ |type| and |subtype| fields. Actually we could just use the type or subtype but as the size is
+ small but on the other hand using an int here makes sense.
+
+ half0 | quart0 quart1 | vinfo | size | type subtype
+ half1 | | vlink
+
+ The |tlink| and |rlink| fields are used in disc nodes as tail and replace pointers (again
+ halfwords). We no longer need |rlink| as it's equivalent to |alink| (the prev pointer). The
+ |tlink| fields is used for links to the tail of a list. These indirect macros are somewhat
+ complicating matters. Again these have been renamed.
+
+ We used to have |alink(a)| being |vlink(a,1)| but that has (after a few years) been replaced by
+ |node_prev| because is cleaner. Keep in mind that in \LUATEX\ we use double linked node lists. So,
+ we now only have some \quote {hard coded} pointers to the memory array in this file, not in the
+ files that use the node fields. However, for the next two paragraphs to be true, I need to find
+ a solution for the insert_ptr first because that is an index.
+
+ Now, a logical question is: should we stick to the link and info model for nodes? One reason
+ is that we share the model with tokens. A less relevant reason is that the glue is stored in 8
+ bytes but that can be reverted to 4 bytes if needed. So, indeed at some point we might see a 32
+ bit wide array show up here as we're now more or less prepared for that. It will bump the direct
+ node numbers but that should work out okay. So, in the end, after stepwise abstraction we now
+ have field definitions that use a base and offset e.g. |vlink(a,3)| instead of |vlink(a+3)|.
+ Also, we have many more fields and using meaningful names quickly started to make sense.
+
+ Once all is stable I will play with |var_mem| being an array of pointers and |malloc| the
+ smaller memoryword arrays (per node). This might lead to (not always) smaller memory footprint:
+ we have one pointer per node (but only that array gets preallocated) but we need less memory in
+ total, unless we use many nodes. Anyway, we keep the indirect model (which might add overhead,
+ but that can be compensated by \CPU\ caches) because using a numeric node pointer is more
+ efficient and quite handy. If we would go completely struct the source would change so much that
+ we loose the charm of \TEX\ and documentation and there is no gain in it. Also, using halfword
+ indices (but then to pointers) for nodes has the huge advantage that it is fast in \LUA\ (always
+ a bottleneck) and these node indices can (and have to) be stored in tokens. One nice side effect
+ would be that we have node indices in a sequence (without the current jumps due to node size
+ offset, which in turn gives more room for nodes references in tokens).
+
+ In spite of all extensions we hope the spirit of how \TEX\ does it is still very visible.
+
+*/
+
+# define mvalue(a,b) lmt_node_memory_state.nodes[a+b].P
+# define lvalue(a,b) lmt_node_memory_state.nodes[a+b].L
+# define dvalue(a,b) lmt_node_memory_state.nodes[a+b].D
+
+# define vinfo(a,b) lmt_node_memory_state.nodes[a+b].half0
+# define vlink(a,b) lmt_node_memory_state.nodes[a+b].half1
+
+# define vinfo0(a,b) lmt_node_memory_state.nodes[a+b].quart00
+# define vinfo1(a,b) lmt_node_memory_state.nodes[a+b].quart01
+# define vlink0(a,b) lmt_node_memory_state.nodes[a+b].quart10
+# define vlink1(a,b) lmt_node_memory_state.nodes[a+b].quart11
+
+# define vinfo00(a,b) lmt_node_memory_state.nodes[a+b].single00
+# define vinfo01(a,b) lmt_node_memory_state.nodes[a+b].single01
+# define vinfo02(a,b) lmt_node_memory_state.nodes[a+b].single02
+# define vinfo03(a,b) lmt_node_memory_state.nodes[a+b].single03
+# define vlink00(a,b) lmt_node_memory_state.nodes[a+b].single10
+# define vlink01(a,b) lmt_node_memory_state.nodes[a+b].single11
+# define vlink02(a,b) lmt_node_memory_state.nodes[a+b].single12
+# define vlink03(a,b) lmt_node_memory_state.nodes[a+b].single13
+
+/*tex
+ We have some shared field names. Some day the subtypes will get meaningful names dependent on
+ the node type, if only because some already have. We used to have
+
+ \starttyping
+ # define type(a) vinfo0(a,0)
+ # define subtype(a) vinfo1(a,0)
+ # define node_size(a) vinfo(a,0)
+ \stoptyping
+
+ but we dropped the size mechanism and made most field shortcuts verbose in order to be able to
+ use variable names with the same name combined with proper syntax highlighting etc. It also
+ gives less noise when we search in the whole source tree. More later.
+*/
+
+# define node_type(a) vinfo0(a,0)
+# define node_subtype(a) vinfo1(a,0)
+
+# define node_next(a) vlink(a,0)
+# define node_prev(a) vlink(a,1)
+# define node_attr(a) vinfo(a,1)
+
+# define node_head(a) vlink(a,0) /*tex the head |hlink(a)| aka |vlink(a)| of a disc sublist */
+# define node_tail(a) vinfo(a,1) /*tex the tail |tlink(a)| aka |vinfo(a)|, overlaps with |node_attr()| */
+
+/*tex
+
+ The dimension fields shared their locations which made for sometimes more compact code but
+ in the end the number of placxes where it really saved code were limited. Also, compilers will
+ do their job and deal with common code. So, these are now replaced by more meaningful names:
+
+ \starttyping
+ # define width(a) vlink(a,2)
+ # define depth(a) vlink(a,3)
+ # define height(a) vlink(a,4)
+ \stoptyping
+
+ Inserts use a trick. The insert pointers directly point into a node at the place where the list
+ starts which is why |list_ptr| has to overlap with |node_next|! I have looked into changign this
+ but it doesn't pay off and it's best to stay close to the original. A side effect is that some
+ fields in insert nodes are sort of impossible (for now).
+
+ \starttyping
+ # define box_list_ptr(a) vlink(a,5) // We need to use the same field as |node_next|.
+ # define insert_list(a) (a + 5) // This kind of makes a virtual node: start at list.
+ \stoptyping
+
+ Beware of the fact that for instance alignments use some fields for other purposes, like:
+ |u_part(a)|, |v_part(a)|, |span_ptr(a)|, etc. and assume the rest of the fields to overlap
+ with list nodes. So, we cannot simply reshuffle positions!
+
+ In the original \TEX\ source (and also in \LUATEX) there are a couple of offsets used. Most
+ noticeably is the |list_offset| but in 2.0618 the related trickery was replaced by using
+ |list_ptr| and using the fact that we have a doubel linked list. The four fields are in
+ successive memory words and that means that we can use |node_next| in a field pointed to
+ by |list_offset| (because actually we then have the list pointer!). This makes for simple
+ loops in original \TEX. The dimension offsets are used to set fields in boxed but we already
+ abstracted that to proper field names; these were for instance used in alignment nodes that
+ have mostly the same properties as a box node.
+
+ \starttyping
+ # define width_offset 2
+ # define depth_offset 3
+ # define height_offset 4
+ # define list_offset 5
+ \stoptyping
+
+ These abstractions mean that we now have nodes, fields and offsets all abstracted in such a way
+ that all definitions and trickery in in this file. Of course I could have messed up.
+
+*/
+
+/*tex
+
+ Syntex supports demands some extra fields in nodes that makes it possible to output location as
+ well as file/line information for viewer-editor synchronization. The ideas is quite okay but
+ unfortunately the implementation of the library is rather bound to the way e.g. \LATEX\ typesets
+ documents. Synctex has always been problematic when it comes to \CONTEXT. There is for instance
+ no control over filenames and discussions around some limitations (and possible features) in the
+ \PDFTEX\ and early \LUATEX\ times never resulted in fixing that (setting filenames, adding some
+ additional synchronization points, etc). All that was supposed to happen deep down in the library
+ and was not considered to be dealt with by a macro package. For instance multiple loading of the
+ same file (metapost runs or smaple files) was a problem as was the need to block access to files
+ in tds (like styles). We also needed binding to for instance elements in an \XML\ file where line
+ numbers are sort of special and out of sync with inclusion. I guess we were ahead of the pack
+ because after nearly two decades of \LUATEX\ there is some discussion about this.
+
+ Anyway, for the reasons mentioned \LUATEX\ offers some setters that overload the engine ones and
+ that permits \CONTEXT\ to implement its own variant. However, in \LUAMETATEX\ setting tags and
+ lines from \LUA\ is now the only way to support \SYNCTEX\ because the library is absent: we just
+ have some extra fields in some nodes. In \LUAMETATEX\ only glyph and list nodes have these fields
+ as it makes no sense to have them elsewhere: macro packages can add glue and kerns and rules and
+ \unknown\ all over the place and adding file state info there only makes things confusing and
+ working less well. This is what the mode parameter can handle in \LUATEX\ and in \LUAMETATEX\ it
+ only supports the modes 1 and 3.
+
+ As a side note: the fact that a viewer needs to embed the library is also a limitation. Calling
+ out to an external program that analyzes the file and gives back the filename and line is more
+ flexible and robust. Because we have such an analyzer in \MKIV\ it was no big deal to add a few
+ lines so that the \TEX shop environment could use that script/method (bidirectional); hopefully
+ other viewers and editors will follow.
+
+ So, compared to \LUATEX\ less nodes have the extra fields (which saves memory) and therefore
+ less has to be set. Because there is no library at all, writing a synctex file is up to some
+ additional \LUA\ code, but that was already the case in \MKIV\ anyway. We might at some point
+ change the field names to \quote {file} and \quote {line} and remove interface options that
+ have no use any more. We also moved to a more generic naming of (input related) fields.
+
+*/
+
+/*
+
+ Temporary nodes are really special head node pointers that only need links. They ensure that
+ there is at least one node in a list.
+
+*/
+
+# define temp_node_size 2
+
+/*tex
+
+ In \LUATEX\ we have attribute list nodes and attribute nodes that are (anyway) of the same
+ size. In the end I decided to combine them into one node with a subtype. That also helps
+ diagnose issues. It is one of the few nodes now that has fields depending on the subtype
+ but these nodes are not really user ones anyway.
+
+*/
+
+# define attribute_node_size 2
+# define attribute_unset(a) vinfo(a,1)
+# define attribute_index(a) vinfo(a,1) /*tex actually we need half of this */
+# define attribute_count(a) vlink(a,1) /*tex the reference count */
+# define attribute_value(a) vlink(a,1)
+
+typedef enum attribute_subtypes {
+ attribute_list_subtype,
+ attribute_value_subtype,
+} attribute_subtypes;
+
+# define last_attribute_subtype attribute_value_subtype
+
+/*tex
+ Penalties have only one primitive so we don't have |_code| here, also because it would conflict
+ with arguments.
+*/
+
+# define penalty_node_size 3
+# define penalty_amount(a) vlink(a,2)
+
+typedef enum penalty_subtypes {
+ user_penalty_subtype,
+ linebreak_penalty_subtype, /*tex includes widow, club, broken etc. */
+ line_penalty_subtype,
+ word_penalty_subtype,
+ orphan_penalty_subtype,
+ final_penalty_subtype,
+ math_pre_penalty_subtype,
+ math_post_penalty_subtype,
+ before_display_penalty_subtype,
+ after_display_penalty_subtype,
+ equation_number_penalty_subtype,
+} penalty_subtypes;
+
+# define last_penalty_subtype equation_number_penalty_subtype
+
+/*tex
+ We have plenty of glue variables and in the node lists most are also flagged. There is no
+ one|-|to|-|one correspondence between the codes (in tokens) and subtypes (in nodes) as listed
+ below, but they come close. The special math related glues and inserts now have nicer numbers.
+*/
+
+typedef enum glue_subtypes {
+ user_skip_glue,
+ line_skip_glue,
+ baseline_skip_glue,
+ par_skip_glue,
+ above_display_skip_glue,
+ below_display_skip_glue,
+ above_display_short_skip_glue,
+ below_display_short_skip_glue,
+ left_skip_glue,
+ right_skip_glue,
+ top_skip_glue,
+ split_top_skip_glue,
+ tab_skip_glue,
+ space_skip_glue,
+ xspace_skip_glue,
+ zero_space_skip_glue,
+ par_fill_right_skip_glue,
+ par_fill_left_skip_glue,
+ par_init_right_skip_glue,
+ par_init_left_skip_glue,
+ indent_skip_glue,
+ left_hang_skip_glue,
+ right_hang_skip_glue,
+ correction_skip_glue,
+ inter_math_skip_glue,
+ ignored_glue, /*tex |subtype| for cases where we ignore zero glue (alignments) */
+ page_glue, /*tex |subtype| used in the page builder */
+ /*tex math */
+ math_skip_glue,
+ thin_mu_skip_glue,
+ med_mu_skip_glue,
+ thick_mu_skip_glue,
+ /*tex more math */
+ conditional_math_glue, /*tex special |subtype| to suppress glue in the next node */ /* no need for jump */
+ rulebased_math_glue,
+ mu_glue, /*tex |subtype| for math glue */
+ /*tex leaders (glue with list) */
+ a_leaders, /*tex |subtype| for aligned leaders */
+ c_leaders, /*tex |subtype| for centered leaders */
+ x_leaders, /*tex |subtype| for expanded leaders */
+ g_leaders, /*tex |subtype| for global (page) leaders */
+ u_leaders,
+} glue_subtypes;
+
+# define last_glue_subtype u_leaders
+
+typedef enum skip_glue_codes_alias {
+ par_fill_skip_glue = par_fill_right_skip_glue,
+} skip_glue_codes_alias;
+
+# define is_leader(a) (node_subtype(a) >= a_leaders)
+
+# define glue_node_size 7
+# define glue_spec_size 5
+# define glue_data(a) vinfo(a,2) /* ignored in spec */
+# define glue_amount(a) vlink(a,2)
+# define glue_shrink(a) vinfo(a,3)
+# define glue_stretch(a) vlink(a,3)
+# define glue_stretch_order(a) vinfo(a,4)
+# define glue_shrink_order(a) vlink(a,4)
+# define glue_font(a) vinfo(a,5) /* not in spec */ /* when inter_math_skip_glue: parameter */
+# define glue_leader_ptr(a) vlink(a,5) /* not in spec */
+# define glue_options(a) vinfo(a,6) /* not in spec */ /* for now only internal */
+# define glue_unused(a) vlink(a,6) /* not in spec */
+
+inline static void tex_add_glue_option (halfword a, halfword r) { glue_options(a) |= r; }
+inline static void tex_remove_glue_option (halfword a, halfword r) { glue_options(a) &= ~(r | glue_options(a)); }
+inline static int tex_has_glue_option (halfword a, halfword r) { return (glue_options(a) & r) == r; }
+
+typedef enum glue_option_codes {
+ glue_option_normal = 0x0000,
+ // glue_force_auto_break = 0x0001,
+ // glue_originates_in_math = 0x0002,
+ glue_option_no_auto_break = 0x0001,
+} glue_option_codes;
+
+typedef enum math_subtypes {
+ begin_inline_math,
+ end_inline_math
+} math_subtypes;
+
+# define last_math_subtype end_inline_math
+
+/*tex
+ Math nodes (currently) partially overlap with glue because they also have a glue property.
+*/
+
+# define math_node_size 6
+# define math_surround(a) vinfo(a,2)
+# define math_amount(a) vlink(a,2)
+# define math_shrink(a) vinfo(a,3)
+# define math_stretch(a) vlink(a,3)
+# define math_stretch_order(a) vinfo(a,4)
+# define math_shrink_order(a) vlink(a,4)
+# define math_penalty(a) vinfo(a,5)
+# define math_options(a) vlink(a,5)
+
+inline static void tex_add_math_option (halfword a, halfword r) { math_options(a) |= r; }
+inline static void tex_remove_math_option (halfword a, halfword r) { math_options(a) &= ~(r | math_options(a)); }
+inline static int tex_has_math_option (halfword a, halfword r) { return (math_options(a) & r) == r; }
+
+/*tex Here are some (inline) helpers. We need specific ones for math glue. */
+
+inline static int tex_glue_is_zero(halfword g)
+{
+ return (! g) || ((glue_amount(g) == 0) && (glue_stretch(g) == 0) && (glue_shrink(g) == 0));
+}
+
+inline static int tex_math_glue_is_zero(halfword g)
+{
+ return (! g) || ((math_amount(g) == 0) && (math_stretch(g) == 0) && (math_shrink(g) == 0));
+}
+
+inline static int tex_same_glue(halfword a, halfword b)
+{
+ return
+ (a == b) /* same glue specs or both zero */
+ || (a && b && glue_amount(a) == glue_amount(b)
+ && glue_stretch(a) == glue_stretch(b)
+ && glue_shrink(a) == glue_shrink(b)
+ && glue_stretch_order(a) == glue_stretch_order(b)
+ && glue_shrink_order(a) == glue_shrink_order(b)
+ )
+ ;
+}
+
+inline static void tex_reset_glue_to_zero(halfword target)
+{
+ if (target) {
+ glue_amount(target) = 0;
+ glue_stretch(target) = 0;
+ glue_shrink(target) = 0;
+ glue_stretch_order(target) = 0;
+ glue_shrink_order(target) = 0;
+ }
+}
+
+inline static void tex_reset_math_glue_to_zero(halfword target)
+{
+ if (target) {
+ math_amount(target) = 0;
+ math_stretch(target) = 0;
+ math_shrink(target) = 0;
+ math_stretch_order(target) = 0;
+ math_shrink_order(target) = 0;
+ }
+}
+
+inline static void tex_copy_glue_values(halfword target, halfword source)
+{
+ if (source) {
+ glue_amount(target) = glue_amount(source);
+ glue_stretch(target) = glue_stretch(source);
+ glue_shrink(target) = glue_shrink(source);
+ glue_stretch_order(target) = glue_stretch_order(source);
+ glue_shrink_order(target) = glue_shrink_order(source);
+ } else {
+ glue_amount(target) = 0;
+ glue_stretch(target) = 0;
+ glue_shrink(target) = 0;
+ glue_stretch_order(target) = 0;
+ glue_shrink_order(target) = 0;
+ }
+}
+
+inline static int tex_is_par_init_glue(halfword n)
+{
+ switch (node_subtype(n)) {
+ case indent_skip_glue:
+ case par_init_left_skip_glue:
+ case par_init_right_skip_glue:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*tex
+ Kern nodes are relatively simple. Instead of |width| we use |kern_amount| which makes more
+ sense: we can go left, right, up or down. Margin kerns have been dropped and are now just a
+ special subtype of regular kerns.
+*/
+
+typedef enum kern_subtypes {
+ font_kern_subtype,
+ explicit_kern_subtype, /*tex |subtype| of kern nodes from |\kern| and |\/| */
+ accent_kern_subtype, /*tex |subtype| of kern nodes from accents */
+ italic_kern_subtype,
+ left_margin_kern_subtype,
+ right_margin_kern_subtype,
+ explicit_math_kern_subtype,
+ math_shape_kern_subtype,
+ horizontal_math_kern_subtype,
+ vertical_math_kern_subtype,
+} kern_subtypes;
+
+# define last_kern_subtype vertical_math_kern_subtype
+
+# define kern_node_size 3
+# define kern_amount(a) vlink(a,2) /*tex aka |width = vlink(a,2)| */
+# define kern_expansion(a) vinfo(a,2) /*tex expansion factor (hz) */
+
+inline static int tex_is_margin_kern(halfword n)
+{
+ return (n && node_type(n) == kern_node && (node_subtype(n) == left_margin_kern_subtype || node_subtype(n) == right_margin_kern_subtype));
+}
+
+/*tex
+
+ Disc nodes are complicated: they have three embedded nesting nodes to which the |pre_break|,
+ |post_break| and |no_break| fields point. In there we find a head pointer (|vlink| aka |hlink|)
+ and tail pointer (|tlink|). The |alink| pointer is used in the base mode font machinery and is
+ not really a prev pointer. We have to make sure it gets nilled when we communicate with \LUA.
+
+ The no-, pre-, and postbreak fields point to nesting nodes that are part of the disc node (three
+ times two memorywords). Sometimes these nodes are actually used, for instance when a temp node
+ is expected at the head of a list. The layout is:
+
+ \starttyping
+ [ type+subtype + next ]
+ [ attr + prev ]
+ [ penalty + nobreak ]
+ [ prebreak + postbreak ]
+ [ type+subtype next/hlink ] (nesting node prebreak)
+ [ tlink prev ]
+ [ type+subtype next/hlink ] (nesting node postbreak)
+ [ tlink prev ]
+ [ type+subtype next/hlink ] (nesting node nobreak)
+ [ tlink prev ]
+ \stoptyping
+
+ Another reason why we need the indirect apoproach is that we can set the fields to |null| which
+ is better than point to a nest node with no following up.
+
+*/
+
+/*tex
+ Among the dropped nodes (\LUATEX\ has them) are movements nodes (used in the \DVI\ backend)
+ and variable nodes (replaced by specification nodes).
+
+ Nesting nodes are really simple and just use the common type, subtype and next fields so they
+ have no dedicated fields. They can be part of another node type (like disc nodes).
+*/
+
+# define nesting_node_size 2
+
+typedef enum nesting_subtypes {
+ pre_break_code,
+ post_break_code,
+ no_break_code,
+ insert_head_code,
+ unset_nesting_code,
+} nesting_subtypes;
+
+# define last_nesting_subtype unset_nesting_code
+
+/*tex Here the codes in commands and subtypes are in sync. */
+
+typedef enum discretionary_subtypes {
+ normal_discretionary_code,
+ explicit_discretionary_code,
+ automatic_discretionary_code,
+ mathematics_discretionary_code,
+ syllable_discretionary_code,
+} discretionary_subtypes;
+
+# define last_discretionary_subtype syllable_discretionary_code
+# define last_discretionary_code automatic_discretionary_code
+
+typedef enum disc_options {
+ disc_option_normal_word = 0x0,
+ disc_option_pre_word = 0x1,
+ disc_option_post_word = 0x2,
+} disc_options;
+
+# define disc_node_size 13
+# define disc_no_break(a) vlink(a,2) /* beware: vinfo is used for type/subtype */
+# define disc_pre_break(a) vlink(a,3) /* beware: vinfo is used for type/subtype */
+# define disc_post_break(a) vlink(a,4) /* beware: vinfo is used for type/subtype */
+/* disc_no_break_node 5 6 */ /* this is a nesting node of size 2 */
+/* disc_pre_break_node 7 8 */ /* this is a nesting node of size 2 */
+/* disc_post_break_node 9 10 */ /* this is a nesting node of size 2 */
+# define disc_penalty(a) vinfo(a,11)
+# define disc_options(a) vlink(a,11)
+# define disc_class(a) vinfo(a,12)
+# define disc_unused(a) vlink(a,12)
+
+# define set_disc_penalty(a,b) disc_penalty(a) = b
+# define set_disc_class(a,b) disc_class(a) = b
+# define set_disc_options(a,b) disc_options(a) = b
+# define set_disc_option(a,b) disc_options(a) |= b
+
+# define has_disc_option(a,b) ((disc_options(a) & b) == b)
+
+# define unset_disc_class -1
+
+/*tex
+ These are pseudo nodes inside a node. We used to reference them by |*_break_head| but now call
+ just call them nodes so that we can use head and tail instead of hlink and tlink.
+*/
+
+# define disc_pre_break_node(a) (a+5)
+# define disc_post_break_node(a) (a+7)
+# define disc_no_break_node(a) (a+9)
+
+# define disc_pre_break_head(a) node_head(disc_pre_break_node(a))
+# define disc_post_break_head(a) node_head(disc_post_break_node(a))
+# define disc_no_break_head(a) node_head(disc_no_break_node(a))
+
+# define disc_pre_break_tail(a) node_tail(disc_pre_break_node(a))
+# define disc_post_break_tail(a) node_tail(disc_post_break_node(a))
+# define disc_no_break_tail(a) node_tail(disc_no_break_node(a))
+
+extern void tex_set_disc_field (halfword target, halfword location, halfword source);
+extern void tex_check_disc_field (halfword target);
+extern void tex_set_discpart (halfword d, halfword h, halfword t, halfword code);
+extern halfword tex_flatten_discretionaries (halfword head, int *count, int nest);
+extern void tex_flatten_leaders (halfword box, int *count);
+extern void tex_soften_hyphens (halfword head, int *found, int *replaced);
+extern halfword tex_harden_spaces (halfword head, halfword tolerance, int *count);
+
+/*tex
+ Lists need a rather large node, also because the have quite some extra possibilities, like the
+ orientation features. We can put the dir with the orientation but it becomes messy in casting
+ that way. Also, memory is not really a constraint and for a cpu cache we're better off this
+ way.
+
+ In the original setup the unset and align_record nodes have overlapping fields. This has the
+ side effect that when we access the alternates from \LUA\ that they can have weird values
+ unless we reset them. Even then, it can be that we actually want to use those other fields
+ somehow. For that reason it's better to waste a few more slots and play safe. We can now
+ actually explore table cells with offsets if we want.
+
+ Beware: in alignments
+
+ \startitemize[packed]
+ \startitem align record nodes become unset nodes \stopitem
+ \startitem unset nodes become hlist or vlist nodes \stopitem
+ \stopitemize
+*/
+
+typedef enum list_subtypes {
+ unknown_list,
+ line_list, /*tex paragraph lines */
+ hbox_list, /*tex |\hbox| */
+ indent_list, /*tex indentation box */
+ container_list, /*tex container box */
+ align_row_list, /*tex row from a |\halign| or |\valign| */
+ align_cell_list, /*tex cell from a |\halign| or |\valign| */
+ equation_list, /*tex display equation */
+ equation_number_list, /*tex display equation number */
+ math_list_list,
+ math_char_list,
+ math_pack_list,
+ math_h_extensible_list,
+ math_v_extensible_list,
+ math_h_delimiter_list,
+ math_v_delimiter_list,
+ math_over_delimiter_list,
+ math_under_delimiter_list,
+ math_numerator_list,
+ math_denominator_list,
+ math_modifier_list,
+ math_fraction_list,
+ math_nucleus_list,
+ math_sup_list,
+ math_sub_list,
+ math_pre_post_list,
+ math_degree_list,
+ math_scripts_list,
+ math_over_list,
+ math_under_list,
+ math_accent_list,
+ math_radical_list,
+ math_fence_list,
+ math_rule_list,
+ math_ghost_list,
+ insert_result_list,
+ local_list,
+ local_left_list,
+ local_right_list,
+ local_middle_list,
+} list_subtypes ;
+
+# define last_list_subtype local_middle_list
+# define noad_class_list_base 0x0100
+
+typedef enum list_anchors {
+ left_origin_anchor = 0x001,
+ left_height_anchor = 0x002,
+ left_depth_anchor = 0x003,
+ right_origin_anchor = 0x004,
+ right_height_anchor = 0x005,
+ right_depth_anchor = 0x006,
+ center_origin_anchor = 0x007,
+ center_height_anchor = 0x008,
+ center_depth_anchor = 0x009,
+ halfway_total_anchor = 0x00A,
+ halfway_height_anchor = 0x00B,
+ halfway_depth_anchor = 0x00C,
+ halfway_left_anchor = 0x00D,
+ halfway_right_anchor = 0x00E,
+} list_anchors;
+
+typedef enum list_signs {
+ negate_x_anchor = 0x100,
+ negate_y_anchor = 0x200,
+} list_signs;
+
+typedef enum list_geometries {
+ no_geometry = 0x0,
+ offset_geometry = 0x1,
+ orientation_geometry = 0x2,
+ anchor_geometry = 0x4,
+} list_geometries;
+
+# define box_node_size 15
+# define box_width(a) vlink(a,2)
+# define box_w_offset(a) vinfo(a,2)
+# define box_depth(a) vlink(a,3)
+# define box_d_offset(a) vinfo(a,3)
+# define box_height(a) vlink(a,4)
+# define box_h_offset(a) vinfo(a,4)
+# define box_list(a) vlink(a,5) /* 5 = list_offset */
+# define box_shift_amount(a) vinfo(a,5)
+# define box_glue_order(a) vlink(a,6)
+# define box_glue_sign(a) vinfo(a,6)
+# define box_glue_set(a) dvalue(a,7) /* So we reserve a whole memory word! */
+# define box_dir(a) vlink00(a,8)
+# define box_package_state(a) vlink01(a,8)
+# define box_axis(a) vlink02(a,8)
+# define box_geometry(a) vlink03(a,8)
+# define box_orientation(a) vinfo(a,8) /* also used for size in alignments */
+# define box_x_offset(a) vlink(a,9)
+# define box_y_offset(a) vinfo(a,9)
+# define box_pre_migrated(a) vlink(a,10)
+# define box_post_migrated(a) vinfo(a,10)
+# define box_pre_adjusted(a) vlink(a,11)
+# define box_post_adjusted(a) vinfo(a,11)
+# define box_source_anchor(a) vlink(a,12)
+# define box_target_anchor(a) vinfo(a,12)
+# define box_anchor(a) vlink(a,13)
+# define box_index(a) vinfo(a,13)
+# define box_input_file(a) vlink(a,14) /* aka box_synctex_tag */
+# define box_input_line(a) vinfo(a,14) /* aka box_synctex_line */
+
+# define box_total(a) (box_height(a) + box_depth(a))
+
+inline static void tex_set_box_geometry (halfword b, halfword g) { box_geometry(b) |= (singleword) (g); }
+/* static void tex_unset_box_geometry (halfword b, halfword g) { box_geometry(b) &= (singleword) ~((singleword) (g) | box_geometry(b)); } */
+inline static void tex_unset_box_geometry (halfword b, halfword g) { box_geometry(b) &= (singleword) (~g); }
+inline static int tex_has_geometry (halfword g, halfword f) { return ((singleword) (g) & (singleword) (f)) == (singleword) (f); }
+inline static int tex_has_box_geometry (halfword b, halfword g) { return (box_geometry(b) & (singleword) (g)) == (singleword) (g); }
+
+typedef enum package_states {
+ unknown_package_state = 0x00,
+ hbox_package_state = 0x01,
+ vbox_package_state = 0x02,
+ vtop_package_state = 0x03,
+ /* maybe vcenter */
+} package_states;
+
+typedef enum package_dimension_states {
+ package_dimension_not_set = 0x00,
+ package_dimension_size_set = 0x04,
+} package_dimension_states;
+
+typedef enum package_leader_states {
+ package_u_leader_not_set = 0x00,
+ package_u_leader_set = 0x08,
+ package_u_leader_delayed = 0x10,
+} package_leader_states;
+
+# define set_box_package_state(p,s) box_package_state(p) |= s
+# define has_box_package_state(p,s) ((box_package_state(p) & s) == s)
+# define is_box_package_state(p,s) ((p & s) == s)
+
+typedef enum list_axis { /* or maybe math states */
+ no_math_axis = 0x01,
+} list_axis;
+
+# define has_box_axis(p,s) ((box_axis(p) & s) == s)
+# define set_box_axis(p,s) box_axis(p) |= (s & 0xFF)
+
+/*tex
+ These |unset| nodes have the same layout as list nodes and at some point become an |hlist| or
+ |vlist| node.
+*/
+
+# define unset_node_size box_node_size
+# define box_glue_stretch(a) box_w_offset(a)
+# define box_glue_shrink(a) box_h_offset(a)
+# define box_span_count(a) box_d_offset(a)
+# define box_size(a) box_orientation(a)
+
+/*tex
+ The |align record| nodes have the same layout as list nodes and at some point become an |unset|
+ node.
+*/
+
+# define align_record_size box_node_size
+# define align_record_span_ptr(a) box_w_offset(a) /*tex A column spanning list */
+# define align_record_cmd(a) box_h_offset(a) /*tex Info to remember during template. */
+# define align_record_chr(a) box_d_offset(a) /*tex Info to remember during template. */
+# define align_record_pre_part(a) box_x_offset(a) /*tex The pointer to |u_j| token list. */
+# define align_record_post_part(a) box_y_offset(a) /*tex The pointer to |v_j| token list. */
+# define align_record_dimension(a) box_orientation(a) /*tex Optionally enforced width. */
+
+/*tex
+ Span nodes are tricky in the sense that their |span_link| actually has to sit in the same slot
+ as |align_record_span_ptr| because we need the initial location to be the same. This is why we
+ renamed this field to |span_ptr|. Moving it to another spot than in \LUATEX\ also opens the
+ possibility for attributes to cells.
+*/
+
+# define span_node_size 3
+# define span_span(a) vinfo(a,1)
+# define span_unused(a) vlink(a,1)
+# define span_width(a) vlink(a,2) /* overlaps with |box_width(a)|. */
+# define span_ptr(a) vinfo(a,2) /* overlaps with |box_w_offset(a)| and align_record_span_ptr(a). */
+
+/*tex
+ Here the subtypes and command codes partly overlay. We actually hav eonly avery few left because
+ it's mostly a backend feature now.
+*/
+
+typedef enum rule_subtypes {
+ normal_rule_subtype,
+ empty_rule_subtype,
+ strut_rule_subtype,
+ outline_rule_subtype,
+ user_rule_subtype,
+ math_over_rule_subtype,
+ math_under_rule_subtype,
+ math_fraction_rule_subtype,
+ math_radical_rule_subtype,
+ box_rule_subtype,
+ image_rule_subtype,
+} rule_subtypes;
+
+typedef enum rule_codes {
+ normal_rule_code,
+ empty_rule_code,
+ strut_rule_code,
+} rule_codes;
+
+# define last_rule_subtype image_rule_subtype
+# define first_rule_code normal_rule_code
+# define last_rule_code strut_rule_code
+
+# define rule_node_size 7
+# define rule_width(a) vlink(a,2)
+# define rule_x_offset(a) vinfo(a,2)
+# define rule_depth(a) vlink(a,3)
+# define rule_y_offset(a) vinfo(a,3)
+# define rule_height(a) vlink(a,4)
+# define rule_data(a) vinfo(a,4)
+# define rule_left(a) vinfo(a,5)
+# define rule_right(a) vlink(a,5)
+# define rule_font(a) vinfo(a,6)
+# define rule_character(a) vlink(a,6)
+
+# define rule_total(a) (rule_height(a) + rule_depth(a))
+
+/*tex
+
+ Originally glyph nodes had a |lig_ptr| but storing components makes not that much sense so we
+ dropped that. The free slot is now used for a state field. We already had a data field that
+ took another free slot and that behaves like an attribute. The glyph data field can be set at
+ the \TEX\ end, the state field is only accessible in \LUA. At the same time we reshuffled the
+ fields a bit so that the most accessed fields are close together.
+
+ The \LUATEX\ engine dropped the language node and moved that feature to the glyph nodes. In
+ addition to the language more properties could be set but they were all packed into one
+ halfword. In \LUAMETATEX\ we waste a few more bytes and keep the language separate but we
+ still pack a few properties.
+
+ In \TEX\ we have character nodes and glyph nodes, but here we only have one type. The subtype
+ can be used to indicate if we have ligatures but in \LUATEX\ for various reasons we don't follow
+ the integrated approach that \TEX\ has: we have callbacks for hyphenation, ligature building,
+ kerning etc.\ which demands separation, but more important is that we want to use \LUA\ to deal
+ with modern fonts. The components field that is still present in \LUATEX\ is gone because it
+ serves no purpose. We don't need to reassemble and when dealing with \OPENTYPE\ fonts we loose
+ information in successive steps anyway.
+
+ This also makes that the subtype is now only used to flag if glyphs have been processed. The
+ macro package can decide what additional properties get stored in this field.
+
+ We used to have this:
+
+ \starttyping
+ inline static void protect_glyph (halfword a) { quarterword s = node_subtype(a) ; if (s <= 256) { node_subtype(a) = s == 1 ? 256 : 256 + s; } }
+ inline static void unprotect_glyph (halfword a) { quarterword s = node_subtype(a) ; if (s > 256) { node_subtype(a) = s - 256; } }
+ inline static int is_protected_glyph (halfword a) { return node_subtype(a) >= 256; }
+ \stoptyping
+
+ These were also dropped:
+
+ \starttyping
+ # define is_character(p) (((node_subtype(p)) & glyph_character) == glyph_character)
+ # define is_ligature(p) (((node_subtype(p)) & glyph_ligature ) == glyph_ligature )
+ # define is_simple_character(p) (is_character(p) && ! is_ligature(p))
+ # define set_is_glyph(p) node_subtype(p) = (quarterword) (node_subtype(p) & ~glyph_character)
+ \stoptyping
+
+*/
+
+/*tex
+
+ Putting |width|, |height| and |depth| in a glyph has some advantages, for instance when we
+ fetch them in the builder, packer, \LUA\ interface, but it also has a disadvantage: we need to
+ have more complex copying of glyph nodes. For instance, when we copy glyphs in the open type
+ handler (e.g. for multiples) we also copy the fields. But then when we set a character, we also
+ would have to set the dimensions. Okay, some helper could do that (or a flag in setchar). It's
+ anyway not something to do in a hurry. An |x_extra| field is something different: combined with
+ setting |x_offset| that could replace font kerns: |x_advance = width + x_offset + x_extra|.
+
+*/
+
+//define glyph_node_size 12
+# define glyph_node_size 13
+# define glyph_character(a) vinfo(a,2)
+# define glyph_font(a) vlink(a,2)
+# define glyph_data(a) vinfo(a,3) /*tex We had that unused, so now it's like an attribute. */
+# define glyph_state(a) vlink(a,3) /*tex A user field (can be handy in \LUA). */
+# define glyph_language(a) vinfo(a,4)
+# define glyph_script(a) vlink(a,4)
+# define glyph_options(a) vinfo(a,5)
+# define glyph_hyphenate(a) vlink(a,5)
+# define glyph_protected(a) vinfo00(a,6)
+# define glyph_lhmin(a) vinfo01(a,6)
+# define glyph_rhmin(a) vinfo02(a,6)
+# define glyph_discpart(a) vinfo03(a,6)
+# define glyph_expansion(a) vlink(a,6)
+# define glyph_x_scale(a) vinfo(a,7)
+# define glyph_y_scale(a) vlink(a,7)
+# define glyph_scale(a) vinfo(a,8)
+# define glyph_raise(a) vlink(a,8)
+# define glyph_left(a) vinfo(a,9)
+# define glyph_right(a) vlink(a,9)
+# define glyph_x_offset(a) vinfo(a,10)
+# define glyph_y_offset(a) vlink(a,10)
+//define glyph_input_file(a) vinfo(a,11) /* aka glyph_synctex_tag */
+//define glyph_input_line(a) vlink(a,11) /* aka glyph_synctex_line */
+# define glyph_properties(a) vinfo0(a,11)
+# define glyph_group(a) vinfo1(a,11)
+# define glyph_index(a) vlink(a,11)
+# define glyph_input_file(a) vinfo(a,12)
+# define glyph_input_line(a) vlink(a,12)
+
+# define get_glyph_data(a) ((halfword) glyph_data(a))
+# define get_glyph_state(a) ((halfword) glyph_state(a))
+# define get_glyph_language(a) ((halfword) glyph_language(a))
+# define get_glyph_script(a) ((halfword) glyph_script(a))
+# define get_glyph_x_scale(a) ((halfword) glyph_x_scale(a))
+# define get_glyph_y_scale(a) ((halfword) glyph_y_scale(a))
+# define get_glyph_scale(a) ((halfword) glyph_scale(a))
+# define get_glyph_raise(a) ((halfword) glyph_raise(a))
+# define get_glyph_lhmin(a) ((halfword) glyph_lhmin(a))
+# define get_glyph_rhmin(a) ((halfword) glyph_rhmin(a))
+# define get_glyph_left(a) ((halfword) glyph_left(a))
+# define get_glyph_right(a) ((halfword) glyph_right(a))
+# define get_glyph_hyphenate(a) ((halfword) glyph_hyphenate(a))
+# define get_glyph_options(a) ((halfword) glyph_options(a))
+# define get_glyph_dohyph(a) (hyphenation_permitted(glyph_hyphenate(a), syllable_hyphenation_mode ) || hyphenation_permitted(glyph_hyphenate(a), force_handler_hyphenation_mode))
+# define get_glyph_uchyph(a) (hyphenation_permitted(glyph_hyphenate(a), uppercase_hyphenation_mode) || hyphenation_permitted(glyph_hyphenate(a), force_handler_hyphenation_mode))
+
+# define set_glyph_data(a,b) glyph_data(a) = b
+# define set_glyph_state(a,b) glyph_state(a) = b
+# define set_glyph_language(a,b) glyph_language(a) = b
+# define set_glyph_script(a,b) glyph_script(a) = b
+# define set_glyph_x_scale(a,b) glyph_x_scale(a) = b
+# define set_glyph_y_scale(a,b) glyph_y_scale(a) = b
+# define set_glyph_x_offset(a,b) glyph_x_offset(a) = b
+# define set_glyph_y_offset(a,b) glyph_y_offset(a) = b
+# define set_glyph_scale(a,b) glyph_scale(a) = b
+# define set_glyph_raise(a,b) glyph_raise(a) = b
+# define set_glyph_left(a,b) glyph_left(a) = b
+# define set_glyph_right(a,b) glyph_right(a) = b
+# define set_glyph_lhmin(a,b) glyph_lhmin(a) = (singleword) b
+# define set_glyph_rhmin(a,b) glyph_rhmin(a) = (singleword) b
+# define set_glyph_hyphenate(a,b) glyph_hyphenate(a) = ((halfword) b)
+# define set_glyph_options(a,b) glyph_options(a) = ((halfword) b)
+/* set_glyph_dohyph(a,b) glyph_hyphenate(a) = ((halfword) flip_hyphenation_mode(glyph_hyphenate(a),syllable_hyphenation_mode)) */
+# define set_glyph_uchyph(a,b) glyph_hyphenate(a) = ((halfword) flip_hyphenation_mode(glyph_hyphenate(a),uppercase_hyphenation_mode))
+# define set_glyph_discpart(a,b) glyph_discpart(a) = (singleword) (b)
+# define get_glyph_discpart(a) ((halfword) glyph_discpart(a))
+
+typedef enum glyph_subtypes {
+ /* initial value: */
+ glyph_unset_subtype,
+ /* traditional text: */
+ glyph_character_subtype,
+ glyph_ligature_subtype,
+ /* special math */
+ glyph_math_delimiter_subtype,
+ glyph_math_extensible_subtype,
+ /* engine math, class driven */
+ glyph_math_ordinary_subtype,
+ glyph_math_operator_subtype,
+ glyph_math_binary_subtype,
+ glyph_math_relation_subtype,
+ glyph_math_open_subtype,
+ glyph_math_close_subtype,
+ glyph_math_punctuation_subtype,
+ glyph_math_variable_subtype,
+ glyph_math_active_subtype,
+ glyph_math_inner_subtype,
+ glyph_math_under_subtype,
+ glyph_math_over_subtype,
+ glyph_math_fraction_subtype,
+ glyph_math_radical_subtype,
+ glyph_math_middle_subtype,
+ glyph_math_accent_subtype,
+ glyph_math_fenced_subtype,
+ glyph_math_ghost_subtype,
+ /* extra math, user classes, set but anonymous */
+ glyph_math_extra_subtype = 31,
+} glyph_subtypes;
+
+# define last_glyph_subtype glyph_math_accent_subtype
+
+typedef enum glyph_hstate_codes {
+ glyph_discpart_unset,
+ glyph_discpart_pre,
+ glyph_discpart_post,
+ glyph_discpart_replace,
+ glyph_discpart_always,
+} glyph_hstate_codes;
+
+typedef enum glyph_option_codes {
+ /*tex These are part of the defaults (all): */
+ glyph_option_normal_glyph = 0x0000,
+ glyph_option_no_left_ligature = 0x0001,
+ glyph_option_no_right_ligature = 0x0002,
+ glyph_option_no_left_kern = 0x0004,
+ glyph_option_no_right_kern = 0x0008,
+ glyph_option_no_expansion = 0x0010,
+ glyph_option_no_protrusion = 0x0020,
+ glyph_option_apply_x_offset = 0x0040,
+ glyph_option_apply_y_offset = 0x0080,
+ glyph_option_no_italic_correction = 0x0100,
+ /* These are only meant for math characters: */
+ glyph_option_math_discretionary = 0x0200,
+ glyph_option_math_italics_too = 0x0400,
+ /*tex So watch out: this is a subset! */
+ glyph_option_all = 0x01FF,
+} glyph_option_codes;
+
+typedef enum auto_discretionary_codes {
+ auto_discretionary_normal = 0x0001, /* turn glyphs into discretionary with three similar components */
+ auto_discretionary_italic = 0x0002, /* also include italic correcxtion when present */
+} auto_discretionary_codes;
+
+inline static void tex_add_glyph_option (halfword a, halfword r) { glyph_options(a) |= r; }
+inline static void tex_remove_glyph_option (halfword a, halfword r) { glyph_options(a) &= ~(r | glyph_options(a)); }
+inline static int tex_has_glyph_option (halfword a, halfword r) { return (glyph_options(a) & r) == r; }
+
+/*tex
+ As we have a small field available for protection we no longer need to pack the protection
+ state in the subtype. We can now basically use the subtype for anything we want (as long as it
+ stays within the range |0x0000-0xFFFF|.
+*/
+
+/* inline static void tex_protect_glyph (halfword a) { node_subtype(a) |= (quarterword) 0x8000; } */
+/* inline static void tex_unprotect_glyph (halfword a) { node_subtype(a) &= (quarterword) 0x7FFF; } */
+/* inline static int tex_is_protected_glyph (halfword a) { return node_subtype(a) >= (quarterword) 0x8000; } */
+/* inline static int tex_subtype_of_glyph (halfword a) { return node_subtype(a) & (quarterword) 0x7FFF; } */
+
+typedef enum glyph_protection_codes {
+ glyph_unprotected_code = 0x0,
+ glyph_protected_text_code = 0x1,
+ glyph_protected_math_code = 0x2,
+} glyph_protection_codes;
+
+/*tex
+ Next come some very specialized nodes types. First the marks. They just register a token list.
+*/
+
+# define mark_node_size 3
+# define mark_ptr(a) vlink(a,2)
+# define mark_index(a) vinfo(a,2)
+
+typedef enum mark_codes {
+ set_mark_value_code,
+ reset_mark_value_code,
+} mark_codes;
+
+# define last_mark_subtype reset_mark_value_code
+
+/*tex
+ The (not really used in \CONTEXT) |\vadjust| nodes are also small. The codes and subtypes
+ overlap.
+*/
+
+typedef enum adjust_subtypes {
+ pre_adjust_code,
+ post_adjust_code,
+ local_adjust_code,
+} adjust_subtypes;
+
+typedef enum adjust_options {
+ adjust_option_none = 0x00,
+ adjust_option_before = 0x01,
+ adjust_option_baseline = 0x02,
+ adjust_option_depth_before = 0x04,
+ adjust_option_depth_after = 0x08,
+ adjust_option_depth_check = 0x10,
+ adjust_option_depth_last = 0x20,
+} adjust_options;
+
+# define last_adjust_subtype local_adjust_code
+
+# define adjust_node_size 5
+# define adjust_list(a) vlink(a,2)
+# define adjust_options(a) vinfo(a,2)
+# define adjust_index(a) vlink(a,3)
+# define adjust_reserved(a) vinfo(a,3)
+# define adjust_depth_before(a) vlink(a,4)
+# define adjust_depth_after(a) vinfo(a,4)
+
+# define has_adjust_option(p,o) ((adjust_options(p) & o) == o)
+
+/*tex
+ Inserts are more complicated. The |ins| node stores an insert in the list while |inserting|
+ nodes keep track of where to break the page so that they (hopefully) stay with the text. As
+ already mentioned, the insert node is tricky in the sense that it uses an offset to an
+ embedded (fake) node. That node acts as start of a next chain. Making that more transparent
+ would demand some changes that I'm not willing to make right now (and maybe never).
+*/
+
+# define insert_node_size 6 /* can become 1 smaller or we can have insert_index instead of subtype */
+# define insert_index(a) vinfo(a,2) /* width is not used */
+# define insert_float_cost(a) vlink(a,2)
+# define insert_max_depth(a) vlink(a,3)
+# define insert_total_height(a) vlink(a,4) /* the sum of height and depth, i.e. total */
+# define insert_list(a) vinfo(a,5) /* is alias for |node_next|*/
+# define insert_split_top(a) vlink(a,5) /* a state variable */
+
+# define insert_first_box(a) (a + 5) /*tex A fake node where box_list_ptr becomes a next field. */
+
+# define split_node_size 5 /*tex Can become a |split_up_node|. */
+# define split_insert_index(a) vinfo(a,2) /*tex Same slot! */
+# define split_broken(a) vlink(a,2) /*tex An insertion for this class will break here if anywhere. */
+# define split_broken_insert(a) vinfo(a,3) /*tex This insertion might break at |broken_ptr|. */
+# define split_last_insert(a) vlink(a,3) /*tex The most recent insertion for this |subtype|. */
+# define split_best_insert(a) vinfo(a,4) /*tex The optimum most recent insertion. */
+# define split_height(a) vlink(a,4) /*tex Aka |height(a) = vlink(a,4)| */ /* todo */
+
+typedef enum split_subtypes {
+ normal_split_subtype,
+ insert_split_subtype,
+} split_subtypes;
+
+# define last_split_subtype insert_split_subtype
+
+/*tex
+ It's now time for some Some handy shortcuts. These are used when determining proper break points
+ and|/|or the beginning or end of words.
+*/
+
+# define last_preceding_break_node whatsit_node
+# define last_non_discardable_node dir_node
+# define last_node_with_attributes glyph_node
+# define last_complex_node align_record_node
+# define max_node_type passive_node
+
+# define precedes_break(a) (node_type(a) <= last_preceding_break_node)
+# define precedes_kern(a) ((node_type(a) == kern_node) && (node_subtype(a) == font_kern_subtype || node_subtype(a) == accent_kern_subtype || node_subtype(a) == math_shape_kern_subtype))
+# define precedes_dir(a) ((node_type(a) == dir_node) && normalize_line_mode_permitted(normalize_line_mode_par,break_after_dir_mode))
+# define non_discardable(a) (node_type(a) <= last_non_discardable_node)
+
+inline static int tex_nodetype_is_complex (halfword t) { return t <= last_complex_node; }
+inline static int tex_nodetype_has_attributes (halfword t) { return t <= last_node_with_attributes; }
+inline static int tex_nodetype_has_subtype (halfword t) { return t != glue_spec_node && t != math_spec_node && t != font_spec_node; }
+inline static int tex_nodetype_has_prev (halfword t) { return t != glue_spec_node && t != math_spec_node && t != font_spec_node && t != attribute_node; }
+inline static int tex_nodetype_has_next (halfword t) { return t != glue_spec_node && t != math_spec_node && t != font_spec_node; }
+inline static int tex_nodetype_is_visible (halfword t) { return (t >= 0) && (t <= max_node_type) && lmt_interface.node_data[t].visible; }
+
+/*tex
+ This is a bit weird place to define them but anyway. In the meantime in \LUAMETATEX\ we no
+ longer have the option to report the codes used in \ETEX. We have different nodes so it makes
+ no sense to complicate matters (although earlier version of \LUAMETATEX\ has this organized
+ quite well \unknown\ just an example of cleaning up, wondering about the use and then dropping
+ it.
+*/
+
+# define get_node_size(i) (lmt_interface.node_data[i].size)
+# define get_node_name(i) (lmt_interface.node_data[i].name)
+/* get_etex_code(i) (lmt_interface.node_data[i].etex) */
+
+/*tex
+ Although expressions could use some dedicated data structure, currently they are implemented
+ using a linked list. This means that only memory is the limitation for recursion but I might
+ as well go for a dedicated structure some day, just for the fun of implementing it. It is
+ probably also more efficient. The current approach is inherited from \ETEX. The stack is only
+ used when we have expressions between parenthesis.
+*/
+
+# define expression_node_size 3
+# define expression_type(a) vinfo00(a,1) /*tex one of the value levels */
+# define expression_state(a) vinfo01(a,1)
+# define expression_result(a) vinfo02(a,1)
+# define expression_unused(a) vinfo03(a,1)
+# define expression_expression(a) vlink(a,1) /*tex saved expression so far */
+# define expression_term(a) vlink(a,2) /*tex saved term so far */
+# define expression_numerator(a) vinfo(a,2) /*tex saved numerator */
+
+/*tex
+ To be decided: go double
+*/
+
+# define expression_entry(a) lvalue(a,2)
+
+/*tex
+ This is a node that stores a font state. In principle we can do without but for tracing it
+ really helps to have this compound element because it is more compact. We could have gone
+ numeric and use the sparse array approach but then we'd have to add a 4 int store which is more
+ code and also makes save and restore more complex.
+*/
+
+# define font_spec_node_size 4 /* we can be smaller: no attr and no prev */
+# define font_spec_identifier(a) vinfo(a,2)
+# define font_spec_scale(a) vlink(a,2)
+# define font_spec_x_scale(a) vinfo(a,3)
+# define font_spec_y_scale(a) vlink(a,3)
+
+inline static int tex_same_fontspec(halfword a, halfword b)
+{
+ return
+ (a == b)
+ || (a && b && font_spec_identifier(a) == font_spec_identifier(b)
+ && font_spec_scale(a) == font_spec_scale(b)
+ && font_spec_x_scale(a) == font_spec_x_scale(b)
+ && font_spec_y_scale(a) == font_spec_y_scale(b)
+ )
+ ;
+}
+
+/*tex
+ At the cost of some more memory we now use a mode for storage. This not only overcomes the
+ \UNICODE\ limitation but also permits storing more in the future.
+*/
+
+# define math_spec_node_size 3
+# define math_spec_class(a) vinfo00(a,1) /* attr */
+# define math_spec_family(a) vinfo01(a,1)
+# define math_spec_character(a) vlink(a,1) /* prev */
+# define math_spec_properties(a) vinfo0(a,2)
+# define math_spec_group(a) vinfo1(a,2)
+# define math_spec_index(a) vlink(a,2)
+
+# define math_spec_value(a) (((math_spec_class(a) & 0x3F) << 12) + ((math_spec_family(a) & 0x3F) << 8) + (math_spec_character(a) & 0xFF))
+
+inline static int tex_same_mathspec(halfword a, halfword b)
+{
+ return
+ (a == b)
+ || (a && b && math_spec_class(a) == math_spec_class(b)
+ && math_spec_family(a) == math_spec_family(b)
+ && math_spec_character(a) == math_spec_character(b)
+ && math_spec_properties(a) == math_spec_properties(b)
+ && math_spec_group(a) == math_spec_group(b)
+ && math_spec_index(a) == math_spec_index(b)
+ )
+ ;
+}
+
+/*tex
+ Here are some more stack related nodes.
+*/
+
+# define align_stack_node_size 10
+# define align_stack_align_ptr(a) vinfo(a,1)
+# define align_stack_cur_align(a) vlink(a,1)
+# define align_stack_preamble(a) vinfo(a,2)
+# define align_stack_cur_span(a) vlink(a,2)
+# define align_stack_cur_loop(a) vinfo(a,3)
+# define align_stack_wrap_source(a) vlink(a,3)
+# define align_stack_align_state(a) vinfo(a,4)
+# define align_stack_no_align_level(a) vlink(a,4)
+# define align_stack_cur_post_adjust_head(a) vinfo(a,5)
+# define align_stack_cur_post_adjust_tail(a) vlink(a,5)
+# define align_stack_cur_pre_adjust_head(a) vinfo(a,6)
+# define align_stack_cur_pre_adjust_tail(a) vlink(a,6)
+# define align_stack_cur_post_migrate_head(a) vinfo(a,7)
+# define align_stack_cur_post_migrate_tail(a) vlink(a,7)
+# define align_stack_cur_pre_migrate_head(a) vinfo(a,8)
+# define align_stack_cur_pre_migrate_tail(a) vlink(a,8)
+# define align_stack_no_tab_skips(a) vinfo(a,9)
+# define align_stack_attr_list(a) vlink(a,9)
+
+/*tex
+ If nodes are for nesting conditionals. We have more state information that in (for instance)
+ \LUATEX\ because we have more tracing and more test variants.
+*/
+
+# define if_node_size 3 /*tex we can use prev now */
+# define if_limit_type(a) vinfo0(a,1) /*tex overlaps with node_attr */
+# define if_limit_subtype(a) vinfo1(a,1) /*tex overlaps with node_attr */
+# define if_limit_unless(a) vinfo00(a,2)
+# define if_limit_step(a) vinfo01(a,2)
+# define if_limit_stepunless(a) vinfo02(a,2)
+# define if_limit_unused(a) vinfo03(a,2)
+# define if_limit_line(a) vlink(a,2)
+
+/*tex
+ Now come some rather special ones. For instance par shapes and file cq.\ line related nodes
+ were variable nodes. Thsi was dropped and replaced by a more generic specficiation node type.
+ In principle we can use that for more purposes.
+
+ We use a bit of abstraction as preparation for different allocations. Dynamic allocation makes
+ it possible to get rid of variable nodes but it is slower.
+
+ Because this node has no links we can use the next field as counter. The subtype is just for
+ diagnostics. This node is special in the sense that it has a real pointer. Such nodes will not
+ be stored in the format file. Because there is a pointer field we have some extra accessors.
+
+ Todo: we also need to catch the fact that we can run out of memory but in practice that will
+ not happen soon, for instance because we seldom use parshapes. And in the meantime the pseudo
+ file related nodes are gone anyway because all file IO has been delegated to \LUA\ now.
+*/
+
+# define specification_node_size 3
+# define specification_count(a) vlink(a,0)
+# define specification_options(a) vinfo(a,1)
+# define specification_unused(a) vlink(a,1)
+# define specification_pointer(a) (mvalue(a,2))
+
+typedef enum specification_options {
+ specification_option_repeat = 0x01,
+} specifications_options;
+
+# define specification_index(a,n) ((memoryword *) specification_pointer(a))[n - 1]
+
+# define specification_repeat(a) ((specification_options(a) & specification_option_repeat) == specification_option_repeat)
+
+# define specification_n(a,n) (specification_repeat(a) ? ((n - 1) % specification_count(a) + 1) : (n > specification_count(a) ? specification_count(a) : n))
+
+extern void tex_null_specification_list (halfword a);
+extern void tex_new_specification_list (halfword a, halfword n, halfword o);
+extern void tex_dispose_specification_list (halfword a);
+extern void tex_copy_specification_list (halfword a, halfword b);
+extern void tex_shift_specification_list (halfword a, int n, int rotate);
+
+inline static int tex_get_specification_count (halfword a) { return specification_count(a); }
+inline static halfword tex_get_specification_indent (halfword a, halfword n) { return specification_index(a,specification_n(a,n)).half0; }
+inline static halfword tex_get_specification_width (halfword a, halfword n) { return specification_index(a,specification_n(a,n)).half1; }
+inline static halfword tex_get_specification_penalty (halfword a, halfword n) { return specification_index(a,specification_n(a,n)).half0; }
+inline static void tex_set_specification_indent (halfword a, halfword n, halfword v) { specification_index(a,n).half0 = v; }
+inline static void tex_set_specification_width (halfword a, halfword n, halfword v) { specification_index(a,n).half1 = v; }
+inline static void tex_set_specification_penalty (halfword a, halfword n, halfword v) { specification_index(a,n).half0 = v; }
+inline static void tex_set_specification_option (halfword a, int o) { specification_options(a) |= o; }
+
+extern halfword tex_new_specification_node (halfword n, quarterword s, halfword options);
+extern void tex_dispose_specification_nodes (void);
+
+/*tex
+ We now define some math related nodes (and noads) and start with style and choice nodes. Style
+ nodes can be smaller, the information is encoded in |subtype|, but choice nodes are on-the-spot
+ converted to style nodes with slack. The advantage is that we don't run into issues when a choice
+ node is the first node in which case we would have to adapt head pointers (read: feed them back
+ into the calling routines). So, we keep this as it is now.
+
+ Parameter nodes started out as an experiment. We could actually use the same mechanism as
+ attributes but (1) we don't want attribute nodes in the list, it is very math specific and (3)
+ we don't need to be real fast here.
+
+ Maybe these three can be merged into one type but on the other hand they are part of the \TEX\
+ legacy and well documented so \unknown for now we keep it as-is. In the meantime we are no
+ longer casting choices to styles.
+
+*/
+
+# define style_node_size 3
+# define style_style node_subtype
+# define style_scale(a) vinfo(a,2)
+# define style_reserved(a) vlink(a,2)
+
+# define choice_node_size 5
+//define choice_style node_subtype
+# define choice_display_mlist(a) vinfo(a,2) /*tex mlist to be used in display style or pre_break */
+# define choice_text_mlist(a) vlink(a,2) /*tex mlist to be used in text style or post_break */
+# define choice_script_mlist(a) vinfo(a,3) /*tex mlist to be used in script style or no_break */
+# define choice_script_script_mlist(a) vlink(a,3) /*tex mlist to be used in scriptscript style */
+# define choice_class(a) vinfo(a,4) /*tex we could abuse the script script field */
+# define choice_unused(a) vlink(a,4)
+
+# define choice_pre_break choice_display_mlist
+# define choice_post_break choice_text_mlist
+# define choice_no_break choice_script_mlist
+
+# define parameter_node_size 3
+# define parameter_style node_subtype
+# define parameter_name(a) vinfo(a,2)
+# define parameter_value(a) vlink(a,2)
+
+typedef enum simple_choice_subtypes {
+ normal_choice_subtype,
+ discretionary_choice_subtype,
+} simple_choice_subtypes;
+
+# define last_choice_subtype discretionary_choice_subtype
+
+/*tex
+ Because noad types get changed when processing we need to make sure some if the node sizes
+ match and that we don't share slots with different properties.
+
+ First come the regular noads. The generic noad has the same size and similar fields as a fence
+ noad, and their types get swapped a few times.
+
+ We accept a little waste of space in order to get nicer code. After all, math is not that
+ demanding. Although delimiter, accent, fraction and radical share the same structure we do use
+ specific field names because of clarity. Not all fields are used always.
+
+ \starttabulate[|l|l|l|l|l|l|]
+ \FL
+ \BC \BC noad \BC accent \BC fraction \BC radical \NC fence \NC \NR
+ \ML \NC
+ \NC vlink 2 \NC new_hlist \NC \NC \NC \NC \NC \NR
+ \ML \NC
+ \NC vinfo 2 \NC nucleus \NC \NC \NC \NC \NC \NR
+ \NC vlink 3 \NC supscr \NC \NC numerator \NC \NC \NC \NR
+ \NC vinfo 3 \NC subscr \NC \NC denominator \NC \NC \NC \NR
+ \NC vlink 4 \NC supprescr \NC \NC \NC \NC \NC \NR
+ \NC vinfo 4 \NC subprescr \NC \NC \NC \NC \NC \NR
+ \ML \NC
+ \NC vlink 5 \NC italic \NC \NC \NC \NC \NC \NR
+ \NC vinfo 5 \NC width \NC \NC \NC \NC \NC \NR
+ \NC vlink 6 \NC height \NC \NC \NC \NC \NC \NR
+ \NC vinfo 6 \NC depth \NC \NC \NC \NC \NC \NR
+ \ML \NC
+ \NC vlink 7 \NC options \NC \NC \NC \NC \NC \NR
+ \NC vinfo 7 \NC style \NC \NC \NC \NC \NC \NR
+ \NC vlink 8 \NC family \NC \NC \NC \NC \NC \NR
+ \NC vinfo 8 \NC class \NC \NC \NC \NC \NC \NR
+ \NC vlink 9 \NC source \NC \NC \NC \NC \NC \NR
+ \NC vinfo 9 \NC prime \NC \NC \NC \NC \NC \NR
+ \NC vlink 10 \NC leftslack \NC \NC \NC \NC \NC \NR
+ \NC vinfo 10 \NC rightslack \NC \NC \NC \NC \NC \NR
+ \ML \NC
+ \NC vlink 11 \NC extra_1 \NC top_character \NC rule_thickness \NC degree \NC list \NC \NR
+ \NC vinfo 11 \NC extra_2 \NC bot_character \NC left_delimiter \NC left_delimiter \NC source \NC \NR
+ \NC vlink 12 \NC extra_3 \NC overlay_character \NC right_delimiter \NC right_delimiter \NC top \NC \NR
+ \NC vinfo 12 \NC extra_4 \NC fraction \NC middle_delimiter \NC \NC bottom \NC \NR
+ \NC vlink 13 \NC extra_5 \NC topovershoot \NC \NC height \NC \NC \NR
+ \NC vinfo 13 \NC extra_6 \NC botovershoot \NC \NC depth \NC \NC \NR
+ \LL
+ \stoptabulate
+
+ We can use smaller variables for style and class and then have one field available for
+ other usage so no need to grow.
+
+*/
+
+# define noad_state_node_size 6
+# define noad_state_topright(a) vlink(a,2)
+# define noad_state_bottomright(a) vinfo(a,2)
+# define noad_state_topleft(a) vlink(a,3)
+# define noad_state_bottomleft(a) vinfo(a,3)
+# define noad_state_height(a) vlink(a,4)
+# define noad_state_depth(a) vinfo(a,4)
+# define noad_state_toptotal(a) vlink(a,5)
+# define noad_state_bottomtotal(a) vinfo(a,5)
+
+# define noad_size 14
+# define noad_new_hlist(a) vlink(a,2) /*tex the translation of an mlist; a bit confusing name */
+# define noad_nucleus(a) vinfo(a,2)
+# define noad_supscr(a) vlink(a,3)
+# define noad_subscr(a) vinfo(a,3)
+# define noad_supprescr(a) vlink(a,4)
+# define noad_subprescr(a) vinfo(a,4)
+# define noad_italic(a) vlink(a,5) /*tex Sometimes used, might become more. */
+# define noad_width(a) vinfo(a,5)
+# define noad_height(a) vlink(a,6)
+# define noad_depth(a) vinfo(a,6)
+# define noad_options(a) vlink(a,7)
+# define noad_style(a) vinfo00(a,7)
+# define noad_family(a) vinfo01(a,7)
+# define noad_script_state(a) vinfo02(a,7)
+# define noad_analyzed(a) vinfo03(a,7) /*tex used for experiments */
+# define noad_state(a) vlink(a,8) /*tex this might replace */
+# define noad_class_main(a) vinfo00(a,8)
+# define noad_class_left(a) vinfo01(a,8)
+# define noad_class_right(a) vinfo02(a,8)
+# define noad_script_order(a) vinfo03(a,8)
+# define noad_source(a) vlink(a,9)
+# define noad_prime(a) vinfo(a,9)
+# define noad_left_slack(a) vlink(a,10)
+# define noad_right_slack(a) vinfo(a,10)
+# define noad_extra_1(a) vlink(a,11)
+# define noad_extra_2(a) vinfo(a,11)
+# define noad_extra_3(a) vlink(a,12)
+# define noad_extra_4(a) vinfo(a,12)
+# define noad_extra_5(a) vlink(a,13)
+# define noad_extra_6(a) vinfo(a,13)
+
+# define noad_total(a) (noad_height(a) + noad_depth(a))
+
+# define noad_has_postscripts(a) (noad_subscr(a) || noad_supscr(a))
+# define noad_has_prescripts(a) (noad_subprescr(a) || noad_supprescr(a))
+# define noad_has_scripts(a) (noad_has_postscripts(a) || noad_has_prescripts(a) || noad_prime(a))
+# define noad_has_following_scripts(a) (noad_subscr(a) || noad_supscr(a) || noad_prime(a))
+# define noad_has_superscripts(a) (noad_supprescr(a) || noad_supscr(a) || noad_prime(a))
+# define noad_has_subscripts(a) (noad_subprescr(a) || noad_subscr(a))
+
+# define noad_has_scriptstate(a,s) ((noad_script_state(a) & s) == s)
+
+# define unset_noad_class 0xFE
+
+typedef enum noad_script_states {
+ post_super_script_state = 0x01,
+ post_sub_script_state = 0x02,
+ pre_super_script_state = 0x04,
+ pre_sub_script_state = 0x08,
+ prime_script_state = 0x10,
+} noad_script_states;
+
+typedef enum noad_script_locations {
+ prime_unknown_location,
+ prime_at_begin_location,
+ prime_above_sub_location,
+ prime_at_end_location,
+} noad_prime_locations;
+
+typedef enum noad_script_order {
+ script_unknown_first,
+ script_primescript_first,
+ script_subscript_first,
+ script_superscript_first,
+} noad_script_order;
+
+typedef struct noad_classes {
+ singleword main;
+ singleword left;
+ singleword right;
+} noad_classes;
+
+# define reset_noad_classes(n) do { \
+ noad_class_main(n) = (singleword) unset_noad_class; \
+ noad_class_left(n) = (singleword) unset_noad_class; \
+ noad_class_right(n) = (singleword) unset_noad_class; \
+} while (0);
+
+# define set_noad_classes(n,c) do { \
+ noad_class_main(n) = (singleword) (c & 0xFF); \
+ noad_class_left(n) = (singleword) (c & 0xFF); \
+ noad_class_right(n) = (singleword) (c & 0xFF); \
+} while (0);
+
+# define set_noad_main_class(n,c) noad_class_main(n) = (singleword) (c & 0xFF)
+# define set_noad_left_class(n,c) noad_class_left(n) = (singleword) (c & 0xFF)
+# define set_noad_right_class(n,c) noad_class_right(n) = (singleword) (c & 0xFF)
+
+# define get_noad_main_class(n) (noad_class_main(n))
+# define get_noad_left_class(n) (noad_class_left(n))
+# define get_noad_right_class(n) (noad_class_right(n))
+
+# define set_noad_style(n,s) noad_style(n) = (singleword) (s & 0xFF)
+# define set_noad_family(n,f) noad_family(n) = (singleword) (f & 0xFF)
+
+/*tex
+ Options are something \LUATEX\ and in \LUAMETEX\ we added some more. When we have dimensions
+ then we obey |axis| and otherwise |noaxis|. This might evolve a bit over time. These options
+ currently are on the same spot but we pretend they aren't so we have dedicated accessors. This
+ also makes clear what noads have what options.
+
+ If we run out of options we can combine some, like auto.
+*/
+
+typedef enum noad_options {
+ noad_option_axis = 0x00000001,
+ noad_option_no_axis = 0x00000002,
+ noad_option_exact = 0x00000004,
+ noad_option_left = 0x00000008, /* align option for overflown under/over */ /* used ? */
+ noad_option_middle = 0x00000010, /* idem */
+ noad_option_right = 0x00000020, /* idem */
+ noad_option_adapt_to_left_size = 0x00000040, /* old trickery, might go away but kind of fun */
+ noad_option_adapt_to_right_size = 0x00000080, /* idem */
+ noad_option_no_sub_script = 0x00000100,
+ noad_option_no_super_script = 0x00000200,
+ noad_option_no_sub_pre_script = 0x00000400,
+ noad_option_no_super_pre_script = 0x00000800,
+ noad_option_no_script = 0x00001000,
+ noad_option_no_overflow = 0x00002000, /* keep (middle) extensible widthin target size */
+ noad_option_void = 0x00004000, /* wipe and set width to zero */
+ noad_option_phantom = 0x00008000, /* wipe */
+ noad_option_openup_height = 0x00010000,
+ noad_option_openup_depth = 0x00020000,
+ noad_option_limits = 0x00040000, /* traditional modifier */
+ noad_option_no_limits = 0x00080000, /* idem */
+ noad_option_prefer_font_thickness = 0x00100000,
+ noad_option_no_ruling = 0x00200000,
+ noad_option_shifted_sub_script = 0x00400000,
+ noad_option_shifted_super_script = 0x00800000,
+ noad_option_shifted_sub_pre_script = 0x01000000,
+ noad_option_shifted_super_pre_script = 0x02000000,
+ noad_option_unpack_list = 0x04000000,
+ noad_option_no_check = 0x08000000, /* don't check for missing end fence */
+ noad_option_auto = 0x10000000,
+ noad_option_unroll_list = 0x20000000,
+ noad_option_followed_by_space = 0x40000000,
+ /* available: */
+ noad_option_reserved = 0x80000000,
+} noad_options;
+
+# define has_option(a,b) (((a) & (b)) == (b))
+# define unset_option(a,b) ((a) & ~(b))
+
+inline static void tex_add_noad_option (halfword a, halfword r) { noad_options(a) |= r; }
+inline static void tex_remove_noad_option (halfword a, halfword r) { noad_options(a) &= ~(r | noad_options(a)); }
+inline static int tex_has_noad_option (halfword a, halfword r) { return (noad_options(a) & r) == r; }
+
+inline int has_noad_no_script_option(halfword n, halfword option)
+{
+ switch (node_type(n)) {
+ case simple_noad:
+ case accent_noad:
+ case radical_noad:
+ case fence_noad:
+ case fraction_noad:
+ return has_option(noad_options(n), option) || has_option(noad_options(n), noad_option_no_script);
+ }
+ return 0;
+}
+
+# define has_noad_option_nosubscript(a) has_noad_no_script_option(a, noad_option_no_sub_script)
+# define has_noad_option_nosupscript(a) has_noad_no_script_option(a, noad_option_no_super_script)
+# define has_noad_option_nosubprescript(a) has_noad_no_script_option(a, noad_option_no_sub_pre_script)
+# define has_noad_option_nosupprescript(a) has_noad_no_script_option(a, noad_option_no_super_pre_script)
+
+# define has_noad_option_shiftedsubscript(a) (has_option(noad_options(a), noad_option_shifted_sub_script))
+# define has_noad_option_shiftedsupscript(a) (has_option(noad_options(a), noad_option_shifted_super_script))
+# define has_noad_option_shiftedsubprescript(a) (has_option(noad_options(a), noad_option_shifted_sub_pre_script))
+# define has_noad_option_shiftedsupprescript(a) (has_option(noad_options(a), noad_option_shifted_super_pre_script))
+# define has_noad_option_axis(a) (has_option(noad_options(a), noad_option_axis))
+# define has_noad_option_exact(a) (has_option(noad_options(a), noad_option_exact))
+# define has_noad_option_noaxis(a) (has_option(noad_options(a), noad_option_no_axis))
+# define has_noad_option_openupheight(a) (has_option(noad_options(a), noad_option_openup_height))
+# define has_noad_option_openupdepth(a) (has_option(noad_options(a), noad_option_openup_depth))
+# define has_noad_option_adapttoleft(a) (has_option(noad_options(a), noad_option_adapt_to_left_size))
+# define has_noad_option_adapttoright(a) (has_option(noad_options(a), noad_option_adapt_to_right_size))
+# define has_noad_option_limits(a) (has_option(noad_options(a), noad_option_limits))
+# define has_noad_option_nolimits(a) (has_option(noad_options(a), noad_option_no_limits))
+# define has_noad_option_nooverflow(a) (has_option(noad_options(a), noad_option_no_overflow))
+# define has_noad_option_preferfontthickness(a) (has_option(noad_options(a), noad_option_prefer_font_thickness))
+# define has_noad_option_noruling(a) (has_option(noad_options(a), noad_option_no_ruling))
+# define has_noad_option_unpacklist(a) (has_option(noad_options(a), noad_option_unpack_list))
+# define has_noad_option_nocheck(a) (has_option(noad_options(a), noad_option_no_check))
+# define has_noad_option_exact(a) (has_option(noad_options(a), noad_option_exact))
+# define has_noad_option_left(a) (has_option(noad_options(a), noad_option_left))
+# define has_noad_option_middle(a) (has_option(noad_options(a), noad_option_middle))
+# define has_noad_option_right(a) (has_option(noad_options(a), noad_option_right))
+# define has_noad_option_auto(a) (has_option(noad_options(a), noad_option_auto))
+# define has_noad_option_phantom(a) (has_option(noad_options(a), noad_option_phantom))
+# define has_noad_option_void(a) (has_option(noad_options(a), noad_option_void))
+# define has_noad_option_unrolllist(a) (has_option(noad_options(a), noad_option_unroll_list))
+# define has_noad_option_followedbyspace(a) (has_option(noad_options(a), noad_option_followed_by_space))
+
+/*tex
+ In the meantime the codes and subtypes are in sync. The variable component does not really
+ become a subtype.
+*/
+
+typedef enum simple_noad_subtypes {
+ ordinary_noad_subtype,
+ operator_noad_subtype,
+ binary_noad_subtype,
+ relation_noad_subtype,
+ open_noad_subtype,
+ close_noad_subtype,
+ punctuation_noad_subtype,
+ variable_noad_subtype, /* we want to run in parallel */
+ active_noad_subtype, /* we want to run in parallel */
+ inner_noad_subtype,
+ under_noad_subtype,
+ over_noad_subtype,
+ fraction_noad_subtype,
+ radical_noad_subtype,
+ middle_noad_subtype,
+ accent_noad_subtype,
+ fenced_noad_subtype,
+ ghost_noad_subtype,
+ vcenter_noad_subtype,
+} simple_noad_subtypes;
+
+# define last_noad_type vcenter_noad_subtype
+# define last_noad_subtype vcenter_noad_subtype
+
+typedef enum math_component_types {
+ math_component_ordinary_code,
+ math_component_operator_code,
+ math_component_binary_code,
+ math_component_relation_code,
+ math_component_open_code,
+ math_component_close_code,
+ math_component_punctuation_code,
+ math_component_variable_code,
+ math_component_inner_code,
+ math_component_under_code,
+ math_component_over_code,
+ math_component_fraction_code,
+ math_component_radical_code,
+ math_component_middle_code,
+ math_component_accent_code,
+ math_component_fenced_code,
+ math_component_ghost_code,
+ math_component_atom_code,
+} math_component_types;
+
+# define first_math_component_type math_component_ordinary_code
+# define last_math_component_type math_component_accent_code
+
+/*tex
+ When I added adapt options, the |math_limits_cmd| became |math_modifier_cmd| just because it
+ nicely fits in there.
+*/
+
+typedef enum math_modifier_types {
+ display_limits_modifier_code,
+ limits_modifier_code,
+ no_limits_modifier_code,
+ adapt_to_left_modifier_code,
+ adapt_to_right_modifier_code,
+ axis_modifier_code,
+ no_axis_modifier_code,
+ phantom_modifier_code,
+ void_modifier_code,
+ source_modifier_code,
+ openup_height_modifier_code,
+ openup_depth_modifier_code,
+} math_modifier_types;
+
+# define first_math_modifier_code display_limits_modifier_code
+# define last_math_modifier_code openup_depth_modifier_code
+
+/*tex accent noads: todo, left and right offsets and options */
+
+# define accent_noad_size noad_size
+# define accent_top_character noad_extra_1 /*tex the |top_accent_chr| field of an accent noad */
+# define accent_bottom_character noad_extra_2 /*tex the |bot_accent_chr| field of an accent noad */
+# define accent_middle_character noad_extra_3 /*tex the |overlay_accent_chr| field of an accent noad */
+# define accent_fraction noad_extra_4
+# define accent_top_overshoot noad_extra_5
+# define accent_bot_overshoot noad_extra_6
+
+typedef enum math_accent_subtypes {
+ bothflexible_accent_subtype,
+ fixedtop_accent_subtype,
+ fixedbottom_accent_subtype,
+ fixedboth_accent_subtype,
+} math_accent_subtypes;
+
+# define last_accent_subtype fixedboth_accent_subtype
+
+/*tex
+ With these left and right fencing noads we have a historical mix of |fence| and |delimiter| (and
+ |shield|) naming which for now we keep. It gets swapped with the generic noad, so size matters.
+ */
+
+# define fence_noad_size noad_size
+# define fence_delimiter_list noad_extra_1 // not really a list
+# define fence_delimiter_top noad_extra_3
+# define fence_delimiter_bottom noad_extra_4
+//define fence_delimiter_first noad_extra_5
+//define fence_delimiter_last noad_extra_6
+
+typedef enum fence_subtypes {
+ unset_fence_side,
+ left_fence_side,
+ middle_fence_side,
+ right_fence_side,
+ left_operator_side,
+ no_fence_side,
+ extended_left_fence_side,
+ extended_middle_fence_side,
+ extended_right_fence_side,
+} fence_subtypes;
+
+# define last_fence_subtype extended_right_fence_side
+# define first_fence_code left_fence_side
+# define last_fence_code extended_right_fence_side
+
+/*tex
+ Fraction noads are generic in the sense that they are also used for non|-|fractions, not that
+ it matters much. We keep them as they are in \TEX\ but have more fields.
+
+ We put the numerator and denomerator in script fields so there can be no such direct scripts
+ attached. Because we have prescripts we can used these fields and limit this handicap a bit but
+ if we ever overcome this (at the cost of more fields in these similar noads) we need to adapt
+ the error message for double scripts in |tex_run_math_script|.
+
+*/
+
+# define fraction_noad_size noad_size
+# define fraction_numerator noad_supprescr /* ! */
+# define fraction_denominator noad_subprescr /* ! */
+# define fraction_rule_thickness noad_extra_1
+# define fraction_left_delimiter noad_extra_2
+# define fraction_right_delimiter noad_extra_3
+# define fraction_middle_delimiter noad_extra_4
+# define fraction_h_factor noad_extra_5
+# define fraction_v_factor noad_extra_6
+
+typedef enum fraction_subtypes {
+ over_fraction_subtype,
+ atop_fraction_subtype,
+ above_fraction_subtype,
+ skewed_fraction_subtype,
+ stretched_fraction_subtype,
+} fraction_subtypes;
+
+# define valid_fraction_subtype(s) (s >= over_fraction_subtype && s <= stretched_fraction_subtype)
+
+/*tex
+ Radical noads are like fraction noads, but they only store a |left_delimiter|. They are also
+ used for extensibles (over, under, etc) so the name is is somewhat confusing.
+*/
+
+# define radical_noad_size noad_size
+# define radical_degree noad_extra_1
+# define radical_left_delimiter noad_extra_2
+# define radical_right_delimiter noad_extra_3
+# define radical_height noad_extra_5
+# define radical_depth noad_extra_6
+
+typedef enum radical_subtypes {
+ normal_radical_subtype,
+ radical_radical_subtype,
+ root_radical_subtype,
+ rooted_radical_subtype,
+ under_delimiter_radical_subtype,
+ over_delimiter_radical_subtype,
+ delimiter_under_radical_subtype,
+ delimiter_over_radical_subtype,
+ delimited_radical_subtype,
+ h_extensible_radical_subtype,
+} radical_subtypes;
+
+# define last_radical_subtype h_extensible_radical_subtype
+# define last_radical_code h_extensible_radical_subtype
+
+/*tex
+ Again a very simple small node: it represents a math character so naturally it has a family.
+ It can be turned list. These are subnodes. When an extra options field gets added, the
+ overlapping character and list fields can be split, so then we also have the origin saved.
+
+ The following nodes are kernel nodes: |math_char_node|, |math_text_char_node|, |sub_box_node|
+ and |sub_mlist_node|. Characters eventually becomes wrapped in a list.
+*/
+
+typedef enum math_kernel_options {
+ math_kernel_no_italic_correction = 0x0001,
+ math_kernel_no_left_pair_kern = 0x0002,
+ math_kernel_no_right_pair_kern = 0x0004,
+ math_kernel_auto_discretionary = 0x0008,
+ math_kernel_full_discretionary = 0x0010,
+} math_kernel_options;
+
+# define math_kernel_node_size 5
+# define kernel_math_family(a) vinfo(a,2)
+# define kernel_math_character(a) vlink(a,2)
+# define kernel_math_options(a) vinfo(a,3)
+# define kernel_math_list(a) vlink(a,3)
+# define kernel_math_properties(a) vinfo0(a,4) /* for characters */
+# define kernel_math_group(a) vinfo1(a,4) /* for characters */
+# define kernel_math_index(a) vlink(a,4) /* for characters */
+
+# define math_kernel_node_has_option(a,b) ((kernel_math_options(a) & b) == b)
+# define math_kernel_node_set_option(a,b) kernel_math_options(a) = (kernel_math_options(a) | b)
+
+/*tex
+ This is also a subnode, this time for a delimiter field. The large family field is only used
+ in traditional \TEX\ fonts where a base character can come from one font, and the extensible
+ from another, but in \OPENTYPE\ math font that doesn't happen.
+*/
+
+# define math_delimiter_node_size 4
+# define delimiter_small_family(a) vinfo(a,2) /*tex |family| for small delimiter */
+# define delimiter_small_character(a) vlink(a,2) /*tex |character| for small delimiter */
+# define delimiter_large_family(a) vinfo(a,3) /*tex |family| for large delimiter */
+# define delimiter_large_character(a) vlink(a,3) /*tex |character| for large delimiter */
+
+/*tex
+ Before we come to the by now rather large local par node we define some small ones. The
+ boundary nodes are an extended version of the original ones. The direction nodes are
+ a simplified version of what \OMEGA\ has as whatsit. In \LUATEX\ it became a first class
+ citizen and in \LUAMETATEX\ we cleaned it up.
+*/
+
+typedef enum boundary_subtypes {
+ cancel_boundary,
+ user_boundary,
+ protrusion_boundary,
+ word_boundary,
+ page_boundary,
+ par_boundary,
+} boundary_subtypes;
+
+# define last_boundary_subtype word_boundary
+# define last_boundary_code page_boundary
+
+# define boundary_node_size 3
+# define boundary_data(a) vinfo(a,2)
+
+typedef enum dir_subtypes {
+ normal_dir_subtype,
+ cancel_dir_subtype,
+} dir_subtypes;
+
+# define last_dir_subtype cancel_dir_subtype
+
+# define dir_node_size 3
+# define dir_direction(a) vinfo(a,2)
+# define dir_level(a) vlink(a,2)
+
+/*tex
+ Local par nodes come from \OMEGA\ and store the direction as well as local boxes. In \LUATEX
+ we use a leaner direction model and in \LUAMETATEX\ we only kept the two directions that just
+ work. In the end it is the backend that deals with these properties. The frontend just keeps
+ a little track of them.
+
+ However, in \LUAMETATEX\ we can also store the paragraph state in this node. That way we no
+ longer have the issue that properties are lost when a group ends before a |\par| is triggered.
+ This is probably a feature that only makes sense in \CONTEXT\ which is why I made sure that
+ there is not much overhead. In the first version one could control each variable, but as we
+ ran out of bits in the end was done per group of variables. However, when I really need more
+ detail I might go for a 64 bit field instead. After all we have that possibility in memory
+ words.
+
+ These local par nodes can actually end up in the middle of lines as they can be used to change
+ the left and right box as well as inject penalties. For that reason they now have a proper
+ subtype so that the initial and successive instances can be recognized.
+ */
+
+typedef enum par_codes {
+ par_none_code,
+ par_hsize_code,
+ par_left_skip_code,
+ par_right_skip_code,
+ par_hang_indent_code,
+ par_hang_after_code,
+ par_par_indent_code,
+ par_par_fill_left_skip_code,
+ par_par_fill_right_skip_code,
+ par_par_init_left_skip_code,
+ par_par_init_right_skip_code,
+ par_adjust_spacing_code,
+ par_protrude_chars_code,
+ par_pre_tolerance_code,
+ par_tolerance_code,
+ par_emergency_stretch_code,
+ par_looseness_code,
+ par_last_line_fit_code,
+ par_line_penalty_code,
+ par_inter_line_penalty_code,
+ par_club_penalty_code,
+ par_widow_penalty_code,
+ par_display_widow_penalty_code,
+ par_orphan_penalty_code,
+ par_broken_penalty_code,
+ par_adj_demerits_code,
+ par_double_hyphen_demerits_code,
+ par_final_hyphen_demerits_code,
+ par_par_shape_code,
+ par_inter_line_penalties_code,
+ par_club_penalties_code,
+ par_widow_penalties_code,
+ par_display_widow_penalties_code,
+ par_orphan_penalties_code,
+ par_baseline_skip_code,
+ par_line_skip_code,
+ par_line_skip_limit_code,
+ par_adjust_spacing_step_code,
+ par_adjust_spacing_shrink_code,
+ par_adjust_spacing_stretch_code,
+ par_hyphenation_mode_code,
+ par_shaping_penalties_mode_code,
+ par_shaping_penalty_code,
+} par_codes;
+
+typedef enum par_categories {
+ par_none_category = 0x00000000,
+ par_hsize_category = 0x00000001, // \hsize
+ par_skip_category = 0x00000002, // \leftskip \rightskip
+ par_hang_category = 0x00000004, // \hangindent \hangafter
+ par_indent_category = 0x00000008, // \parindent
+ par_par_fill_category = 0x00000010, // \parfillskip \parfillleftskip
+ par_adjust_category = 0x00000020, // \adjustspacing
+ par_protrude_category = 0x00000040, // \protrudechars
+ par_tolerance_category = 0x00000080, // \tolerance \pretolerance
+ par_stretch_category = 0x00000100, // \emergcystretch
+ par_looseness_category = 0x00000200, // \looseness
+ par_last_line_category = 0x00000400, // \lastlinefit
+ par_line_penalty_category = 0x00000800, // \linepenalty \interlinepenalty \interlinepenalties
+ par_club_penalty_category = 0x00001000, // \clubpenalty \clubpenalties
+ par_widow_penalty_category = 0x00002000, // \widowpenalty \widowpenalties
+ par_display_penalty_category = 0x00004000, // \displaypenalty \displaypenalties
+ par_broken_penalty_category = 0x00008000, // \brokenpenalty
+ par_demerits_category = 0x00010000, // \doublehyphendemerits \finalhyphendemerits \adjdemerits
+ par_shape_category = 0x00020000, // \parshape
+ par_line_category = 0x00040000, // \baselineskip \lineskip \lineskiplimit
+ par_hyphenation_category = 0x00080000, // \Hyphenationmode
+ par_shaping_penalty_category = 0x00100000, // \shapingpenaltiesmode
+ par_orphan_penalty_category = 0x00200000, // \orphanpenalties
+ par_all_category = 0x7FFFFFFF, //
+} par_categories;
+
+static int par_category_to_codes[] = {
+ par_none_category,
+ par_hsize_category, // par_hsize_code
+ par_skip_category, // par_left_skip_code
+ par_skip_category, // par_right_skip_code
+ par_hang_category, // par_hang_indent_code
+ par_hang_category, // par_hang_after_code
+ par_indent_category, // par_par_indent_code
+ par_par_fill_category, // par_par_fill_skip_code
+ par_par_fill_category, // par_par_fill_left_skip_code
+ par_par_fill_category, // par_par_init_skip_code
+ par_par_fill_category, // par_par_init_skip_code
+ par_adjust_category, // par_adjust_spacing_code
+ par_protrude_category, // par_protrude_chars_code
+ par_tolerance_category, // par_pre_tolerance_code
+ par_tolerance_category, // par_tolerance_code
+ par_stretch_category, // par_emergency_stretch_code
+ par_looseness_category, // par_looseness_code
+ par_last_line_category, // par_last_line_fit_code
+ par_line_penalty_category, // par_line_penalty_code
+ par_line_penalty_category, // par_inter_line_penalty_code
+ par_club_penalty_category, // par_club_penalty_code
+ par_widow_penalty_category, // par_widow_penalty_code
+ par_display_penalty_category, // par_display_widow_penalty_code
+ par_orphan_penalty_category, // par_orphan_penalty_code
+ par_broken_penalty_category, // par_broken_penalty_code
+ par_demerits_category, // par_adj_demerits_code
+ par_demerits_category, // par_double_hyphen_demerits_code
+ par_demerits_category, // par_final_hyphen_demerits_code
+ par_shape_category, // par_par_shape_code
+ par_line_penalty_category, // par_inter_line_penalties_code
+ par_club_penalty_category, // par_club_penalties_code
+ par_widow_penalty_category, // par_widow_penalties_code
+ par_display_penalty_category, // par_display_widow_penalties_code
+ par_orphan_penalty_category, // par_orphan_penalties_code
+ par_line_category, // par_baseline_skip_code
+ par_line_category, // par_line_skip_code
+ par_line_category, // par_line_skip_limit_code
+ par_adjust_category, // par_adjust_spacing_step_code
+ par_adjust_category, // par_adjust_spacing_shrink_code
+ par_adjust_category, // par_adjust_spacing_stretch_code
+ par_hyphenation_category, // par_hyphenation_mode_code
+ par_shaping_penalty_category, // par_shaping_penalties_mode_code
+ par_shaping_penalty_category, // par_shaping_penalty_code
+};
+
+/*tex
+ Todo: make the fields 6+ into a par_state node so that local box ones can be
+ small. Also, penalty and broken fields now are duplicate. Do we need to keep
+ these?
+*/
+
+# define par_node_size 28
+# define par_penalty_interline(a) vinfo(a,2) /*tex These come from \OMEGA. */
+# define par_penalty_broken(a) vlink(a,2) /*tex These come from \OMEGA. */
+# define par_box_left(a) vinfo(a,3)
+# define par_box_left_width(a) vlink(a,3)
+# define par_box_right(a) vinfo(a,4)
+# define par_box_right_width(a) vlink(a,4)
+# define par_box_middle(a) vinfo(a,5) /* no width here */
+# define par_dir(a) vlink(a,5)
+# define par_state(a) vinfo(a,6)
+# define par_hsize(a) vlink(a,6)
+# define par_left_skip(a) vinfo(a,7)
+# define par_right_skip(a) vlink(a,7)
+# define par_hang_indent(a) vinfo(a,8)
+# define par_hang_after(a) vlink(a,8)
+# define par_par_indent(a) vinfo(a,9)
+# define par_par_fill_left_skip(a) vlink(a,9)
+# define par_par_fill_right_skip(a) vinfo(a,10)
+# define par_adjust_spacing(a) vlink(a,10)
+# define par_protrude_chars(a) vinfo(a,11)
+# define par_pre_tolerance(a) vlink(a,11)
+# define par_tolerance(a) vinfo(a,12)
+# define par_emergency_stretch(a) vlink(a,12)
+# define par_looseness(a) vinfo(a,13)
+# define par_last_line_fit(a) vlink(a,13)
+# define par_line_penalty(a) vinfo(a,14)
+# define par_inter_line_penalty(a) vlink(a,14)
+# define par_club_penalty(a) vinfo(a,15)
+# define par_widow_penalty(a) vlink(a,15)
+# define par_display_widow_penalty(a) vinfo(a,16)
+# define par_orphan_penalty(a) vlink(a,16)
+# define par_broken_penalty(a) vinfo(a,17)
+# define par_adj_demerits(a) vlink(a,17)
+# define par_double_hyphen_demerits(a) vinfo(a,18)
+# define par_final_hyphen_demerits(a) vlink(a,18)
+# define par_par_shape(a) vinfo(a,19)
+# define par_inter_line_penalties(a) vlink(a,19)
+# define par_club_penalties(a) vinfo(a,20)
+# define par_widow_penalties(a) vlink(a,20)
+# define par_display_widow_penalties(a) vinfo(a,21)
+# define par_orphan_penalties(a) vlink(a,21)
+# define par_baseline_skip(a) vinfo(a,22)
+# define par_line_skip(a) vlink(a,22)
+# define par_line_skip_limit(a) vinfo(a,23)
+# define par_adjust_spacing_step(a) vlink(a,23)
+# define par_adjust_spacing_shrink(a) vinfo(a,24)
+# define par_adjust_spacing_stretch(a) vlink(a,24)
+# define par_end_par_tokens(a) vinfo(a,25)
+# define par_hyphenation_mode(a) vlink(a,25)
+# define par_shaping_penalties_mode(a) vinfo(a,26)
+# define par_shaping_penalty(a) vlink(a,26)
+# define par_par_init_left_skip(a) vlink(a,27)
+# define par_par_init_right_skip(a) vinfo(a,27)
+
+typedef enum par_subtypes {
+ vmode_par_par_subtype,
+ local_box_par_subtype,
+ hmode_par_par_subtype,
+ penalty_par_subtype,
+ math_par_subtype,
+} par_subtypes;
+
+# define last_par_subtype math_par_subtype
+
+inline static int tex_is_start_of_par_node(halfword n)
+{
+ return ( n && (node_type(n) == par_node) && (node_subtype(n) == vmode_par_par_subtype || node_subtype(n) == hmode_par_par_subtype) );
+}
+
+extern halfword tex_get_par_par (halfword p, halfword what);
+extern void tex_set_par_par (halfword p, halfword what, halfword v, int force);
+extern void tex_snapshot_par (halfword p, halfword what);
+extern halfword tex_find_par_par (halfword head);
+/* halfword tex_internal_to_par_code (halfword cmd, halfword index); */
+extern void tex_update_par_par (halfword cmd, halfword index);
+
+inline static int tex_par_state_is_set (halfword p, halfword what) { return (par_state(p) & par_category_to_codes[what]) == par_category_to_codes[what]; }
+inline static void tex_set_par_state (halfword p, halfword what) { par_state(p) |= par_category_to_codes[what]; }
+inline static int tex_par_to_be_set (halfword state, halfword what) { return (state & par_category_to_codes[what]) == par_category_to_codes[what]; }
+
+/*tex
+ Because whatsits are used by the backend (or callbacks in the frontend) we do provide this node.
+ It only has the basic properties: subtype, attribute, prev link and next link. User nodes have
+ been dropped because one can use whatsits to achieve the same. We also don't standardize the
+ subtypes as it's very macro package specific what they do. So, only a size here:
+*/
+
+# define whatsit_node_size 2
+
+/*tex
+ Active and passive nodes are used in the par builder. There is plenty of comments in the code
+ that explains them (although it's not that trivial I guess). Delta nodes just store the
+ progression in widths, stretch and shrink: they are copies of arrays. Originally they just used
+ offsets:
+
+ \starttyping
+ # define delta_node_size 10
+ # define delta_field(a,n) node_next(a + n)
+ \stoptyping
+
+ But that wasted 9 halfs for storing the 9 fields. So, next I played with this:
+
+ \starttyping
+ # define delta_field_1(d) (delta_field(d,1)) // or: vinfo(d,1)
+ # define delta_field_2(d) (delta_field(d,2)) // or: vlink(d,1)
+ ...
+ # define delta_field_9(d) (delta_field(d,9)) // or: vinfo(d,5)
+ \stoptyping
+
+ But soon after that more meaningfull names were introduced, simply because in the code where they
+ are used also verbose names showed up.
+
+ The active node is actually a |hyphenated_node| or an |unhyphenated_node| but for now we keep
+ the \TEX\ lingua. We could probably turn the type into a subtype and moev fitness to another
+ spot.
+*/
+
+/* is vinfo(a,2) used? it not we can have fitness there and hyphenated/unyphenates as subtype */
+
+# define active_node_size 4 /*tex |hyphenated_node| or |unhyphenated_node| */
+# define active_fitness node_subtype /*tex |very_loose_fit..tight_fit| on final line for this break */
+# define active_break_node(a) vlink(a,1) /*tex pointer to the corresponding passive node */
+# define active_line_number(a) vinfo(a,1) /*tex line that begins at this breakpoint */
+# define active_total_demerits(a) vlink(a,2) /*tex the quantity that \TEX\ minimizes */
+# define active_short(a) vinfo(a,3) /*tex |shortfall| of this line */
+# define active_glue(a) vlink(a,3) /*tex corresponding glue stretch or shrink */
+
+# define passive_node_size 7
+# define passive_cur_break(a) vlink(a,1) /*tex in passive node, points to position of this breakpoint */
+# define passive_prev_break(a) vinfo(a,1) /*tex points to passive node that should precede this one */
+# define passive_pen_inter(a) vinfo(a,2)
+# define passive_pen_broken(a) vlink(a,2)
+# define passive_left_box(a) vlink(a,3)
+# define passive_left_box_width(a) vinfo(a,3)
+# define passive_last_left_box(a) vlink(a,4)
+# define passive_last_left_box_width(a) vinfo(a,4)
+# define passive_right_box(a) vlink(a,5)
+# define passive_right_box_width(a) vinfo(a,5)
+# define passive_serial(a) vlink(a,6) /*tex serial number for symbolic identification (pass) */
+# define passive_middle_box(a) vinfo(a,6)
+
+# define delta_node_size 6
+# define delta_field_total_glue(d) vinfo(d,1)
+# define delta_field_total_shrink(d) vinfo(d,2)
+# define delta_field_total_stretch(d) vlink(d,2)
+# define delta_field_total_fi_amount(d) vinfo(d,3)
+# define delta_field_total_fil_amount(d) vlink(d,3)
+# define delta_field_total_fill_amount(d) vinfo(d,4)
+# define delta_field_total_filll_amount(d) vlink(d,4)
+# define delta_field_font_shrink(d) vinfo(d,5)
+# define delta_field_font_stretch(d) vlink(d,5)
+
+/*tex
+ Again we now have some helpers. We have a double linked list so here we go:
+*/
+
+inline static void tex_couple_nodes(int a, int b)
+{
+ node_next(a) = b;
+ node_prev(b) = a;
+}
+
+inline static void tex_try_couple_nodes(int a, int b)
+{
+ if (b) {
+ if (a) {
+ node_next(a) = b;
+ }
+ node_prev(b) = a;
+ } else if (a) {
+ node_next(a) = null;
+ }
+}
+
+inline static void tex_uncouple_node(int a)
+{
+ node_next(a) = null;
+ node_prev(a) = null;
+}
+
+inline static halfword tex_head_of_node_list(halfword n)
+{
+ while (node_prev(n)) {
+ n = node_prev(n);
+ }
+ return n;
+}
+
+inline static halfword tex_tail_of_node_list(halfword n)
+{
+ while (node_next(n)) {
+ n = node_next(n);
+ }
+ return n;
+}
+
+/*tex
+ Attribute management is kind of complicated. They are stored in a sorted linked list and we
+ try to share these for successive nodes. In \LUATEX\ a state is kept and reset frequently but
+ in \LUAMETATEX\ we try to be more clever, for instance we keep track of grouping. This comes
+ as some overhead but saves reconstructing (often the same) list. It also saves memory.
+*/
+
+# define attribute_cache_disabled max_halfword
+# define current_attribute_state lmt_node_memory_state.attribute_cache
+
+extern halfword tex_copy_attribute_list (halfword attr);
+extern halfword tex_copy_attribute_list_set (halfword attr, int index, int value);
+extern halfword tex_patch_attribute_list (halfword attr, int index, int value);
+extern void tex_dereference_attribute_list (halfword attr);
+extern void tex_build_attribute_list (halfword target);
+extern halfword tex_current_attribute_list (void);
+extern int tex_unset_attribute (halfword target, int index, int value);
+extern void tex_unset_attributes (halfword first, halfword last, int index);
+extern void tex_set_attribute (halfword target, int index, int value);
+extern int tex_has_attribute (halfword target, int index, int value);
+
+extern void tex_reset_node_properties (halfword target);
+
+# define get_attribute_list(target) \
+ node_attr(target)
+
+# define add_attribute_reference(a) do { \
+ if (a && a != attribute_cache_disabled) { \
+ ++attribute_count(a); \
+ } \
+} while (0)
+
+# define delete_attribute_reference(a) do { \
+ if (a && a != attribute_cache_disabled) { \
+ tex_dereference_attribute_list(a); \
+ } \
+} while (0)
+
+# define remove_attribute_list(target) do { \
+ halfword old_a = node_attr(target); \
+ delete_attribute_reference(old_a); \
+ node_attr(target) = null; \
+} while (0)
+
+/*
+inline static void remove_attribute_list(halfword target)
+{
+ halfword a_old = node_attr(target);
+ if (a_old && a_old != attribute_cache_disabled) {
+ dereference_attribute_list(a_old);
+ }
+ node_attr(target) = null;
+}
+*/
+
+/* This can be dangerous: */
+
+# define wipe_attribute_list_only(target) \
+ node_attr(target) = null;
+
+/*tex
+ Better is to add a ref before we remove one because there's the danger of premature freeing
+ otherwise.
+*/
+
+typedef enum saved_attribute_items {
+ saved_attribute_item_list = 0,
+ saved_attribute_n_of_items = 1,
+} saved_attribute_items;
+
+inline static void tex_attach_attribute_list_copy(halfword target, halfword source)
+{
+ halfword a_new = node_attr(source);
+ halfword a_old = node_attr(target);
+ node_attr(target) = a_new;
+ add_attribute_reference(a_new);
+ delete_attribute_reference(a_old);
+}
+
+inline static void tex_attach_attribute_list_attribute(halfword target, halfword a_new)
+{
+ halfword a_old = node_attr(target);
+ if (a_old != a_new) {
+ node_attr(target) = a_new;
+ add_attribute_reference(a_new);
+ delete_attribute_reference(a_old);
+ }
+}
+
+# define attach_current_attribute_list tex_build_attribute_list /* (target) */
+
+# define set_current_attribute_state(v) do { \
+ current_attribute_state = v; \
+} while (0)
+
+# define change_attribute_register(a,id,value) do { \
+ if (eq_value(id) != value) { \
+ if (is_global(a)) { \
+ int i; \
+ for (i = (lmt_save_state.save_stack_data.ptr - 1); i >= 0; i--) { \
+ if (save_type(i) == saved_attribute_list) { \
+ delete_attribute_reference(save_value(i)); \
+ save_value(i) = attribute_cache_disabled; \
+ } \
+ } \
+ } else { \
+ delete_attribute_reference(current_attribute_state); \
+ } \
+ set_current_attribute_state(attribute_cache_disabled); \
+ } \
+} while (0)
+
+# define save_attribute_state_before() do { \
+ halfword c = current_attribute_state; \
+ tex_set_saved_record(saved_attribute_item_list, saved_attribute_list, 0, c); \
+ lmt_save_state.save_stack_data.ptr += saved_attribute_n_of_items; \
+ add_attribute_reference(c); \
+} while (0)
+
+# define save_attribute_state_after() do { \
+} while (0)
+
+# define unsave_attribute_state_before() do { \
+ halfword c = current_attribute_state; \
+ delete_attribute_reference(c); \
+} while (0)
+
+# define unsave_attribute_state_after() do { \
+ lmt_save_state.save_stack_data.ptr -= saved_attribute_n_of_items; \
+ set_current_attribute_state(saved_value(saved_attribute_item_list)); \
+} while (0)
+
+/*tex
+ We now arrive at some functions that report the nodes to users. The subtype information that
+ is used in the \LUA\ interface is stored alongside.
+*/
+
+extern void tex_print_short_node_contents (halfword n);
+extern void tex_show_node_list (halfword n, int threshold, int max);
+extern halfword tex_actual_box_width (halfword r, scaled base_width);
+extern void tex_print_name (halfword p, const char *what);
+extern void tex_print_node_list (halfword n, const char *what, int threshold, int max);
+/* void tex_print_node_and_details (halfword p); */
+/* void tex_print_subtype_and_attributes_info (halfword p, halfword s, node_info *data); */
+extern void tex_print_extended_subtype (halfword p, quarterword s);
+extern void tex_aux_show_dictionary (halfword p, halfword properties, halfword group, halfword index, halfword font, halfword character);
+
+extern halfword tex_new_node (quarterword i, quarterword j);
+extern void tex_flush_node_list (halfword n);
+extern void tex_flush_node (halfword n);
+extern halfword tex_copy_node_list (halfword n, halfword e);
+extern halfword tex_copy_node (halfword n);
+extern halfword tex_copy_node_only (halfword n);
+/* halfword tex_fix_node_list (halfword n); */
+
+/*tex
+ We already defined glue and gluespec node but here are some of the properties
+ that they have. Again a few helpers.
+*/
+
+typedef enum glue_orders {
+ normal_glue_order,
+ fi_glue_order,
+ fil_glue_order,
+ fill_glue_order,
+ filll_glue_order
+} glue_orders;
+
+typedef enum glue_amounts {
+ /* we waste slot zero, we padd anyway */
+ total_glue_amount = 1, // 1 //
+ total_stretch_amount = 2, // 3 //
+ total_fi_amount = 3, // 4 //
+ total_fil_amount = 4, // 5 //
+ total_fill_amount = 5, // 6 //
+ total_filll_amount = 6, // 7 //
+ total_shrink_amount = 7, // 2 //
+ font_stretch_amount = 8, // 8 //
+ font_shrink_amount = 9, // 9 //
+} glue_amounts;
+
+# define min_glue_order normal_glue_order
+# define max_glue_order filll_glue_order
+
+typedef enum glue_signs {
+ normal_glue_sign,
+ stretching_glue_sign,
+ shrinking_glue_sign
+} glue_signs;
+
+# define min_glue_sign normal_glue_sign
+# define max_glue_sign shrinking_glue_sign
+
+# define normal_glue_multiplier 0.0
+
+inline halfword tex_checked_glue_sign(halfword sign)
+{
+ if ((sign < min_glue_sign) || (sign > max_glue_sign)) {
+ return normal_glue_sign;
+ } else {
+ return sign;
+ }
+}
+
+inline halfword tex_checked_glue_order(halfword order)
+{
+ if ((order < min_glue_order) || (order > max_glue_order)) {
+ return normal_glue_order;
+ } else {
+ return order;
+ }
+}
+
+/*tex
+ These are reserved nodes that sit at the start of main memory. We could actually just allocate
+ them, but then we also need to set some when we start up. Now they are just saved in the format
+ file. In \TEX\ these nodes were shared as much as possible (using a reference count) but here
+ we just use copies.
+
+ Below we start at |zero_glue| which in our case is just 0, or |null| in \TEX\ speak. After these
+ reserved nodes the memory used for whatever nodes are needed takes off.
+
+ Changing this to real nodes makes sense but is also tricky due to initializations ... some day
+ (we need to store stuff in teh states then and these are not saved!).
+
+
+*/
+
+# define fi_glue (zero_glue + glue_spec_size) /*tex These are constants */
+# define fil_glue (fi_glue + glue_spec_size)
+# define fill_glue (fil_glue + glue_spec_size)
+# define filll_glue (fill_glue + glue_spec_size)
+# define fil_neg_glue (filll_glue + glue_spec_size)
+
+# define page_insert_head (fil_neg_glue + glue_spec_size)
+# define contribute_head (page_insert_head + split_node_size) /*tex This was temp_node_size but we assign more. */
+# define page_head (contribute_head + temp_node_size)
+# define temp_head (page_head + glue_node_size) /*tex It gets a glue type assigned. */
+# define hold_head (temp_head + temp_node_size)
+# define post_adjust_head (hold_head + temp_node_size)
+# define pre_adjust_head (post_adjust_head + temp_node_size)
+# define post_migrate_head (pre_adjust_head + temp_node_size)
+# define pre_migrate_head (post_migrate_head + temp_node_size)
+# define align_head (pre_migrate_head + temp_node_size)
+# define active_head (align_head + temp_node_size)
+# define end_span (active_head + active_node_size)
+# define begin_period (end_span + span_node_size) /*tex Used to mark begin of word in hjn. */
+# define end_period (begin_period + glyph_node_size) /*tex Used to mark end of word in hjn. */
+
+# define last_reserved (end_period + glyph_node_size - 1)
+
+/*tex More helpers! */
+
+extern int tex_list_has_glyph (halfword list);
+
+extern halfword tex_new_null_box_node (quarterword type, quarterword subtype);
+extern halfword tex_new_rule_node (quarterword subtype);
+extern halfword tex_new_glyph_node (quarterword subtype, halfword fnt, halfword chr, halfword parent); /*tex afterwards: when we mess around */
+extern halfword tex_new_char_node (quarterword subtype, halfword fnt, halfword chr, int all); /*tex as we go: in maincontrol */
+extern halfword tex_new_text_glyph (halfword fnt, halfword chr);
+extern halfword tex_new_disc_node (quarterword subtype);
+extern halfword tex_new_glue_spec_node (halfword param);
+extern halfword tex_new_param_glue_node (quarterword param, quarterword subtype);
+extern halfword tex_new_glue_node (halfword qlue, quarterword subtype);
+extern halfword tex_new_kern_node (scaled width, quarterword subtype);
+extern halfword tex_new_penalty_node (halfword penalty, quarterword subtype);
+extern halfword tex_new_par_node (quarterword mode);
+
+extern halfword tex_new_temp_node (void);
+
+extern scaled tex_glyph_width (halfword p); /* x/y scaled */
+extern scaled tex_glyph_height (halfword p); /* x/y scaled */
+extern scaled tex_glyph_depth (halfword p); /* x/y scaled */
+extern scaled tex_glyph_total (halfword p); /* x/y scaled */
+extern scaledwhd tex_glyph_dimensions (halfword p); /* x/y scaled */
+extern int tex_glyph_has_dimensions (halfword p); /* x/y scaled */
+extern scaled tex_glyph_width_ex (halfword p); /* x/y scaled, expansion included */
+extern scaledwhd tex_glyph_dimensions_ex (halfword p); /* x/y scaled, expansion included */
+
+extern halfword tex_kern_dimension (halfword p);
+extern halfword tex_kern_dimension_ex (halfword p); /* expansion included */
+
+extern scaledwhd tex_pack_dimensions (halfword p);
+
+extern halfword tex_list_node_mem_usage (void);
+extern halfword tex_reversed_node_list (halfword list);
+extern int tex_n_of_used_nodes (int counts[]);
+
+# define _valid_node_(p) ((p > lmt_node_memory_state.reserved) && (p < lmt_node_memory_state.nodes_data.allocated) && (lmt_node_memory_state.nodesizes[p] > 0))
+
+inline static int tex_valid_node(halfword n)
+{
+ return n && _valid_node_(n) ? n : null;
+}
+
+/*tex This is a bit strange place but better than a macro elsewhere: */
+
+inline static int tex_math_skip_boundary(halfword n)
+{
+ return (n && node_type(n) == glue_node
+ && (node_subtype(n) == space_skip_glue ||
+ node_subtype(n) == xspace_skip_glue ||
+ node_subtype(n) == zero_space_skip_glue));
+}
+
+typedef enum special_node_list_types { /* not in sycn with the above .. maybe add bogus ones */
+ page_insert_list_type,
+ contribute_list_type,
+ page_list_type,
+ temp_list_type,
+ hold_list_type,
+ post_adjust_list_type,
+ pre_adjust_list_type,
+ post_migrate_list_type,
+ pre_migrate_list_type,
+ align_list_type,
+ /* in different spot */
+ page_discards_list_type,
+ split_discards_list_type,
+ // best_page_break_type
+} special_node_list_types;
+
+extern int tex_is_special_node_list (halfword n, int *istail);
+extern halfword tex_get_special_node_list (special_node_list_types list, halfword *tail);
+extern void tex_set_special_node_list (special_node_list_types list, halfword head);
+
+extern scaled tex_effective_glue (halfword parent, halfword glue);
+
+extern const char *tex_aux_subtype_str (halfword n );
+
+# endif
+