diff options
Diffstat (limited to 'source/luametatex/source/tex/texmath.c')
-rw-r--r-- | source/luametatex/source/tex/texmath.c | 5593 |
1 files changed, 5593 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texmath.c b/source/luametatex/source/tex/texmath.c new file mode 100644 index 000000000..d38cbf182 --- /dev/null +++ b/source/luametatex/source/tex/texmath.c @@ -0,0 +1,5593 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" + +/*tex + + The code can be simplified a lot when we decide that traditional 8 bit fonts are virtualized in + a way that avoids the split delimiter definitions (small and large) and that the traditional + way to define characters is dropped in favor of the unicode variants. So, this might happen at + some point. After all it makes no sense to use this engine with traditional fonts because there + \PDFTEX\ is a better choice. + + We might also benefit more from the fact that we have prev pointers. Occasionally I visit this + file and make some variables more verbose. I'm in no hurry with that. + +*/ + +/*tex + + When \TEX\ reads a formula that is enclosed between |$|'s, it constructs an \quote {mlist}, + which is essentially a tree structure representing that formula. An mlist is a linear sequence + of items, but we can regard it as a tree structure because mlists can appear within mlists. For + example, many of the entries can be subscripted or superscripted, and such \quote {scripts} are + mlists in their own right. + + An entire formula is parsed into such a tree before any of the actual typesetting is done, + because the current style of type is usually not known until the formula has been fully scanned. + For example, when the formula |$a+b \over c+d$| is being read, there is no way to tell that |a+b| + will be in script size until |\over| has appeared. + + During the scanning process, each element of the mlist being built is classified as a relation, + a binary operator, an open parenthesis, etc., or as a construct like |\sqrt| that must be built + up. This classification appears in the mlist data structure. + + After a formula has been fully scanned, the mlist is converted to an hlist so that it can be + incorporated into the surrounding text. This conversion is controlled by a recursive procedure + that decides all of the appropriate styles by a \quote {top-down} process starting at the + outermost level and working in towards the subformulas. The formula is ultimately pasted together + using combinations of horizontal and vertical boxes, with glue and penalty nodes inserted as + necessary. + + An mlist is represented internally as a linked list consisting chiefly of \quote {noads} + (pronounced \quotation {no-adds}), to distinguish them from the somewhat similar \quote {nodes} + in hlists and vlists. Certain kinds of ordinary nodes are allowed to appear in mlists together + with the noads; \TEX\ tells the difference by means of the |type| field, since a noad's |type| + is always greater than that of a node. An mlist does not contain character nodes, hlist nodes, + vlist nodes, math nodes or unset nodes; in particular, each mlist item appears in the + variable-size part of |mem|, so the |type| field is always present. + + Each noad is five or more words long. The first word contains the |type| and |subtype| and |link| + fields that are already so familiar to us; the second contains the attribute list pointer, and + the third, fourth an fifth words are called the noad's |nucleus|, |subscr|, and |supscr| fields. + (This use of a combined attribute list is temporary. Eventually, each of fields need their own + list) + + Consider, for example, the simple formula |$x^2$|, which would be parsed into an mlist containing + a single element called an |ord_noad|. The |nucleus| of this noad is a representation of |x|, the + |subscr| is empty, and the |supscr| is a representation of |2|. + + The |nucleus|, |subscr|, and |supscr| fields are further broken into subfields. If |p| points to + a noad, and if |q| is one of its principal fields (e.g., |q=subscr(p)|), |q=null| indicates a + field with no value (the corresponding attribute of noad |p| is not present). Otherwise, there are + several possibilities for the subfields, depending on the |type| of |q|. + + \startitemize + + \startitem + |type(q)=math_char_node| means that |math_fam(q)| refers to one of the sixteen font + families, and |character(q)| is the number of a character within a font of that family, as + in a character node. + \stopitem + + \startitem + |type(q) = math_text_char_node| is similar, but the character is unsubscripted and + unsuperscripted and it is followed immediately by another character from the same font. + (This |type| setting appears only briefly during the processing; it is used to suppress + unwanted italic corrections.) + \stopitem + + \startitem + |type(q) = sub_box_node| means that |math_list(q)| points to a box node (either an + |hlist_node| or a |vlist_node|) that should be used as the value of the field. The + |shift_amount| in the subsidiary box node is the amount by which that box will be + shifted downward. + \stopitem + + \startitem + |type(q) = sub_mlist_node| means that |math_list(q)| points to an mlist; the mlist must + be converted to an hlist in order to obtain the value of this field. + \stopitem + + \startitem + In the latter case, we might have |math_list(q) = null|. This is not the same as |q = + null|; for example, |$P_{\}$| and |$P$| produce different results (the former will not + have the \quote {italic correction} added to the width of |P|, but the \quote {script + skip} will be added). + \stopitem + + \startitemize + + Concerning display skips, \TEX\ normally always inserts before and only after when larger than + zero. This can now be controlled with |\mathdisplayskipmode|: + + \starttabulate + \NC 0 \NC normal \TEX \NC \NR + \NC 1 \NC always \NC \NR + \NC 2 \NC non-zero \NC \NR + \NC 3 \NC ignore \NC \NR + \stoptabulate + +*/ + +math_state_info lmt_math_state = { + .size = 0, + .level = 0, + /* .opentype = 1, */ + /* .padding = 0, */ + .par_head = NULL, + .fam_head = NULL, + .last_left = 0, + .last_right = 0, + .last_atom = 0, + .scale = 1000, +}; + +static int tex_aux_scan_math (halfword p, halfword style, int usetextfont, halfword toks, halfword toks_text, int nocomponent, halfword cls, halfword all); +static halfword tex_aux_finish_math_list (halfword p); +static void tex_aux_math_math_component (halfword n, int append); + +# define cramped 1 + +# define cramped_style(A) (2 * ((A) / 2) + cramped) /*tex cramp the style */ +# define sub_style(A) (2 * ((A) / 4) + script_style + cramped) /*tex smaller and cramped */ +# define sup_style(A) (2 * ((A) / 4) + script_style + ((A) % 2)) /*tex smaller */ +# define num_style(A) ((A) + 2 - 2 * ((A) / 6)) /*tex smaller unless already scriptscript */ +# define denom_style(A) (2 * ((A) / 2) + cramped + 2 - 2 * ((A) / 6)) /*tex smaller, cramped */ +# define sup_sup_style(A) sup_style(sup_style((A))) /*tex smaller */ + +inline static mathdictval tex_fake_math_dict(halfword chr) +{ + mathdictval d = { 0, 0, 0 }; + if (math_dict_properties_par || math_dict_group_par) { + d.properties = (unsigned short) math_dict_properties_par; + d.group = (unsigned short) math_dict_group_par; + d.index = (unsigned int) chr; + } + return d; +} + +void tex_math_copy_char_data(halfword target, halfword source, int wipelist) +{ + if (node_type(source) == math_char_node) { + kernel_math_family(target) = kernel_math_family(source); + kernel_math_character(target) = kernel_math_character(source); + kernel_math_options(target) = kernel_math_options(source); + kernel_math_properties(target) = kernel_math_properties(source); + kernel_math_group(target) = kernel_math_group(source); + kernel_math_index(target) = kernel_math_index(source); + } else { + kernel_math_list(target) = kernel_math_list(source); + if (wipelist) { + kernel_math_list(source) = null; + } + } +} + +// static const math_styles map_cramped_style[] = { /*tex cramp the style */ +// cramped_display_style, +// cramped_display_style, +// cramped_text_style, +// cramped_text_style, +// cramped_script_style, +// cramped_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// }; +// +// static const math_styles map_subscript_style[] = { /*tex smaller and cramped */ +// cramped_script_style, +// cramped_script_style, +// cramped_script_style, +// cramped_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// }; +// +// static const math_styles map_superscript_style[] = { /*tex smaller */ +// script_style, +// script_style, +// script_style, +// script_style, +// script_script_style, +// script_script_style, +// script_script_style, +// script_script_style, +// }; +// +// static const math_styles map_numerator_style[] = {/*tex smaller unless already scriptscript */ +// script_style, +// cramped_script_style, +// script_style, +// cramped_script_style, +// script_script_style, +// cramped_script_script_style, +// script_script_style, +// cramped_script_script_style, +// }; +// +// static const math_styles map_denominator_style[] = { /*tex smaller, all cramped */ +// cramped_script_style, +// cramped_script_style, +// cramped_script_style, +// cramped_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// cramped_script_script_style, +// }; +// +// static const math_styles map_double_superscript_style[] = { /*tex smaller, keep cramped */ +// script_style, +// cramped_script_style, +// script_style, +// cramped_script_style, +// script_script_style, +// cramped_script_script_style, +// script_script_style, +// cramped_script_script_style, +// }; + +/*tex + This is very \TEX: a variable class influences the family being used. +*/ + +halfword tex_size_of_style(halfword style) +{ + switch (style) { + case script_style: + case cramped_script_style: + return script_size; + case script_script_style: + case cramped_script_script_style: + return script_script_size; + break; + default: + return text_size; + } +} + +halfword tex_math_style_variant(halfword style, halfword param) +{ + switch (tex_get_math_parameter(style, param, NULL)) { + case math_normal_style_variant: + return style; + case math_cramped_style_variant: + // return map_cramped_style[s]; + return cramped_style(style); + case math_subscript_style_variant: + // return map_subscript_style[s]; + return sub_style(style); + case math_superscript_style_variant: + case math_small_style_variant: + // return map_superscript_style[s]; + return sup_style(style); + case math_smaller_style_variant: + case math_numerator_style_variant: + // return map_numerator_style[s]; + return num_style(style); + case math_denominator_style_variant: + // return map_denominator_style[s]; + return denom_style(style); + case math_double_superscript_variant: + // return map_double_superscript_style[s]; + return sup_sup_style(style); + default: + return style; + } +} + +int tex_math_has_class_option(halfword cls, int option) +{ + halfword value = count_parameter(first_math_options_code + cls); + if (value == no_class_options) { + unsigned parent = (unsigned) count_parameter(first_math_parent_code + cls); + cls = (parent >> 16) & 0xFF; + if (! valid_math_class_code(cls)) { + return 0; + } + value = count_parameter(first_math_options_code + cls); + } + return (value & option) == option; +} + +static void tex_aux_unsave_math(void) +{ + tex_unsave(); + lmt_save_state.save_stack_data.ptr -= saved_math_n_of_items; + tex_flush_node_list(lmt_dir_state.text_dir_ptr); + if (saved_type(saved_math_item_direction) == saved_text_direction) { + lmt_dir_state.text_dir_ptr = saved_value(saved_math_item_direction); + } else { + tex_confusion("unsave math"); + } +} + +/*tex + + Sometimes it is necessary to destroy an mlist. The following subroutine empties the current + list, assuming that |abs(mode) = mmode|. + +*/ + +void tex_flush_math(void) +{ + halfword head = cur_list.head; + tex_flush_node_list(node_next(head)); + tex_flush_node_list(cur_list.incomplete_noad); + node_next(head) = null; + cur_list.tail = head; + cur_list.incomplete_noad = null; +} + +/*tex A printing helper. */ + +static void tex_aux_print_parameter(const char *what, halfword style, halfword param, halfword indirect, halfword value) +{ + tex_begin_diagnostic(); + tex_print_char('{'); + tex_print_str(what); + tex_print_char(' '); + if (indirect >= 0 && indirect <= last_math_indirect) { + tex_print_str(lmt_interface.math_indirect_values[indirect].name); + tex_print_char(' '); + } + if (param < math_parameter_last) { + tex_print_cmd_chr(set_math_parameter_cmd, param); + } else { + tex_print_format("%x %x ", math_parameter_spacing_left(param), math_parameter_spacing_right(param)); + } + tex_print_cmd_chr(math_style_cmd, style); + tex_print_char('='); + switch (math_parameter_value_type(param)) { + case math_int_parameter: + case math_style_parameter: + tex_print_int(value); + break; + case math_dimen_parameter: + tex_print_dimension(value, pt_unit); + break; + case math_muglue_parameter: + tex_print_spec(value, mu_unit); + break; + default: + tex_print_int(value); + break; + } + tex_print_char('}'); + tex_end_diagnostic(); +} + +static void tex_aux_print_fam(const char *what, halfword size, halfword fam) +{ + tex_begin_diagnostic(); + tex_print_format("{%s %C %i=%F}", what, define_family_cmd, size, tex_fam_fnt(fam, size)); + tex_end_diagnostic(); +} + +/*tex + Before we can do anything in math mode, we need fonts. We can use |max_n_of_math_families| + instead of 256 but we need to pack in bytes anyway so there is no gain. +*/ + +int tex_fam_fnt(int fam, int size) +{ + return (int) sa_get_item_4(lmt_math_state.fam_head, fam + (256 * size)).int_value; +} + +void tex_def_fam_fnt(int fam, int size, int fnt, int level) +{ + sa_tree_item item; + item.int_value = fnt; + sa_set_item_4(lmt_math_state.fam_head, fam + (256 * size), item, level); + tex_fixup_math_parameters(fam, size, fnt, level); + if (tracing_assigns_par > 1) { + tex_aux_print_fam("assigning", size, fam); + } +} + +static void tex_aux_unsave_math_fam_data(int gl) +{ + if (lmt_math_state.fam_head->stack) { + while (lmt_math_state.fam_head->sa_stack_ptr > 0 && abs(lmt_math_state.fam_head->stack[lmt_math_state.fam_head->sa_stack_ptr].level) >= (int) gl) { + sa_stack_item item = lmt_math_state.fam_head->stack[lmt_math_state.fam_head->sa_stack_ptr]; + if (item.level > 0) { + sa_rawset_item_4(lmt_math_state.fam_head, item.code, item.value_1); + /*tex Now do a trace message, if requested. */ + if (tracing_restores_par > 1) { + int size = item.code / 256; + int fam = item.code % 256; + tex_aux_print_fam("restoring", size, fam); + } + } + (lmt_math_state.fam_head->sa_stack_ptr)--; + } + } +} + +/*tex Math parameters, we have a lot of them! Todo: move the style into 2 */ + +void tex_def_math_parameter(int style, int param, scaled value, int level, int indirect) +{ + sa_tree_item item1, item2; + if (level <= 1) { + if (math_parameter_value_type(param) == math_muglue_parameter) { + item1 = sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &item2); + if (item2.int_value == indirect_math_regular && item1.int_value > thick_mu_skip_code) { + if (lmt_node_memory_state.nodesizes[item1.int_value]) { + tex_free_node(item1.int_value, glue_spec_size); + } + } + } + } + item1.int_value = value; + item2.int_value = indirect; + sa_set_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), item1, item2, level); + if (tracing_assigns_par > 1) { + tex_aux_print_parameter("assigning", style, param, indirect, value); + } +} + +// mukern .. there is no mudimen + +scaled tex_get_math_parameter(int style, int param, halfword *type) +{ + halfword indirect, value; + sa_tree_item v2; + sa_tree_item v1 = sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &v2); + indirect = v2.int_value == lmt_math_state.par_head->dflt.int_value ? indirect_math_unset : v2.uint_value; + value = v1.int_value; + switch (indirect) { + case indirect_math_unset: + if (type) { + *type = no_val_level; + } + return MATHPARAMDEFAULT; + /* we stored nothing */ + case indirect_math_regular: + switch (math_parameter_value_type(param)) { + case math_dimen_parameter: + if (type) { + *type = dimen_val_level; + } + return value; + case math_muglue_parameter: + if (type) { + *type = mu_val_level; + } + return value <= thick_mu_skip_code ? mu_glue_parameter(value) : value; + // case math_int_parameter: + // case math_style_parameter: + default: + if (type) { + *type = int_val_level; + } + return value; + } + /* we stored cs */ + case indirect_math_integer: + if (! value) { + if (type) { + *type = int_val_level; + } + return value; + } else if (eq_type(value) == integer_cmd) { + if (type) { + *type = int_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_dimension: + if (! value) { + if (type) { + *type = dimen_val_level; + } + return value; + } else if (eq_type(value) == dimension_cmd) { + if (type) { + *type = dimen_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_mugluespec: + if (! value) { + if (type) { + *type = mu_val_level; + } + return value; + } else { + switch (eq_type(value)) { + case mugluespec_cmd: + if (type) { + *type = mu_val_level; + } + return eq_value(value); + default: + goto MISMATCH; + } + + } + case indirect_math_gluespec: + if (! value) { + if (type) { + *type = glue_val_level; + } + return value; + } else { + switch (eq_type(value)) { + case gluespec_cmd: + if (type) { + *type = glue_val_level; + } + return eq_value(value); + default: + goto MISMATCH; + } + } + /* we stored chr */ + case indirect_math_register_integer: + if (! value) { + if (type) { + *type = int_val_level; + } + return value; + } else if (eq_type(value) == register_int_reference_cmd) { + if (type) { + *type = int_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_register_dimension: + if (! value) { + if (type) { + *type = dimen_val_level; + } + return value; + } else if (eq_type(value) == register_dimen_reference_cmd) { + if (type) { + *type = dimen_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_register_gluespec: + if (! value) { + if (type) { + *type = glue_val_level; + } + return value; + } else if (eq_type(value) == register_glue_reference_cmd) { + if (type) { + *type = glue_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_register_mugluespec: + if (! value) { + if (type) { + *type = mu_val_level; + } + return value; + } else if (eq_type(value) == register_mu_glue_reference_cmd) { + if (type) { + *type = mu_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_internal_integer: + if (! value) { + if (type) { + *type = int_val_level; + } + return value; + } else if (eq_type(value) == internal_int_reference_cmd) { + if (type) { + *type = int_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_internal_dimension: + if (! value) { + if (type) { + *type = dimen_val_level; + } + return value; + } else if (eq_type(value) == internal_dimen_reference_cmd) { + if (type) { + *type = dimen_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_internal_gluespec: + if (! value) { + if (type) { + *type = glue_val_level; + } + return value; + } else if (eq_type(value) == internal_glue_reference_cmd) { + if (type) { + *type = glue_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + case indirect_math_internal_mugluespec: + if (! value) { + if (type) { + *type = mu_val_level; + } + return value; + } else if (eq_type(value) == internal_mu_glue_reference_cmd) { + if (type) { + *type = mu_val_level; + } + return eq_value(value); + } else { + goto MISMATCH; + } + default: + MISMATCH: + tex_handle_error( + normal_error_type, + "Invalid inherited math parameter", + "You probably changed the type of the inherited math parameter, so I will " + "use zero instead." + ); + return 0; + } +} + +int tex_has_math_parameter(int style, int param) +{ + sa_tree_item v2; + sa_get_item_8(lmt_math_state.par_head, (param + (math_parameter_max_range * style)), &v2); + return v2.int_value == lmt_math_state.par_head->dflt.int_value ? indirect_math_unset : v2.uint_value; +} + +static void tex_aux_unsave_math_parameter_data(int gl) +{ + if (lmt_math_state.par_head->stack) { + while (lmt_math_state.par_head->sa_stack_ptr > 0 && abs(lmt_math_state.par_head->stack[lmt_math_state.par_head->sa_stack_ptr].level) >= (int) gl) { + sa_stack_item item = lmt_math_state.par_head->stack[lmt_math_state.par_head->sa_stack_ptr]; + if (item.level > 0) { + int param = item.code % math_parameter_max_range; + int style = item.code / math_parameter_max_range; + sa_tree_item item1, item2; + if (math_parameter_value_type(param) == math_muglue_parameter) { + item1 = sa_get_item_8(lmt_math_state.par_head, item.code, &item2); + if (item2.int_value == indirect_math_regular && item1.int_value > thick_mu_skip_code) { + /* if (tex_valid_node(item1.int_value)) { */ + if (lmt_node_memory_state.nodesizes[item1.int_value]) { + // printf("HERE 2.1: %i %i / %i %i / %i\n",item2.int_value,item1.int_value, item.value_1.int_value, item.value_2.int_value, node_type(item1.int_value)); + tex_free_node(item1.int_value, glue_spec_size); + } else { + // printf("HERE 2.2: %i %i / %i %i / %i\n",item2.int_value,item1.int_value, item.value_1.int_value, item.value_2.int_value, node_type(item1.int_value)); + } + } + } + sa_rawset_item_8(lmt_math_state.par_head, item.code, item.value_1, item.value_2); + /*tex Do a trace message, if requested. */ + if (tracing_restores_par > 1) { + int indirect = item.value_2.int_value; + tex_aux_print_parameter("restoring", style, param, indirect, tex_get_math_parameter(style, param, NULL)); + } + } + lmt_math_state.par_head->sa_stack_ptr--; + } + } +} + +/*tex Saving and unsaving of both: */ + +void tex_unsave_math_data(int level) +{ + tex_aux_unsave_math_fam_data(level); + tex_aux_unsave_math_parameter_data(level); +} + +/*tex Dumping and undumping: */ + +void tex_dump_math_data(dumpstream f) +{ + if (! lmt_math_state.fam_head) { + lmt_math_state.fam_head = sa_new_tree(MATHFONTSTACK, 4, (sa_tree_item) { .int_value = MATHFONTDEFAULT }); + } + sa_dump_tree(f, lmt_math_state.fam_head); + if (! lmt_math_state.par_head) { + lmt_math_state.par_head = sa_new_tree(MATHPARAMSTACK, 8, (sa_tree_item) { .int_value = MATHPARAMDEFAULT }); + } + sa_dump_tree(f, lmt_math_state.par_head); +} + +void tex_undump_math_data(dumpstream f) +{ + lmt_math_state.fam_head = sa_undump_tree(f); + lmt_math_state.par_head = sa_undump_tree(f); +} + +void tex_initialize_math(void) +{ + if (! lmt_math_state.fam_head) { + lmt_math_state.fam_head = sa_new_tree(MATHFONTSTACK, 4, (sa_tree_item) { .int_value = MATHFONTDEFAULT }); + } + if (! lmt_math_state.par_head) { + lmt_math_state.par_head = sa_new_tree(MATHPARAMSTACK, 8, (sa_tree_item) { .int_value = MATHPARAMDEFAULT }); + tex_initialize_math_spacing(); + } + return; +} + +/*tex + + Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun, or Inn, for purposes + of spacing and line breaking. An |ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, + |close_noad|, |punct_noad|, or |inner_noad| is used to represent portions of the various types. + For example, an |=| sign in a formula leads to the creation of a |rel_noad| whose |nucleus| field + is a representation of an equals sign (usually |fam = 0|, |character = 075|). A formula preceded + by |\mathrel| also results in a |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say, + and possibly separated by one or more ordinary nodes (not noads), \TEX\ will insert a penalty + node (with the current |rel_penalty|) just after the formula that corresponds to the |rel_noad|, + unless there already was a penalty immediately following; and a \quote {thick space} will be + inserted just before the formula that corresponds to the |op_noad|. + + A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually has a |subtype = normal|. The + only exception is that an |op_noad| might have |subtype = limits| or |no_limits|, if the normal + positioning of limits has been overridden for this operator. + + A |radical_noad| also has a |left_delimiter| field, which usually represents a square root sign. + + A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. + + Delimiter fields have four subfields called |small_fam|, |small_char|, |large_fam|, |large_char|. + These subfields represent variable-size delimiters by giving the \quote {small} and \quote + {large} starting characters, as explained in Chapter~17 of {\em The \TEX book}. + + A |fraction_noad| is actually quite different from all other noads. It has |thickness|, + |denominator|, and |numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The + |thickness| is a scaled value that tells how thick to make a fraction rule; however, the special + value |preset_rule_thickness| is used to stand for the |preset_rule_thickness| of the current + size. The |numerator| and |denominator| point to mlists that define a fraction; we always have + |type(numerator) = type(denominator) = sub_mlist|. The |left_delimiter| and |right_delimiter| + fields specify delimiters that will be placed at the left and right of the fraction. In this way, + a |fraction_noad| is able to represent all of \TEX's operators |\over|, |\atop|, |\above|, + |\overwithdelims|, |\atopwithdelims|, and |\abovewithdelims|. + + The |new_noad| function creates an |ord_noad| that is completely |null|. + +*/ + +halfword tex_new_sub_box(halfword curbox) +{ + halfword noad = tex_new_node(simple_noad, ordinary_noad_subtype); + halfword sbox = tex_new_node(sub_box_node, 0); + noad_nucleus(noad) = sbox; + kernel_math_list(sbox) = curbox; + return noad; +} + +quarterword tex_aux_set_math_char(halfword target, mathcodeval *mval, mathdictval *dval) +{ + halfword hmcode = tex_get_hm_code(mval->character_value); + kernel_math_character(target) = mval->character_value; + if (mval->class_value == math_use_current_family_code) { + kernel_math_family(target) = cur_fam_par_in_range ? cur_fam_par : 0; + node_subtype(target) = ordinary_noad_subtype; + } else { + kernel_math_family(target) = mval->family_value; + node_subtype(target) = mval->class_value; + } + if (dval) { + kernel_math_properties(target) = dval->properties; + kernel_math_group(target) = dval->group; + kernel_math_index(target) = dval->index; + } + if ((hmcode & auto_discretionary_normal) == auto_discretionary_normal) { // has_discretionary_normal + math_kernel_node_set_option(target, math_kernel_auto_discretionary); + } + if ((hmcode & auto_discretionary_italic) == auto_discretionary_italic) { // has_discretionary_italic + math_kernel_node_set_option(target, math_kernel_full_discretionary); + } + return node_subtype(target); +} + +/*tex + + A few more kinds of noads will complete the set: An |under_noad| has its nucleus underlined; an + |over_noad| has it overlined. An |accent_noad| places an accent over its nucleus; the accent + character appears as |math_fam (accent_chr (p))| and |math_character (accent_chr (p))|. A + |vcenter_noad| centers its nucleus vertically with respect to the axis of the formula; in such + noads we always have |type (nucleus (p)) = sub_box|. + + And finally, we have the |fence_noad| type, to implement \TEX's |\left| and |\right| as well as + \ETEX's |\middle|. The |nucleus| of such noads is replaced by a |delimiter| field; thus, for + example, |\left(| produces a |fence_noad| such that |delimiter(p)| holds the family and + character codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| never + appears in an mlist except as the first element, and a |fence_noad| with subtype + |right_noad_side| never appears in an mlist except as the last element; furthermore, we either + have both a |left_noad_side| and a |right_noad_side|, or neither one is present. + + Math formulas can also contain instructions like |\textstyle| that override \TeX's normal style + rules. A |style_node| is inserted into the data structure to record such instructions; it is + three words long, so it is considered a node instead of a noad. The |subtype| is either + |display_style| or |text_style| or |script_style| or |script_script_style|. The second and + third words of a |style_node| are not used, but they are present because a |choice_node| is + converted to a |style_node|. + + \TEX\ uses even numbers 0, 2, 4, 6 to encode the basic styles |display_style|, \dots, + |script_script_style|, and adds~1 to get the \quote {cramped} versions of these styles. This + gives a numerical order that is backwards from the convention of Appendix~G in {\em The \TEX + book}; i.e., a smaller style has a larger numerical value. + +*/ + +void tex_run_math_style(void) { + switch (cur_chr) { + case yet_unset_math_style: + { + halfword style = tex_scan_math_style_identifier(1, 0); + if (is_valid_math_style(style)) { + halfword noad = tex_new_node(style_node, (quarterword) style); + cur_list.math_style = style; + tex_tail_append(noad); + } + } + break; + case scaled_math_style: + { + halfword noad = tex_new_node(style_node, scaled_math_style); + style_scale(noad) = tex_scan_int(0, NULL); + // style_scale(noad) = tex_scan_positive_scale(0); + tex_tail_append(noad); + } + break; + default: + if (is_valid_math_style(cur_chr)) { + halfword noad = tex_new_node(style_node, (quarterword) cur_chr); + cur_list.math_style = cur_chr; + tex_tail_append(noad); + } else { + /*tex For now silently ignored. */ + } + } +} + +/*tex + + Let's consider now the previously unwritten part of |show_node_list| that displays the things + that can only be present in mlists; this program illustrates how to access the data structures + just defined. + + In the context of the following program, |p| points to a node or noad that should be displayed, + and the current string contains the \quote {recursion history} that leads to this point. The + recursion history consists of a dot for each outer level in which |p| is subsidiary to some + node, or in which |p| is subsidiary to the |nucleus| field of some noad; the dot is replaced by + |_| or |^| or |/| or |\\| if |p| is descended from the |subscr| or |supscr| or |denominator| or + |numerator| fields of noads. For example, the current string would be |{\^_/}| if |p| points to + the |ord_noad| for |x| in the (ridiculous) formula {$\sqrt {a ^ {\mathinner {b _ + {c \over x+y}}}}$|. + +*/ + +static void tex_aux_display_choice_noad (halfword n, int threshold, int max); +static void tex_aux_display_parameter_node (halfword n); +static void tex_aux_display_simple_noad (halfword n, int threshold, int max); +static void tex_aux_display_radical_noad (halfword n, int threshold, int max); +static void tex_aux_display_accent_noad (halfword n, int threshold, int max); +static void tex_aux_display_fence_noad (halfword n, int threshold, int max); +static void tex_aux_display_fraction_noad (halfword n, int threshold, int max); + +static void tex_aux_print_fam_and_char(halfword n) +{ + tex_print_format(", family %x, character %x, original %x", kernel_math_family(n), kernel_math_character(n)); + tex_aux_show_dictionary(n, kernel_math_properties(n), kernel_math_group(n), kernel_math_index(n), tex_fam_fnt(kernel_math_family(n), 0), kernel_math_character(n)); +} + +int tex_show_math_node(halfword n, int threshold, int max) +{ + switch (node_type(n)) { + case style_node: + /* why not shown? */ + break; + case choice_node: + tex_aux_display_choice_noad(n, threshold, max); + break; + case parameter_node: + tex_aux_display_parameter_node(n); + break; + case simple_noad: + tex_aux_display_simple_noad(n, threshold, max); + break; + case radical_noad: + tex_aux_display_radical_noad(n, threshold, max); + break; + case accent_noad: + tex_aux_display_accent_noad(n, threshold, max); + break; + case fence_noad: + tex_aux_display_fence_noad(n, threshold, max); + break; + case fraction_noad: + tex_aux_display_fraction_noad(n, threshold, max); + break; + case math_text_char_node: + case math_char_node: + tex_aux_print_fam_and_char(n); + break; + case sub_box_node: + tex_print_node_list(kernel_math_list(n), NULL, threshold, max); + break; + case sub_mlist_node: + if (kernel_math_list(n)) { + tex_print_node_list(kernel_math_list(n), NULL, threshold, max); + } else { + tex_print_str(", empty"); + } + break; + default: + return 0; + } + return 1; +} + +inline halfword tex_aux_valid_delimiter(halfword d) +{ + return (d && (delimiter_small_family(d) || delimiter_small_character(d) || delimiter_large_family(d) || delimiter_large_character(d))) ? d : null; +} + +static void tex_aux_print_delimiter(halfword d) +{ + if (delimiter_small_family(d) < 0) { + /*tex This should never happen. */ + tex_print_int(-1); + } else if (delimiter_small_family(d) < 16 && delimiter_large_family(d) < 16 && delimiter_small_character(d) < 256 && delimiter_large_character(d) < 256) { + /*tex Traditional tex style. */ + int a = delimiter_small_family(d) * 256 + delimiter_small_character(d); + a = a * 0x1000 + delimiter_large_family(d) * 256 + delimiter_large_character(d); + tex_print_format(", code %x", a); + } else if ((delimiter_large_family(d) == 0 && delimiter_large_character(d) == 0) || delimiter_small_character(d) > 65535 || delimiter_large_character(d) > 65535) { + /*tex \LUATEX\ style. */ + tex_print_format(", family %x, character %x", delimiter_small_family(d), delimiter_small_character(d)); + } +} + +/*tex + + The next subroutine will descend to another level of recursion when a subsidiary mlist needs to + be displayed. The parameter |c| indicates what character is to become part of the recursion + history. An empty mlist is distinguished from a missing field, because these are not equivalent + (as explained above). + +*/ + +static void tex_aux_display_common_noad(halfword n, int threshold, int max) +{ + tex_print_node_list(noad_nucleus(n), "nucleus", threshold, max); + tex_print_node_list(noad_supscr(n), "superscript", threshold, max); + tex_print_node_list(noad_subscr(n), "subscript", threshold, max); + tex_print_node_list(noad_supprescr(n), "superprescript", threshold, max); + tex_print_node_list(noad_subprescr(n), "subprescript", threshold, max); + tex_print_node_list(noad_prime(n), "primescript", threshold, max); + tex_print_node_list(noad_new_hlist(n), "newhlist", threshold, max); +} + +static void tex_aux_display_parameter_node(halfword n) +{ + tex_print_format(", id %i, style %i", parameter_name(n), parameter_style(n)); +} + +static void tex_aux_display_choice_noad(halfword n, int threshold, int max) +{ + switch (node_subtype(n)) { + case normal_choice_subtype: + tex_print_node_list(choice_display_mlist(n), "display", threshold, max); + tex_print_node_list(choice_text_mlist(n), "text", threshold, max); + tex_print_node_list(choice_script_mlist(n), "script", threshold, max); + tex_print_node_list(choice_script_script_mlist(n), "scriptscript", threshold, max); + break; + case discretionary_choice_subtype: + tex_print_format(", class %i", choice_class(n)); + tex_print_node_list(choice_pre_break(n), "pre", threshold, max); + tex_print_node_list(choice_post_break(n), "post", threshold, max); + tex_print_node_list(choice_no_break(n), "replace", threshold, max); + break; + } +} + +static void tex_aux_display_simple_noad(halfword n, int threshold, int max) +{ + if (noad_source(n)) { + tex_print_format(", source %i", noad_source(n)); + } + tex_aux_display_common_noad(n, threshold, max); +} + +static void tex_aux_display_radical_noad(halfword n, int threshold, int max) +{ + if (noad_width(n)) { + tex_print_format(", width %D", noad_width(n), pt_unit); + } + if (radical_height(n)) { + tex_print_format(", height %D", radical_height(n), pt_unit); + } + if (radical_depth(n)) { + tex_print_format(", depth %D", radical_depth(n), pt_unit); + } + if (noad_source(n) != 0) { + tex_print_format(", source %i", noad_source(n)); + } + if (noad_options(n)) { + tex_print_format(", options %x", noad_options(n)); + } + if (radical_left_delimiter(n)) { + tex_print_str(", left"); + tex_aux_print_delimiter(radical_left_delimiter(n)); + } + if (radical_right_delimiter(n)) { + tex_print_str(", right"); + tex_aux_print_delimiter(radical_right_delimiter(n)); + } + if (radical_degree(n)) { + tex_print_node_list(radical_degree(n), "degree", threshold, max); + } + tex_aux_display_common_noad(n, threshold, max); +} + +static void tex_aux_display_accent_noad(halfword n, int threshold, int max) +{ + halfword top_char = accent_top_character(n); + halfword bottom_char = accent_bottom_character(n); + halfword fraction = accent_fraction(n); + if (fraction) { + tex_print_str(", fraction "); + tex_print_int(fraction); + } + switch (node_subtype(n)) { + case bothflexible_accent_subtype: + if (top_char) { + tex_print_str(", top "); + tex_aux_print_fam_and_char(top_char); + } + if (bottom_char) { + tex_print_str(", bottom "); + tex_aux_print_fam_and_char(bottom_char); + } + if (! (top_char || bottom_char)) { + tex_print_str(", overlay "); + tex_aux_print_fam_and_char(accent_middle_character(n)); + } + break; + case fixedtop_accent_subtype: + if (top_char) { + tex_print_str(", fixed top "); + tex_aux_print_fam_and_char(top_char); + } + if (bottom_char) { + tex_print_str(", bottom "); + tex_aux_print_fam_and_char(bottom_char); + } + break; + case fixedbottom_accent_subtype: + if (top_char) { + tex_print_str(", top "); + tex_aux_print_fam_and_char(top_char); + } + if (bottom_char) { + tex_print_str(", fixed bottom "); + tex_aux_print_fam_and_char(bottom_char); + } + break; + case fixedboth_accent_subtype: + if (top_char) { + tex_print_str(", fixed top "); + tex_aux_print_fam_and_char(top_char); + } + if (bottom_char) { + tex_print_str(", fixed bottom "); + tex_aux_print_fam_and_char(bottom_char); + } + break; + } + tex_aux_display_common_noad(n, threshold, max); +} + +static void tex_aux_display_fence_noad(halfword n, int threshold, int max) +{ + if (noad_height(n)) { + tex_print_format(", height %D", noad_height(n), pt_unit); + } + if (noad_depth(n)) { + tex_print_format(", depth %D", noad_depth(n), pt_unit); + } + if (get_noad_main_class(n) >= 0) { + tex_print_format(", class %i", get_noad_main_class(n)); + } + if (get_noad_left_class(n) >= 0) { + tex_print_format(", leftclass %i", get_noad_left_class(n)); + } + if (get_noad_right_class(n) >= 0) { + tex_print_format(", rightclass %i", get_noad_right_class(n)); + } + if (noad_source(n) != 0) { + tex_print_format(", source %i", noad_source(n)); + } + if (noad_options(n)) { + tex_print_format(", options %x", noad_options(n)); + } + tex_aux_print_delimiter(fence_delimiter_list(n)); + tex_print_node_list(fence_delimiter_top(n), "top", threshold, max); + tex_print_node_list(fence_delimiter_bottom(n), "bottom", threshold, max); +} + +static void tex_aux_display_fraction_noad(halfword n, int threshold, int max) +{ + halfword leftdelimiter = tex_aux_valid_delimiter(fraction_left_delimiter(n)); + halfword rightdelimiter = tex_aux_valid_delimiter(fraction_right_delimiter(n)); + tex_print_str(", thickness "); + if (fraction_rule_thickness(n) == preset_rule_thickness) { + tex_print_str("default"); + } else { + tex_print_dimension(fraction_rule_thickness(n), pt_unit); + } + if (leftdelimiter) { + tex_print_str(", leftdelimiter "); + tex_aux_print_delimiter(leftdelimiter); + } + if (rightdelimiter) { + tex_print_str(", rightdelimiter "); + tex_aux_print_delimiter(rightdelimiter); + } + if (noad_source(n) != 0) { + tex_print_str(", source "); + tex_print_int(noad_source(n)); + } + if (noad_options(n)) { + tex_print_str(", options "); + tex_print_qhex(noad_options(n)); + } + tex_print_node_list(fraction_numerator(n), "numerator", threshold, max); + tex_print_node_list(fraction_denominator(n), "denominator", threshold, max); +} + +/*tex + + The routines that \TEX\ uses to create mlists are similar to those we have just seen for the + generation of hlists and vlists. But it is necessary to make \quote {noads} as well as nodes, + so the reader should review the discussion of math mode data structures before trying to make + sense out of the following program. + + Here is a little routine that needs to be done whenever a subformula is about to be processed. + The parameter is a code like |math_group|. + +*/ + +static void tex_aux_new_save_level_math(quarterword group) +{ + halfword direction = math_direction_par; + tex_set_saved_record(saved_math_item_direction, saved_text_direction, 0, lmt_dir_state.text_dir_ptr); + lmt_save_state.save_stack_data.ptr += saved_math_n_of_items; + lmt_dir_state.text_dir_ptr = tex_new_dir(normal_dir_subtype, direction); + tex_new_save_level(group); + update_tex_par_direction(direction); + update_tex_text_direction(direction); +} + +static void tex_aux_push_math(quarterword group, int style) +{ + if (math_direction_par != text_direction_par) { + cur_list.math_dir = 1; + } + cur_list.math_begin = math_begin_class_par; + cur_list.math_end = math_end_class_par; + cur_list.math_main_style = style; + tex_push_nest(); + cur_list.mode = -mmode; + cur_list.incomplete_noad = null; + cur_list.math_style = style; + tex_aux_new_save_level_math(group); + update_tex_math_left_class(unset_noad_class); + update_tex_math_right_class(unset_noad_class); +} + +static void tex_aux_enter_ordinary_math(int style) +{ + tex_aux_push_math(math_shift_group, style); + update_tex_family(0, unused_math_family); + if (every_math_par) { + tex_begin_token_list(every_math_par, every_math_text); + } +} + +static void tex_aux_enter_display_math(halfword cmd); + +/*tex + + We get into math mode from horizontal mode when a |$| (i.e., a |math_shift| character) is + scanned. We must check to see whether this |$| is immediately followed by another, in case + display math mode is called for. + +*/ + +void tex_run_math_initialize(void) +{ + switch(cur_cmd) { + case math_shift_cmd: + /*tex |get_x_token| would fail on |\ifmmode|! */ + tex_get_token(); + if (cur_cmd == math_shift_cmd && cur_list.mode > nomode) { + tex_aux_enter_display_math(math_shift_cmd); + } else { + tex_back_input(cur_tok); + tex_aux_enter_ordinary_math(text_style); + } + break; + case math_shift_cs_cmd: + if (cur_chr == begin_math_mode_code) { + tex_aux_enter_ordinary_math(tex_scan_math_style_identifier(0, 0)); + } else if (cur_chr == begin_display_math_code && cur_list.mode > nomode) { + tex_aux_enter_display_math(begin_display_math_code); + } else if (cur_chr == begin_inline_math_code) { + tex_aux_enter_ordinary_math(text_style); + } else { + tex_you_cant_error("math shift 1"); + } + break; + default: + tex_you_cant_error("math shift 2"); + break; + } +} + +/*tex + + We get into ordinary math mode from display math mode when |\eqno| or |\leqno| appears. In such + cases |cur_chr| will be 0 or~1, respectively; the value of |cur_chr| is placed onto |save_stack| + for safe keeping. When \TEX\ is in display math mode, |cur_group = math_shift_group|, so it is + not necessary for the |start_eq_no| procedure to test for this condition. + +*/ + +void tex_run_math_equation_number(void) { + if (tex_in_privileged_mode()) { + if (cur_group == math_shift_group) { + tex_set_saved_record(saved_equation_number_item_location, saved_equation_number_location, 0, cur_chr); + lmt_save_state.save_stack_data.ptr += saved_equation_number_n_of_items; + tex_aux_enter_ordinary_math(text_style); + } else { + tex_off_save(); + } + } +} + +/*tex + + Subformulas of math formulas cause a new level of math mode to be entered, on the semantic nest + as well as the save stack. These subformulas arise in several ways: (1)~A left brace by itself + indicates the beginning of a subformula that will be put into a box, thereby freezing its glue + and preventing line breaks. (2)~A subscript or superscript is treated as a subformula if it is + not a single character; the same applies to the nucleus of things like |\underline|. (3)~The + |\left| primitive initiates a subformula that will be terminated by a matching |\right|. The + group codes placed on |save_stack| in these three cases are |math_group|, |math_group|, and + |math_left_group|, respectively. + + Here is the code that handles case (1); the other cases are not quite as trivial, so we shall + consider them later. + +*/ + +void tex_run_math_left_brace(void) +{ + if (math_grouping_mode_par) { + /*tex This is an experiment. Some tracing has to be adapted probably. */ + tex_new_save_level(math_simple_group); + update_tex_internal_math_style(cur_mode == mmode ? cur_list.math_style : -1); + update_tex_internal_math_scale(cur_mode == mmode ? cur_list.math_scale : -1); + } else { + halfword q = tex_new_node(math_char_node, 0); + halfword n = tex_new_node(simple_noad, ordinary_noad_subtype); + tex_tail_append(n); + noad_nucleus(n) = q; + tex_back_input(cur_tok); + tex_aux_scan_math(q, cur_list.math_style, 0, 0, 0, 0, unset_noad_class, unset_noad_class); + } +} + +/*tex + + If the inline directions of |\pardir| and |\mathdir| are opposite, then this function will + return true. Discovering that fact is somewhat odd because it needs traversal of the + |save_stack|. The occurance of displayed equations is weird enough that this is probably still + better than having yet another field in the |input_stack| structures. + + None of this makes much sense if the inline direction of either one of |\pardir| or |\mathdir| + is vertical, but in that case the current math machinery is ill suited anyway so I do not + bother to test that. We now just return the direction. + +*/ + +static int tex_aux_pre_math_par_direction(void) +{ + return tex_located_save_value(internal_int_location(par_direction_code)); +} + +/*tex + + When we enter display math mode, we need to call |line_break| to process the partial paragraph + that has just been interrupted by the display. Then we can set the proper values of + |display_width| and |display_indent| and |pre_display_size|. + +*/ + +static void tex_aux_enter_display_math(halfword cmd) +{ + if (math_display_mode_par) { + tex_aux_push_math(math_shift_group, display_style); + cur_list.math_mode = cmd; + update_tex_family(0, unused_math_family); + if (every_display_par) { + tex_begin_token_list(every_display_par, every_display_text); + } + } else { + /*tex new or partial |pre_display_size| */ + scaled size; + /*tex new |display_width| */ + scaled width; + /*tex new |display_indent| */ + scaled indent; + /*tex + Deal with |\noindent$$| or |$${ }$$| or the 2nd of |$${ }$$| |$${ }$$|. + */ + if (cur_list.head == cur_list.tail || (node_next(cur_list.head) == cur_list.tail && node_type(cur_list.tail) == par_node && ! node_next(cur_list.tail))) { + if (node_next(cur_list.head) == cur_list.tail) { + /*tex + + |resume_after_display| inserts a |par_node|, but if there is another display + immediately following, we have to get rid of that node. + + */ + tex_flush_node(cur_list.tail); + /* cur_list.tail = cur_list.head; */ /* probably needed */ + } + tex_pop_nest(); + size = - max_dimen; + } else { + tex_line_break(1, math_shift_group); + // size = tex_actual_box_width(lmt_linebreak_state.just_box, tex_x_over_n(tex_get_font_em_width(cur_font_par), 1000) * math_pre_display_gap_factor_par); + size = tex_actual_box_width(lmt_linebreak_state.just_box, scaledround((tex_get_font_em_width(cur_font_par) / 1000.0) * math_pre_display_gap_factor_par)); + } + /*tex + + Now we are in vertical mode, working on the list that will contain the display. A displayed + equation is considered to be three lines long, so we calculate the length and offset of line + number |prev_graf + 2|. + + */ + if (par_shape_par) { + /*tex scope of paragraph shape specification */ + int n = tex_get_specification_count(par_shape_par); + if (n > 0) { + if (cur_list.prev_graf + 2 < n) { + n = cur_list.prev_graf + 2; + } + indent = tex_get_specification_indent(par_shape_par, n) ; + width = tex_get_specification_width(par_shape_par, n); + indent = swap_parshape_indent(pre_display_direction_par, indent, width); + } else { + width = hsize_par; + indent = 0; + } + } else if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (cur_list.prev_graf + 2 > hang_after_par)) || (cur_list.prev_graf + 1 < -hang_after_par))) { + halfword hangindent = swap_hang_indent(pre_display_direction_par, hang_indent_par); + width = hsize_par - abs(hangindent); + indent = hangindent > 0 ? hangindent : 0; + } else { + width = hsize_par; + indent = 0; + } + tex_aux_push_math(math_shift_group, display_style); + cur_list.mode = mmode; + update_tex_family(0, unused_math_family); + update_tex_pre_display_size(size); + update_tex_display_width(width); + update_tex_display_indent(indent); + update_tex_pre_display_direction(tex_aux_pre_math_par_direction()); + if (every_display_par) { + tex_begin_token_list(every_display_par, every_display_text); + } + if (lmt_nest_state.nest_data.ptr == 1) { + if (! lmt_page_builder_state.output_active) { + lmt_page_filter_callback(before_display_page_context, 0); + } + tex_build_page(); + } + } +} + +/*tex + + The next routine parses all variations of a delimiter code. The |extcode| tells what syntax form + to use (\TEX\ or \LUATEX) , the |doclass| tells whether or not read a math class also (for + |\delimiter| c.s.). The class is passed on for conversion to |\mathchar|. + +*/ + +static delcodeval tex_aux_scan_extdef_del_code(int extcode, int doclass) +{ + delcodeval d = tex_no_del_code(); + switch (extcode) { + case tex_mathcode: + /*tex This is the easiest: |\delcode|,*/ + { + halfword v = tex_scan_int(0, NULL); + /*tex |MFCCFCC| or |FCCFCC| */ + if (doclass) { + d.small.class_value = (short) (v / 0x1000000); + v = (v & 0xFFFFFF); + } + if (v > 0xFFFFFF) { + tex_handle_error( + normal_error_type, + "Invalid delimiter code", + "I'm going to use 0 instead of that illegal code value." + ); + v = 0; + } + d.small.family_value = (short) (v / 0x100000); + d.small.character_value = (v % 0x100000) / 0x1000; + d.large.family_value = (short) ((v & 0xFFF) / 0x100); + d.large.character_value = (v % 0x100); + /* */ + d.small.character_value = math_character_part(d.small.character_value); + d.large.character_value = math_character_part(d.large.character_value); + } + break; + case umath_mathcode: + /*tex |\Udelcode|: |<0-7><0-0xFF><0-0x10FFFF>| or |<0-0xFF><0-0x10FFFF>| */ + { + if (doclass) { + d.small.class_value = (short) tex_scan_math_class_number(0); + } + d.small.family_value = (short) tex_scan_math_family_number(); + d.small.character_value = tex_scan_math_char_number(); + if (d.small.family_value < 0 || d.small.family_value > max_math_family_index) { + tex_handle_error( + normal_error_type, + "Invalid delimiter family", + "I'm going to use family 0 instead." + ); + d.small.family_value = 0; + d.small.character_value = 0; + } + } + break; + /* + case umathnum_mathcode: + // |\Udelcodenum|: |"FF<21bits>|; the largest numeric value is $2^29-1$, but the top of + // bit 21 can't be used as it contains invalid USV's. + if (doclass) { + tex_confusion("umathnum mathcode"); + } else { + halfword v = tex_scan_int(0, NULL); + d.small.family_value = (short) math_family_part(v); + d.small.character_value = math_character_part(v); + if (d.small.family_value < 0 || d.small.family_value > max_math_family_index || d.small.character_value > max_math_character_code) { + tex_handle_error( + normal_error_type, + "Invalid delimiter code", + "I'm going to use 0 instead of that illegal code value." + ); + d.small.family_value = 0; + d.small.character_value = 0; + } + } + break; + */ + default: + /*tex Something's gone wrong! */ + tex_confusion("unknown extcode, case 1"); + break; + } + d.large.class_value = d.small.class_value; + return d; +} + +void tex_scan_extdef_del_code(int level, int extcode) +{ + delcodeval d; + int chr = tex_scan_char_number(0); + tex_scan_optional_equals(); + d = tex_aux_scan_extdef_del_code(extcode, 0); + tex_set_del_code(chr, d, (quarterword) level); +} + +mathdictval tex_scan_mathdict(void) +{ + mathdictval d = { 0, 0, 0 }; /* use this one directly */ + d.properties = (unsigned short) tex_scan_math_properties_number(); + d.group = (unsigned short) tex_scan_math_group_number(); + d.index = (unsigned int) tex_scan_math_index_number(); + return d; +} + +mathcodeval tex_scan_mathchar(int extcode) +{ + mathcodeval d = { 0, 0, 0 }; /* use this one directly */ + switch (extcode) { + case tex_mathcode: + /*tex |"<4bits><4bits><8bits>| */ + { + halfword v = tex_scan_int(0, NULL); + if (v >= 0) { + if (v > 0xFFFF) { + v = 0xFFFF; + } + d.class_value = (short) math_old_class_part(v); + d.family_value = (short) math_old_family_part(v); + d.character_value = math_old_character_part(v); + } + } + break; + case umath_mathcode: + /*tex |"<6bits>"<6bits>"<20bits>| */ + { + d.class_value = (short) tex_scan_math_class_number(0); + d.family_value = (short) tex_scan_math_family_number(); + d.character_value = tex_scan_math_char_number(); + } + break; + /* + case umathnum_mathcode: + // |"<6bits><6bits><20bits>|: the largest numeric value is $2^32-1$, but the top of bit 21 can't + // be used as it contains invalid USV's. Note: |scan_int| won't accept families 128-255 + // because these use bit 32. + { + halfword v = tex_scan_int(0, NULL); + d.class_value = (short) math_class_part(v); + d.family_value = (short) math_family_part(v); + d.character_value = math_character_part(v); + } + break; + */ + default: + /*tex Something's gone wrong. */ + tex_confusion("unknown extcode, case 2"); + break; + } + if (d.class_value < 0 || d.character_value > max_math_character_code || d.class_value > max_math_class_code || d.family_value > max_math_family_index) { + tex_handle_error( + normal_error_type, + "Invalid math code", + "I'm going to use 0 instead of that illegal code value." + ); + d.class_value = 0; + d.family_value = 0; + d.character_value = 0; + } + return d; +} + +halfword tex_new_math_spec(mathcodeval m, quarterword code) +{ + halfword s = tex_new_node(math_spec_node, code); + math_spec_class(s) = (singleword) m.class_value; + math_spec_family(s) = (singleword) m.family_value; + math_spec_character(s) = m.character_value; + return s; +} + +halfword tex_new_math_dict_spec(mathdictval d, mathcodeval m, quarterword code) +{ + halfword s = tex_new_node(math_spec_node, code); + math_spec_class(s) = (singleword) m.class_value; + math_spec_family(s) = (singleword) m.family_value; + math_spec_character(s) = m.character_value; + math_spec_properties(s) = (quarterword) d.properties; + math_spec_group(s) = (quarterword) d.group; + math_spec_index(s) = d.index; + return s; +} + +mathcodeval tex_get_math_spec(halfword s) +{ + mathcodeval m = { 0, 0, 0 }; + if (s) { + m.class_value = math_spec_class(s); + m.family_value = math_spec_family(s); + m.character_value = math_spec_character(s); + } + return m; +} + +mathdictval tex_get_math_dict(halfword s) +{ + mathdictval d = { 0, 0, 0 }; + if (s) { + d.properties = math_spec_properties(s); + d.group = math_spec_group(s); + d.index = math_spec_index(s); + } + return d; +} + +halfword tex_scan_math_spec(int optional_equal) +{ + mathcodeval m; + if (optional_equal) { + tex_scan_optional_equals(); + } + m = tex_scan_mathchar(umath_mathcode); + return tex_new_math_spec(m, mathspec_mathcode); +} + +void tex_scan_extdef_math_code(int level, int extcode) +{ + mathcodeval d; + int chr = tex_scan_char_number(0); + tex_scan_optional_equals(); + d = tex_scan_mathchar(extcode); + tex_set_math_code(chr, d, (quarterword) level); +} + +/*tex This reads in a delcode when actually a mathcode is needed. */ + +mathcodeval tex_scan_delimiter_as_mathchar(int extcode) +{ + delcodeval dval = tex_aux_scan_extdef_del_code(extcode, 1); + return dval.small; +} + +/*tex + + Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken down into subfields + called |type| and either |math_list| or |(math_fam, math_character)|. The job of |scan_math| is + to figure out what to place in one of these principal fields; it looks at the subformula that + comes next in the input, and places an encoding of that subformula into a given word of |mem|. + + already prepared: every [component, degree, radical, over, under, accent, prime, subscript, + superscript] + + toks : every_subscript_par + toks_text : every_subscipt_text or every_math_text (for tracing) + +*/ + +/*tex + For some reason |$\char44$| gives an undefined |$| when we made that character active in math. +*/ + +static void tex_aux_scan_active_math_char(void) +{ + cur_cs = tex_active_to_cs(cur_chr, 1); + cur_cmd = eq_type(cur_cs); + cur_chr = eq_value(cur_cs); + tex_x_token(); + tex_back_input(cur_tok); +} + +static int tex_aux_scan_math(halfword target, halfword style, int usetextfont, halfword toks, halfword toks_text, int nocomponent, halfword cls, halfword all) +{ + mathcodeval mval = { 0, 0, 0 }; + mathdictval dval = { 0, 0, 0 }; + lmt_math_state.last_atom = cls; + RESTART: + do { + tex_get_x_token(); + } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd); +// RESWITCH: + switch (cur_cmd) { + case char_number_cmd: + /* The |\glyph| variant is accepted but no keywords here. */ + cur_chr = tex_scan_char_number(0); + // fall through + case letter_cmd: + case other_char_cmd: + case char_given_cmd: + mval = tex_get_math_code(cur_chr); + if (mval.class_value == active_math_class_value) { + /*tex An active character is allowed here. */ + tex_aux_scan_active_math_char(); + goto RESTART; + } + dval = tex_fake_math_dict(mval.character_value); + break; + // case char_number_cmd: + // /* The |\glyph| variant is accepted but no keywords here. */ + // cur_chr = tex_scan_char_number(); + // cur_cmd = char_given_cmd; + // goto RESWITCH; + case math_char_number_cmd: + switch (cur_chr) { + case math_char_number_code: + mval = tex_scan_mathchar(tex_mathcode); + break; + case math_xchar_number_code: + mval = tex_scan_mathchar(umath_mathcode); + break; + /* + case math_uchar_number_code: + mval = tex_scan_mathchar(umathnum_mathcode); + break; + */ + default: + tex_confusion("scan math char, case 1"); + break; + } + dval = tex_fake_math_dict(mval.character_value); + break; + case mathspec_cmd: + mval = tex_get_math_spec(cur_chr); + dval = tex_get_math_dict(cur_chr); + break; + // case math_char_given_cmd: + // mval = tex_mathchar_from_integer(cur_chr, tex_mathcode); + // break; + // case math_char_xgiven_cmd: + // mval = tex_mathchar_from_integer(cur_chr, umath_mathcode); + // break; + case delimiter_number_cmd: + switch (cur_chr) { + case math_delimiter_code: + mval = tex_scan_delimiter_as_mathchar(tex_mathcode); + break; + case math_udelimiter_code: + mval = tex_scan_delimiter_as_mathchar(umath_mathcode); + break; + default: + tex_confusion("scan math char, case 2"); + break; + } + break; + case math_component_cmd: + if (nocomponent) { + goto DEFAULT; + } else { + tex_set_saved_record(saved_math_group_item_pointer, saved_math_pointer, 0, target); + tex_set_saved_record(saved_math_group_all_class, saved_math_class, 0, unset_noad_class); + lmt_save_state.save_stack_data.ptr += saved_math_group_n_of_items; + tex_aux_push_math(math_group, style); + if (usetextfont) { + tex_set_math_text_font(style, usetextfont); + } + tex_aux_math_math_component(cur_list.tail, 0); + tex_finish_math_group(); + return 1; + } + case left_brace_cmd: + goto SCAN_SUBFORMULA; + default: + /*tex + The pointer |p| is placed on |save_stack| while a complex subformula is being + scanned. + */ + DEFAULT: + tex_back_input(cur_tok); + tex_scan_left_brace(); + SCAN_SUBFORMULA: + tex_set_saved_record(saved_math_group_item_pointer, saved_math_pointer, 0, target); + tex_set_saved_record(saved_math_group_all_class, saved_math_class, 0, all); + lmt_save_state.save_stack_data.ptr += saved_math_group_n_of_items; + tex_aux_push_math(math_group, style); + toks = every_math_atom_par; + toks_text = every_math_atom_text; + if (toks) { + tex_begin_token_list(toks, (quarterword) toks_text); + } + if (usetextfont) { + tex_set_math_text_font(style, usetextfont); + } + return 1; + } + node_type(target) = math_char_node; + if (glyph_options_par & glyph_option_no_italic_correction) { + math_kernel_node_set_option(target, math_kernel_no_italic_correction); + } + if (glyph_options_par & glyph_option_no_left_kern) { + math_kernel_node_set_option(target, math_kernel_no_left_pair_kern); + } + if (glyph_options_par & glyph_option_no_right_kern) { + math_kernel_node_set_option(target, math_kernel_no_right_pair_kern); + } + tex_aux_set_math_char(target, &mval, &dval); + return 0; +} + +/*tex + + The |append_math_char| procedure creates a new noad appropriate to a given math code, and + appends it to the current mlist. However, if the math code is sufficiently large, the |cur_chr| + is treated as an active character and nothing is appended. + +*/ + +static void tex_aux_append_math_accent(mathcodeval mval, mathdictval dval) +{ + halfword accent = tex_new_node(accent_noad, bothflexible_accent_subtype); + quarterword subtype = ordinary_noad_subtype; + tex_tail_append(accent); + if (! (mval.character_value == 0 && mval.family_value == 0)) { + halfword q = tex_new_node(math_char_node, 0); + subtype = tex_aux_set_math_char(q, &mval, &dval); + accent_top_character(accent) = q; + } + { + halfword q = tex_new_node(math_char_node, subtype); + noad_nucleus(accent) = q; + tex_aux_scan_math(q, tex_math_style_variant(cur_list.math_style, math_parameter_accent_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class); + } +} + +/*tex + Fences are actually constructs and middle sort of interferes here: we keep a sort of flat fence + sequence so middle ends a group and opens a new one. + +*/ + +static void tex_aux_append_math_fence(halfword fence, quarterword class) +{ + switch (class) { + case open_noad_subtype: + { + tex_aux_push_math(math_fence_group, cur_list.math_style); + node_subtype(fence) = left_fence_side; + node_next(cur_list.head) = fence; + cur_list.tail = fence; + cur_list.delim = fence; + } + break; + case close_noad_subtype: + { + halfword q = tex_aux_finish_math_list(fence); + halfword n = tex_new_node(simple_noad, fenced_noad_subtype); + halfword l = tex_new_node(sub_mlist_node, 0); + tex_aux_unsave_math(); + tex_tail_append(n); + node_subtype(fence) = right_fence_side; + noad_nucleus(n) = l; + noad_options(n) |= noad_option_unpack_list; + kernel_math_list(noad_nucleus(n)) = q; + } + break; + case middle_noad_subtype: + { + halfword q = tex_aux_finish_math_list(fence); + tex_aux_unsave_math(); + tex_aux_push_math(math_fence_group, cur_list.math_style); + node_subtype(fence) = middle_fence_side; + node_next(cur_list.head) = q; + cur_list.tail = fence; + cur_list.delim = fence; + } + break; + } +} + +static void tex_aux_append_math_fence_val(mathcodeval mval, mathdictval dval, quarterword class) +{ + halfword fence = tex_new_node(fence_noad, middle_fence_side); + halfword delimiter = tex_new_node(delimiter_node, mval.class_value); + (void) dval; /* maybe todo */ + fence_delimiter_list(fence) = delimiter; + delimiter_small_family(delimiter) = mval.family_value; + delimiter_small_character(delimiter) = mval.character_value; + delimiter_large_family(delimiter) = mval.family_value; + delimiter_large_character(delimiter) = mval.character_value; + set_noad_classes(fence, mval.class_value); + /* todo : share the next three with the regular fences */ + noad_options(fence) |= noad_option_no_check; + if (class == middle_noad_subtype && cur_group != math_fence_group) { + tex_aux_append_math_fence_val((mathcodeval) { 0, 0, 0 }, (mathdictval) { 0, 0, 0 }, open_noad_subtype); + } + tex_aux_append_math_fence(fence, class); +} + +static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int automatic) +{ + if (mval.class_value == active_math_class_value) { + /*tex An active character is allowed here */ + tex_aux_scan_active_math_char(); + return; + } else { + if (automatic && tex_math_has_class_option(mval.class_value, auto_inject_class_option)) { + switch (mval.class_value) { + case accent_noad_subtype: + tex_aux_append_math_accent(mval, dval); + return; + case open_noad_subtype: + case close_noad_subtype: + case middle_noad_subtype: + tex_aux_append_math_fence_val(mval, dval, mval.class_value); + return; + } + } + { + halfword p = tex_new_node(simple_noad, ordinary_noad_subtype); + halfword q = tex_new_node(math_char_node, 0); + noad_nucleus(p) = q; + if (glyph_options_par & glyph_option_no_italic_correction) { + math_kernel_node_set_option(q, math_kernel_no_italic_correction); + } + node_subtype(p) = tex_aux_set_math_char(q, &mval, &dval); + tex_tail_append(p); + } + } +} + +/*tex + + The |append_math_char_in_text| procedure creates a new node representing a math char in text + code, and appends it to the current list. However, if the math code is sufficiently large, the + |cur_chr| is treated as an active character and nothing is appended. + +*/ + +static void tex_aux_append_math_char_in_text(mathcodeval mval, mathdictval dval) +{ + (void) dval; + if (mval.class_value == active_math_class_value) { + /*tex An active character is allowed here. But why in text mode too. */ + tex_aux_scan_active_math_char(); + } else { + halfword p = tex_new_char_node(glyph_character_subtype, tex_fam_fnt(mval.family_value, text_size), mval.character_value, 1); /* todo: data */ + tex_tail_append(p); + } +} + +void tex_run_math_letter(void) +{ + tex_aux_append_math_char(tex_get_math_code(cur_chr), tex_fake_math_dict(cur_chr), 1); +} + +void tex_run_math_char_number(void) { + /*tex + Both |\char| and |\glyph| get the same treatment. Scanning can change |cur_chr| so we do + that first. We no longer check for active here! + */ + mathcodeval mval = { 0, 0, 0 }; + mathdictval dval = { 0, 0, 0 }; + cur_chr = tex_scan_char_number(0); + mval.character_value = cur_chr; + mval.family_value = (short) cur_fam_par; + // tex_aux_append_math_char(tex_get_math_code(cur_chr), tex_fake_math_dict(cur_chr)); + tex_aux_append_math_char(mval, dval, 1); +} + +void tex_run_math_math_spec(void) +{ + tex_aux_append_math_char(tex_get_math_spec(cur_chr), tex_get_math_dict(cur_chr), 1); +} + +void tex_run_text_math_spec(void) +{ + tex_aux_append_math_char_in_text(tex_get_math_spec(cur_chr), tex_get_math_dict(cur_chr)); +} + +int tex_scan_math_cmd_val(mathcodeval *mval, mathdictval *dval) +{ + do { + tex_get_x_token(); + } while (cur_cmd == spacer_cmd); + switch (cur_cmd) { + // case math_char_given_cmd: + // *mval = tex_mathchar_from_integer(cur_chr, tex_mathcode); + // break; + // case math_char_xgiven_cmd: + // *mval = tex_mathchar_from_integer(cur_chr, umath_mathcode); + // break; + case mathspec_cmd: + *mval = tex_get_math_spec(cur_chr); + break; + case math_char_number_cmd: + switch (cur_chr) { + case math_char_number_code: + *mval = tex_scan_mathchar(tex_mathcode); + break; + case math_xchar_number_code: + *mval = tex_scan_mathchar(umath_mathcode); + break; + case math_dchar_number_code: + *dval = tex_scan_mathdict(); + *mval = tex_scan_mathchar(umath_mathcode); + break; + /* + case math_uchar_number_code: + *mval = tex_scan_mathchar(umathnum_mathcode); + break; + */ + default: + /* no message yet */ + return 0; + } + break; + case letter_cmd: + case other_char_cmd: + mval->character_value = cur_chr; + break; + default: + { + halfword n = 0; + tex_back_input(cur_tok); + n = tex_scan_int(0, NULL); + *mval = tex_mathchar_from_integer(n, umath_mathcode); + } + break; + } + return 1; +} + +int tex_scan_math_code_val(halfword code, mathcodeval *mval, mathdictval *dval) +{ + switch (code) { + case math_char_number_code: + *mval = tex_scan_mathchar(tex_mathcode); + break; + case math_xchar_number_code: + *mval = tex_scan_mathchar(umath_mathcode); + break; + case math_dchar_number_code: + *dval = tex_scan_mathdict(); + *mval = tex_scan_mathchar(umath_mathcode); + break; + /* + case math_uchar_number_code: + *mval = tex_scan_mathchar(umathnum_mathcode); + break; + */ + case math_class_number_code: + { + halfword family = cur_fam_par; + halfword class = tex_scan_int(0, NULL); + tex_scan_math_cmd_val(mval, dval); + mval->class_value = (short) class; + mval->family_value = (short) family; + } + break; + default: + /* no message yet */ + tex_back_input(cur_tok); + return 0; + } + return 1; +} + +void tex_run_text_math_char_number(void) { + mathcodeval mval = { 0, 0, 0 }; + mathdictval dval = { 0, 0, 0 }; + if (tex_scan_math_code_val(cur_chr, &mval, &dval)) { + tex_aux_append_math_char_in_text(mval, dval); + } +} + +void tex_run_math_math_char_number(void) { + mathcodeval mval = { 0, 0, 0 }; + mathdictval dval = { 0, 0, 0 }; + if (tex_scan_math_code_val(cur_chr, &mval, &dval)) { + tex_aux_append_math_char(mval, dval, 1); + } +} + +/*tex We build up an argument to |append_math_char|: */ + +// void tex_run_text_math_char_given(void) { +// tex_aux_append_math_char_in_text(tex_mathchar_from_integer(cur_chr, tex_mathcode)); +// } +// +// void tex_run_math_math_char_given(void) { +// tex_aux_append_math_char(tex_mathchar_from_integer(cur_chr, tex_mathcode)); +// } + +/*tex We build up an argument to |append_math_char| the \LUATEX\ way: */ + +// void tex_run_text_math_char_xgiven(void) { +// tex_aux_append_math_char_in_text(tex_mathchar_from_integer(cur_chr, umath_mathcode)); +// } +// +// void tex_run_math_math_char_xgiven(void) { +// tex_aux_append_math_char(tex_mathchar_from_integer(cur_chr, umath_mathcode)); +// } + +void tex_run_math_delimiter_number(void) { + switch (cur_chr) { + case math_delimiter_code: + tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(tex_mathcode), (mathdictval) { 0, 0, 0 }, 0); + break; + case math_udelimiter_code: + tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(umath_mathcode), (mathdictval) { 0, 0, 0 }, 0); + break; + } +} + +/*tex + In original \TEX\ the subtype overlaps the class. Here we are more strict: a subtype is the + main class as in original \TEX\ but we also have overloads: main, left and right. The subtype + drives the rendering, the others the spacing etc. +*/ + +static void tex_aux_math_math_component(halfword target, int append) +{ + quarterword subtype = unset_noad_class; + quarterword allclass = unset_noad_class; + halfword style = cur_list.math_style; + int usetextfont = math_atom_no_font_option; + reset_noad_classes(target); + switch (cur_chr) { + case math_component_ordinary_code: + subtype = ordinary_noad_subtype; + break; + case math_component_operator_code: + subtype = operator_noad_subtype; + break; + case math_component_binary_code: + subtype = binary_noad_subtype; + break; + case math_component_relation_code: + subtype = relation_noad_subtype; + break; + case math_component_open_code: + subtype = open_noad_subtype; + break; + case math_component_close_code: + subtype = close_noad_subtype; + break; + case math_component_punctuation_code: + subtype = punctuation_noad_subtype; + break; + case math_component_variable_code: + subtype = variable_noad_subtype; + break; + case math_component_inner_code: + subtype = inner_noad_subtype; + break; + case math_component_under_code: + subtype = under_noad_subtype; + style = tex_math_style_variant(style, math_parameter_under_line_variant); + break; + case math_component_over_code: + subtype = over_noad_subtype; + style = tex_math_style_variant(style, math_parameter_over_line_variant); + break; + case math_component_fraction_code: + subtype = fraction_noad_subtype; + break; + case math_component_radical_code: + subtype = radical_noad_subtype; + break; + case math_component_middle_code: + subtype = middle_noad_subtype; + break; + case math_component_accent_code: + subtype = accent_noad_subtype; + break; + case math_component_fenced_code: + subtype = fenced_noad_subtype; + break; + case math_component_ghost_code: + subtype = ghost_noad_subtype; + break; + case math_component_atom_code: + { + halfword attrlist = null; + while (1) { + switch (tex_scan_character("custnmaolprvCUSTNMAOLPRV", 0, 1, 0)) { + case 'a': case 'A': + switch (tex_scan_character("ltLT", 0, 0, 0)) { + case 't': case 'T': + if (tex_scan_mandate_keyword("attr", 2)) { + attrlist = tex_scan_attribute(attrlist); + } + break; + case 'l': case 'L': + if (tex_scan_mandate_keyword("all", 2)) { + allclass = (quarterword) tex_scan_math_class_number(0); + if (! valid_math_class_code(subtype)) { + allclass = unset_noad_class; + } + } + break; + default: + tex_aux_show_keyword_error("attr|all"); + goto DONE; + } + break; + case 'l': case 'L': + switch (tex_scan_character("ieIE", 0, 0, 0)) { + case 'e': case 'E': + if (tex_scan_mandate_keyword("leftclass", 2)) { + halfword c = tex_scan_math_class_number(0); + if (! valid_math_class_code(subtype)) { + c = ordinary_noad_subtype; + } + set_noad_left_class(target, c); + } + break; + case 'i': case 'I': + if (tex_scan_mandate_keyword("limits", 2)) { + noad_options(target) |= noad_option_limits; + } + break; + default: + tex_aux_show_keyword_error("leftclass|limits"); + goto DONE; + } + break; + case 'r': case 'R': + if (tex_scan_mandate_keyword("rightclass", 1)) { + halfword c = tex_scan_math_class_number(0); + if (! valid_math_class_code(c)) { + c = ordinary_noad_subtype; + } + set_noad_right_class(target, c); + } + break; + case 'c': case 'C': + if (tex_scan_mandate_keyword("class", 1)) { + subtype = (quarterword) tex_scan_math_class_number(0); + if (! valid_math_class_code(subtype)) { + subtype = ordinary_noad_subtype; + } + set_noad_main_class(target, subtype); + } + break; + case 'u': case 'U': + /*tex A bit over the top, three steps but a push back is still worse. We can scan for 'un'. */ + if (tex_scan_character("nN", 0, 0, 0)) { + switch (tex_scan_character("prPR", 0, 0, 0)) { + case 'p': case 'P': + if (tex_scan_mandate_keyword("unpack", 3)) { + noad_options(target) |= noad_option_unpack_list; + } + break; + case 'r': case 'R': + if (tex_scan_mandate_keyword("unroll", 3)) { + noad_options(target) |= noad_option_unroll_list; + } + break; + default: + tex_aux_show_keyword_error("unpack|unroll"); + goto DONE; + } + } + break; + case 's': case 'S': + if (tex_scan_mandate_keyword("source", 1)) { + noad_source(target) = tex_scan_int(0, NULL); + } + break; + case 't': case 'T': + if (tex_scan_mandate_keyword("textfont", 1)) { + usetextfont = math_atom_text_font_option; + } + break; + case 'm': case 'M': + if (tex_scan_mandate_keyword("mathfont", 1)) { + usetextfont = math_atom_math_font_option; + } + break; + case 'n': case 'N': + /*tex A bit over the top, three steps but a push back is still worse. We can scan for 'no'. */ + if (tex_scan_character("oO", 0, 0, 0)) { + switch (tex_scan_character("loLO", 0, 0, 0)) { + case 'l': case 'L': + if (tex_scan_mandate_keyword("nolimits", 3)) { + noad_options(target) |= noad_option_no_limits; + } + break; + case 'o': case 'O': + if (tex_scan_mandate_keyword("nooverflow", 3)) { + noad_options(target) |= noad_option_no_overflow; + } + break; + default: + tex_aux_show_keyword_error("nolimits|nooverflow"); + goto DONE; + } + } + break; + case 'o': case 'O': + /* no names, just numbers, we might also do that with other noads */ + if (tex_scan_mandate_keyword("options", 1)) { + noad_options(target) = tex_scan_int(0, NULL); + } + break; + case 'v': case 'V': + if (tex_scan_mandate_keyword("void", 1)) { + noad_options(target) |= noad_option_void; + } + break; + case 'p': case 'P': + if (tex_scan_mandate_keyword("phantom", 1)) { + noad_options(target) |= noad_option_phantom; + } + break; + default: + goto DONE; + } + } + DONE: + if (attrlist) { + tex_attach_attribute_list_attribute(target, attrlist); + } + if (subtype == unset_noad_class) { + if (get_noad_left_class(target) != unset_noad_class && get_noad_right_class(target) != unset_noad_class) { + subtype = ordinary_noad_subtype; + } else { + /* mandate, maybe we will just force a keyword */ + subtype = (quarterword) tex_scan_math_class_number(0); + } + } + } + break; + } + if (! valid_math_class_code(subtype)) { + subtype = ordinary_noad_subtype; + } + /*tex + Now we can scan for the content: + */ + { + halfword content = tex_new_node(math_char_node, 0); + noad_nucleus(target) = content; + node_subtype(target) = subtype; + if (append) { + tex_tail_append(target); + } + tex_aux_scan_math(content, style, usetextfont, 0, 0, 0, subtype, allclass); + } +} + +void tex_run_math_math_component(void) +{ + halfword n = tex_new_node(simple_noad, ordinary_noad_subtype); + tex_aux_math_math_component(n, 1); +} + +int tex_is_math_disc(halfword n) +{ + return + n && node_type(n) == hlist_node && box_list(n) && node_type(box_list(n)) == disc_node && + disc_class(box_list(n)) != unset_disc_class && ! node_next(box_list(n)); +} + +halfword tex_math_make_disc(halfword d) +{ + halfword q = tex_new_node(sub_mlist_node, 0); + halfword n = tex_new_node(simple_noad, (quarterword) disc_class(d)); + kernel_math_list(q) = d; + noad_nucleus(n) = q; + noad_options(n) = noad_option_unpack_list; + return n; +} + +/*tex + Easiest is to permit all modifiers and just ignore those that make no sense. We then can + stepwise support whatever modifier we like later on. +*/ + +void tex_run_math_modifier(void) +{ + halfword tail = cur_list.tail; + if (cur_list.head != tail && node_type(tail) == simple_noad) { // maybe all + switch (cur_chr) { + case adapt_to_left_modifier_code: + noad_options(tail) = unset_option(noad_options(tail), noad_option_adapt_to_right_size); + noad_options(tail) |= noad_option_adapt_to_left_size; + break; + case adapt_to_right_modifier_code: + noad_options(tail) = unset_option(noad_options(tail), noad_option_adapt_to_left_size); + noad_options(tail) |= noad_option_adapt_to_right_size; + break; + /* todo: actually this one can also be used for other types */ + case axis_modifier_code: + noad_options(tail) |= noad_option_axis; + break; + case no_axis_modifier_code: + noad_options(tail) |= noad_option_no_axis; + break; + case phantom_modifier_code: + noad_options(tail) |= noad_option_phantom; + break; + case void_modifier_code: + noad_options(tail) |= noad_option_void; + break; + case source_modifier_code: + noad_source(tail) = tex_scan_int(0, NULL); + break; + case openup_height_modifier_code: + noad_options(tail) |= noad_option_openup_height; + noad_height(tail) = tex_scan_dimen(0, 0, 0, 0, NULL); + break; + case openup_depth_modifier_code: + noad_options(tail) |= noad_option_openup_depth; + noad_depth(tail) = tex_scan_dimen(0, 0, 0, 0, NULL); + break; + case display_limits_modifier_code: + noad_options(tail) = unset_option(noad_options(tail), noad_option_limits | noad_option_no_limits); + break; + case limits_modifier_code: + noad_options(tail) = unset_option(noad_options(tail), noad_option_no_limits); + noad_options(tail) |= noad_option_limits; + break; + case no_limits_modifier_code: + noad_options(tail) = unset_option(noad_options(tail), noad_option_limits); + noad_options(tail) |= noad_option_no_limits; + break; + } + } +} + +/*tex + + Delimiter fields of noads are filled in by the |scan_delimiter| routine. The first parameter + of this procedure is the |mem| address where the delimiter is to be placed; the second tells + if this delimiter follows |\radical| or not. + +*/ + +static void tex_aux_scan_delimiter(halfword target, int code, int class) +{ + delcodeval dval = tex_no_del_code(); + mathcodeval mval = tex_no_math_code(); + switch (code) { + case no_mathcode: + /* can be integrated */ + do { + tex_get_x_token(); + } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd); + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + dval = tex_get_del_code(cur_chr); + if (tex_has_del_code(dval)) { + goto REALDELIMITER; + } else { + mval = tex_get_math_code(cur_chr); + goto FAKEDELIMITER; + } + case delimiter_number_cmd: + switch (cur_chr) { + case math_delimiter_code: + /*tex |\delimiter| */ + dval = tex_aux_scan_extdef_del_code(tex_mathcode, 1); + break; + case math_udelimiter_code: + /*tex |\Udelimiter| */ + dval = tex_aux_scan_extdef_del_code(umath_mathcode, 1); + break; + default: + tex_confusion("scan delimiter, case 1"); + break; + } + goto REALDELIMITER; + case mathspec_cmd: + mval = tex_get_math_spec(cur_chr); + goto FAKEDELIMITER; + case math_char_number_cmd: + switch (cur_chr) { + case math_char_number_code: + mval = tex_scan_mathchar(tex_mathcode); + break; + case math_xchar_number_code: + mval = tex_scan_mathchar(umath_mathcode); + break; + /* + case math_uchar_number_code: + mval = tex_scan_mathchar(umathnum_mathcode); + break; + */ + default: + tex_confusion("scan math char, case 1"); + break; + } + goto FAKEDELIMITER; + } + break; + case tex_mathcode: + /*tex |\radical| */ + dval = tex_aux_scan_extdef_del_code(tex_mathcode, 1); + goto REALDELIMITER; + case umath_mathcode: + /*tex |\Uradical| */ + dval = tex_aux_scan_extdef_del_code(umath_mathcode, 0); + goto REALDELIMITER; + default: + tex_confusion("scan delimiter, case 2"); + goto REALDELIMITER; + } + FAKEDELIMITER: + if (class != unset_noad_class) { + mval.class_value = (short) class; + } + dval.small = mval; + dval.large = mval; + REALDELIMITER: + if (! target) { + return; + } else if (tex_has_del_code(dval)) { + node_subtype(target) = dval.small.class_value; + delimiter_small_family(target) = dval.small.family_value; + delimiter_small_character(target) = dval.small.character_value; + delimiter_large_family(target) = dval.large.family_value; + delimiter_large_character(target) = dval.large.character_value; + } else { + tex_back_input(cur_tok); + tex_handle_error( + normal_error_type, + "Missing delimiter (. inserted)", + "I was expecting to see something like '(' or '\\{' or '\\}' here. Acceptable\n" + "delimiters are characters whose \\delcode is nonnegative, or you can use\n" + "'\\delimiter <delimiter code>'." + ); + node_subtype(target) = unset_noad_class; + delimiter_small_family(target) = 0; + delimiter_small_character(target) = 0; + delimiter_large_family(target) = 0; + delimiter_large_character(target) = 0; + } + return; +} + +void tex_run_math_radical(void) +{ + halfword code = cur_chr; + halfword options = 0; + halfword radical = tex_new_node(radical_noad, (quarterword) code); + halfword style = yet_unset_math_style; + halfword variant = 0; /* quad, harmless */ + halfword attrlist = null; + tex_tail_append(radical); + /* only kewords to UI ones? */ + while (1) { + switch (tex_scan_character("abeswlmrhndABESWLMRHDN", 0, 1, 0)) { + case 0: + goto DONE; + case 'a': case 'A': + if (tex_scan_mandate_keyword("attr", 1)) { + attrlist = tex_scan_attribute(attrlist); + } + break; + case 'e': case 'E': + if (tex_scan_mandate_keyword("exact", 1)) { + options = options | noad_option_exact; + } + break; + case 's': case 'S': + switch (tex_scan_character("toTO", 0, 0, 0)) { + case 't': case 'T': + if (tex_scan_mandate_keyword("style", 2)) { + switch (code) { + case normal_radical_subtype: + case radical_radical_subtype: + case root_radical_subtype: + case rooted_radical_subtype: + case delimited_radical_subtype: + style = tex_scan_math_style_identifier(1, 0); + break; + default: + /* ignore */ + break; + } + } + break; + case 'o': case 'O': + if (tex_scan_mandate_keyword("source", 2)) { + noad_source(radical) = tex_scan_int(0, NULL); + } + break; + default: + tex_aux_show_keyword_error("style|source"); + goto DONE; + } + break; + case 'w': case 'W': + if (tex_scan_mandate_keyword("width", 1)) { + noad_width(radical) = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'd': case 'D': + if (tex_scan_mandate_keyword("depth", 1)) { + radical_depth(radical) = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'h': case 'H': + if (tex_scan_mandate_keyword("height", 1)) { + radical_height(radical) = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'l': case 'L': + if (tex_scan_mandate_keyword("left", 1)) { + options = options | noad_option_left; + } + break; + case 'm': case 'M': + if (tex_scan_mandate_keyword("middle", 1)) { + options = options | noad_option_middle; + } + break; + case 'r': case 'R': + if (tex_scan_mandate_keyword("right", 1)) { + options = options | noad_option_right; + } + break; + case 'n': case 'N': + if (tex_scan_mandate_keyword("nooverflow", 1)) { + options |= noad_option_no_overflow; + } + break; + default: + goto DONE; + } + } + DONE: + if (style == yet_unset_math_style) { + switch (code) { + case normal_radical_subtype: + case radical_radical_subtype: + case root_radical_subtype: + variant = math_parameter_radical_variant; + break; + case under_delimiter_radical_subtype: + variant = math_parameter_under_delimiter_variant; + break; + case over_delimiter_radical_subtype: + variant = math_parameter_over_delimiter_variant; + break; + case delimiter_under_radical_subtype: + variant = math_parameter_delimiter_under_variant; + break; + case delimiter_over_radical_subtype: + variant = math_parameter_delimiter_over_variant; + break; + case delimited_radical_subtype: + variant = math_parameter_radical_variant; /* math_parameter_delimited_variant */ + break; + case h_extensible_radical_subtype: + variant = math_parameter_h_extensible_variant; + break; + } + style = variant ? tex_math_style_variant(cur_list.math_style, variant) : cur_list.math_style; + } + if (attrlist) { + tex_attach_attribute_list_attribute(radical, attrlist); + } + noad_options(radical) = options; + set_noad_style(radical, style); + { + switch (code) { + case normal_radical_subtype: + { + halfword left = tex_new_node(delimiter_node, 0); + radical_left_delimiter(radical) = left; + tex_aux_scan_delimiter(left, tex_mathcode, unset_noad_class); + } + break; + case radical_radical_subtype: + case root_radical_subtype: + case rooted_radical_subtype: + case delimited_radical_subtype: + { + halfword left = tex_new_node(delimiter_node, 0); + radical_left_delimiter(radical) = left; + tex_aux_scan_delimiter(left, umath_mathcode, unset_noad_class); + } + switch (code) { + case rooted_radical_subtype: + case delimited_radical_subtype: + { + halfword right = tex_new_node(delimiter_node, 0); + radical_right_delimiter(radical) = right; + tex_aux_scan_delimiter(right, umath_mathcode, unset_noad_class); + } + } + break; + case under_delimiter_radical_subtype: + case over_delimiter_radical_subtype: + case delimiter_under_radical_subtype: + case delimiter_over_radical_subtype: + case h_extensible_radical_subtype: + { + halfword left = tex_new_node(delimiter_node, 0); + radical_left_delimiter(radical) = left; + tex_aux_scan_delimiter(left, umath_mathcode, unset_noad_class); + } + break; + default: + tex_confusion("scan math radical"); + break; + } + } + switch (code) { + case h_extensible_radical_subtype: + /*tex type will change */ + { + halfword q = tex_new_node(sub_box_node, 0); + noad_nucleus(radical) = q; + break; + } + case root_radical_subtype: + case rooted_radical_subtype: + /*tex + The trick with the |node_next(q)| is used by |scan_math| to decide whether it needs to + go on. This code looks a bit weird, is it okay? So, here we directly pick up the two + lists while in choices we go through the somewhat complex \quote {complete} group based + mechanism. + */ + { + halfword q = tex_new_node(math_char_node, 0); + node_next(q) = radical; /* trick */ + radical_degree(radical) = q; + if (! tex_aux_scan_math(radical_degree(radical), tex_math_style_variant(style, math_parameter_degree_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class)) { + /*tex Actually it's always scriptscript I guess. */ + node_next(radical_degree(radical)) = null; + q = tex_new_node(math_char_node, 0); + noad_nucleus(radical) = q; + if (noad_style(radical) != style) { + /* We keep the style in the node for diagnostics. */ + tex_back_input(token_val(math_style_cmd, noad_style(radical))); + } + tex_aux_scan_math(q, tex_math_style_variant(style, math_parameter_radical_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class); + } + break; + } + default : + { + halfword q = tex_new_node(math_char_node, 0); + noad_nucleus(radical) = q; + tex_aux_scan_math(q, tex_math_style_variant(style, variant ? variant : math_parameter_radical_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class); + break; + } + } +} + +void tex_run_math_accent(void) +{ + mathcodeval t = tex_no_math_code(); + mathcodeval b = tex_no_math_code(); + mathcodeval o = tex_no_math_code(); + halfword code = cur_chr; + halfword accent = tex_new_node(accent_noad, bothflexible_accent_subtype); + quarterword subtype = ordinary_noad_subtype; + halfword attrlist = null; + if (cur_cmd == accent_cmd) { + tex_handle_error( + normal_error_type, + "Please use \\mathaccent for accents in math mode", + "I'm changing \\accent to \\mathaccent here; wish me luck. (Accents are not the\n" + "same in formulas as they are in text.)" ); + } + tex_tail_append(accent); + switch (code) { + case math_accent_code: + /*tex |\mathaccent| */ + t = tex_scan_mathchar(tex_mathcode); + break; + case math_uaccent_code: + /*tex |\Umathaccent| */ + while (1) { + switch (tex_scan_character("ansfASFN", 0, 0, 0)) { + case 'a': case 'A': + if (tex_scan_mandate_keyword("attr", 1)) { + attrlist = tex_scan_attribute(attrlist); + } + break; + case 's': case 'S': + if (tex_scan_mandate_keyword("source", 1)) { + noad_source(accent) = tex_scan_int(0, NULL); + } + break; + case 'f': case 'F': + if (tex_scan_mandate_keyword("fraction", 1)) { + accent_fraction(accent) = tex_scan_int(0, NULL); + } + break; + case 'n': case 'N': + if (tex_scan_mandate_keyword("nooverflow", 1)) { + /*tex + Actually there never is an overflow but for consistency we do + accept this key. Mayebe in the future it will be used. + */ + noad_options(accent) |= noad_option_no_overflow; + } + break; + default: + goto DONE; + } + } + DONE: + /* todo: integrate in the above */ + if (tex_scan_keyword("fixed")) { + /*tex top */ + node_subtype(accent) = fixedtop_accent_subtype; + t = tex_scan_mathchar(umath_mathcode); + } else if (tex_scan_keyword("both")) { + /*tex top bottom */ + if (tex_scan_keyword("fixed")) { + node_subtype(accent) = fixedtop_accent_subtype; + } + t = tex_scan_mathchar(umath_mathcode); + if (tex_scan_keyword("fixed")) { + node_subtype(accent) = fixedboth_accent_subtype; + } + b = tex_scan_mathchar(umath_mathcode); + } else if (tex_scan_keyword("bottom")) { + /*tex bottom */ + if (tex_scan_keyword("fixed")) { + node_subtype(accent) = fixedbottom_accent_subtype; + } + b = tex_scan_mathchar(umath_mathcode); + } else if (tex_scan_keyword("top")) { + /*tex top */ + if (tex_scan_keyword("fixed")) { + node_subtype(accent) = fixedtop_accent_subtype; + } + t = tex_scan_mathchar(umath_mathcode); + } else if (tex_scan_keyword("overlay")) { + /* overlay */ + if (tex_scan_keyword("fixed")) { + node_subtype(accent) = fixedtop_accent_subtype; + } + o = tex_scan_mathchar(umath_mathcode); + } else { + /*tex top */ + t = tex_scan_mathchar(umath_mathcode); + } + break; + default: + tex_confusion("scan math accent"); + } + if (attrlist) { + tex_attach_attribute_list_attribute(accent, attrlist); + } + if (! (t.character_value == 0 && t.family_value == 0)) { + halfword n = tex_new_node(math_char_node, 0); + subtype = tex_aux_set_math_char(n, &t, NULL); + accent_top_character(accent) = n; + } + if (! (b.character_value == 0 && b.family_value == 0)) { + halfword n = tex_new_node(math_char_node, 0); + subtype = tex_aux_set_math_char(n, &b, NULL); + accent_bottom_character(accent) = n; + } + if (! (o.character_value == 0 && o.family_value == 0)) { + halfword n = tex_new_node(math_char_node, 0); + subtype = tex_aux_set_math_char(n, &o, NULL); + accent_middle_character(accent) = n; + } + { + halfword n = tex_new_node(math_char_node, subtype); + noad_nucleus(accent) = n; + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_accent_variant), 0, 0, 0, 0, unset_noad_class, unset_noad_class); + } +} + +/*tex + + The routine that scans the four mlists of a |\mathchoice| is very much like the routine that + builds discretionary nodes. Finally, the |\mathchoice| primitive creates a |choice_node|, + which has special subfields |display_mlist|, |text_mlist|, |script_mlist|, and + |script_script_mlist| pointing to the mlists for each style. + +*/ + +void tex_run_math_choice(void) { + switch (cur_chr) { + case math_discretionary_code: + { + halfword n = tex_new_node(choice_node, discretionary_choice_subtype); + choice_class(n) = unset_noad_class; + while (1) { + switch (tex_scan_character("cC", 0, 1, 0)) { + case 0: + goto DONE; + case 'c': case 'C': + if (tex_scan_mandate_keyword("class", 1)) { + choice_class(n) = tex_scan_math_class_number(0); + } + break; + default: + goto DONE; + } + } + DONE: + tex_tail_append(n); + tex_set_saved_record(saved_choice_item_count, saved_choices_count, 0, math_pre_break_choice); + lmt_save_state.save_stack_data.ptr += saved_choice_n_of_items; + tex_aux_push_math(math_choice_group, cur_list.math_style); + tex_scan_left_brace(); + break; + } + case math_choice_code: + /*tex |\mathchoice| */ + { + halfword n = tex_new_node(choice_node, normal_choice_subtype); + tex_tail_append(n); + tex_set_saved_record(saved_choice_item_count, saved_choices_count, 0, math_display_choice); + lmt_save_state.save_stack_data.ptr += saved_choice_n_of_items; + tex_aux_push_math(math_choice_group, display_style); + tex_scan_left_brace(); + break; + } + case math_ustack_code: + /*tex |\Ustack| */ + { + // halfword m = tex_new_node(sub_mlist_node, 0); /* was for some reason a math_char_node */ + halfword m = tex_new_node(math_char_node, 0); + halfword n = tex_new_node(simple_noad, ordinary_noad_subtype); + halfword s = tex_math_style_variant(cur_list.math_style, math_parameter_stack_variant); + tex_tail_append(n); + noad_nucleus(n) = m; + tex_scan_left_brace(); + tex_set_saved_record(0, saved_math_pointer, 0, m); + ++lmt_save_state.save_stack_data.ptr; + tex_aux_push_math(math_group, s); + break; + } + } +} + +int tex_current_math_style(void) +{ + return (abs(cur_list.mode) == mmode) ? cur_list.math_style : -1; +} + +int tex_current_math_main_style(void) +{ + return (abs(cur_list.mode) == mmode) ? cur_list.math_main_style : -1; +} + +void tex_finish_math_choice(void) +{ + halfword content; + tex_aux_unsave_math(); + content = tex_aux_finish_math_list(null); + /* We should just count and not rely on the next hackery test: */ + if (saved_type(saved_choice_item_count - saved_choice_n_of_items) == saved_choices_count) { + int choice = saved_value(saved_choice_item_count - saved_choice_n_of_items); + int style = cur_list.math_style; + switch (node_subtype(cur_list.tail)) { + case normal_choice_subtype: + switch (choice) { + case math_display_choice: + choice_display_mlist(cur_list.tail) = content; + style = text_style; + break; + case math_text_choice: + choice_text_mlist(cur_list.tail) = content; + style = script_style; + break; + case math_script_choice: + choice_script_mlist(cur_list.tail) = content; + style = script_script_style; + break; + case math_script_script_choice: + choice_script_script_mlist(cur_list.tail) = content; + lmt_save_state.save_stack_data.ptr -= saved_choice_n_of_items; + return; + } + break; + case discretionary_choice_subtype: + switch (choice) { + case math_pre_break_choice: + choice_pre_break(cur_list.tail) = content; + style = display_style; + break; + case math_post_break_choice: + choice_post_break(cur_list.tail) = content; + style = text_style; + break; + case math_no_break_choice: + choice_no_break(cur_list.tail) = content; + style = script_style; + lmt_save_state.save_stack_data.ptr -= saved_choice_n_of_items; + return; + } + break; + } + tex_set_saved_record(saved_choice_item_count - saved_choice_n_of_items, saved_choices_count, 0, choice + 1); + tex_aux_push_math(math_choice_group, style); + tex_scan_left_brace(); + } else { + tex_confusion("scan build choices"); + } +} + +void tex_finish_math_fraction(void) +{ + halfword content; + tex_aux_unsave_math(); + content = tex_aux_finish_math_list(null); + if (saved_type(saved_fraction_item_variant - saved_fraction_n_of_items) == saved_fraction_variant) { + halfword over = saved_value(saved_fraction_item_variant - saved_fraction_n_of_items); + halfword autostyle = saved_value(saved_fraction_item_autostyle - saved_fraction_n_of_items); + halfword userstyle = saved_value(saved_fraction_item_userstyle - saved_fraction_n_of_items); + halfword fraction = cur_list.tail; + set_noad_style(fraction, userstyle); + switch (over) { + case math_numerator_above: + kernel_math_list(fraction_numerator(fraction)) = content; + break; + case math_denominator_above: + kernel_math_list(fraction_denominator(fraction)) = content; + lmt_save_state.save_stack_data.ptr -= saved_fraction_n_of_items; + return; + } + tex_set_saved_record(saved_fraction_item_variant - saved_fraction_n_of_items, saved_fraction_variant, 0, over + 1); + tex_aux_push_math(math_fraction_group, autostyle); + tex_scan_left_brace(); + } else { + tex_confusion("scan build fraction"); + } +} + +void tex_finish_math_operator(void) +{ + halfword content; + tex_aux_unsave_math(); + content = tex_aux_finish_math_list(null); + if (saved_type(saved_operator_item_variant - saved_operator_n_of_items) == saved_operator_variant) { + halfword over = saved_value(saved_operator_item_variant - saved_operator_n_of_items); + halfword fenced = cur_list.tail; + switch (over) { + case math_limits_top: + kernel_math_list(fence_delimiter_top(fenced)) = content; + break; + case math_limits_bottom: + kernel_math_list(fence_delimiter_bottom(fenced)) = content; + lmt_save_state.save_stack_data.ptr -= saved_operator_n_of_items; + return; + } + tex_set_saved_record(saved_operator_item_variant - saved_operator_n_of_items, saved_operator_variant, 0, over + 1); + tex_aux_push_math(math_operator_group, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant)); + tex_scan_left_brace(); + } else { + tex_confusion("scan build operator"); + } +} + +/*tex + + Subscripts and superscripts are attached to the previous nucleus by the action procedure called + |sub_sup|. + +*/ + +# define scripts_allowed(A) ((node_type((A)) >= simple_noad) && (node_type((A)) < fence_noad)) + +static halfword tex_math_double_atom(void) +{ + halfword tail = tex_new_node(simple_noad, ordinary_noad_subtype); + halfword list = tex_new_node(sub_mlist_node, 0); + tex_tail_append(tail); + if (math_double_script_mode_par >= 0) { + node_subtype(tail) = (math_double_script_mode_par >> 16) & 0xFF; + noad_class_left(tail) = (math_double_script_mode_par >> 8) & 0xFF; + noad_class_right(tail) = (math_double_script_mode_par >> 0) & 0xFF; + } + noad_nucleus(tail) = list; + return tail; +} + +void tex_run_math_script(void) +{ + int code = cur_chr; + halfword tail = cur_list.tail; + switch (cur_cmd) { + case subscript_cmd: + code = math_sub_script_code; + break; + case superscript_cmd: + code = math_super_script_code; + break; + } + switch (code) { + case math_no_script_code: + { + halfword glue = tex_new_glue_node(zero_glue, conditional_math_glue); + tex_tail_append(glue); + tex_add_glue_option(glue, glue_option_no_auto_break); + } + return; + case math_no_ruling_code: + { + halfword glue = tex_new_glue_node(zero_glue, rulebased_math_glue); + tex_tail_append(glue); + tex_add_glue_option(glue, glue_option_no_auto_break); + } + return; + case math_sub_script_code: + tex_get_token(); + if (cur_tok == underscore_token || cur_cmd == subscript_cmd) { + tex_get_token(); + if (cur_tok == underscore_token || cur_cmd == subscript_cmd) { + tex_get_token(); + if (cur_tok == underscore_token || cur_cmd == subscript_cmd) { + code = math_shifted_sub_pre_script_code; + } else { + tex_back_input(cur_tok); + code = math_shifted_sub_script_code; + } + } else { + tex_back_input(cur_tok); + code = math_sub_pre_script_code; + } + } else { + tex_back_input(cur_tok); + } + break; + case math_super_script_code: + tex_get_token(); + if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) { + tex_get_token(); + if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) { + tex_get_token(); + if (cur_tok == circumflex_token || cur_cmd == superscript_cmd) { + code = math_shifted_super_pre_script_code; + } else { + tex_back_input(cur_tok); + code = math_shifted_super_script_code; + } + } else { + tex_back_input(cur_tok); + code = math_super_pre_script_code; + } + } else { + tex_back_input(cur_tok); + } + break; + } + if (tail == cur_list.head || (! scripts_allowed(tail))) { + halfword n = tex_new_node(sub_mlist_node, 0); + tail = tex_new_node(simple_noad, ordinary_noad_subtype); + tex_tail_append(tail); + noad_nucleus(tail) = n; + } + switch (code) { + case math_sub_script_code: + case math_no_sub_script_code: + case math_shifted_sub_script_code: + { + if (noad_subscr(tail)) { + tail = tex_math_double_atom(); + if (math_double_script_mode_par < 0) { + tex_handle_error( + normal_error_type, + "Double subscript", + "I treat 'x_1_2' essentially like 'x_1{}_2'." + ); + } + } + switch (code) { + case math_no_sub_script_code: + noad_options(tail) |= noad_option_no_sub_script; + break; + case math_shifted_sub_script_code: + noad_options(tail) |= noad_option_shifted_sub_script; + break; + } + { + halfword n = tex_new_node(math_char_node, 0); + noad_subscr(tail) = n; + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class); + if (! noad_script_order(tail)) { + noad_script_order(tail) = script_subscript_first; + } + } + break; + } + case math_sub_pre_script_code: + case math_no_sub_pre_script_code: + case math_shifted_sub_pre_script_code: + { + if (noad_subprescr(tail)) { + int limitation = node_type(tail) == fraction_noad; /*tex See remark at node definition. */ + tail = tex_math_double_atom(); + if (math_double_script_mode_par < 0) { + tex_handle_error( + normal_error_type, + limitation ? "Fractions take no pre subscript directly" : "Double pre subscript", + "I just ignore it; consider wrapping this element." + ); + } + } + switch (code) { + case math_no_sub_pre_script_code: + noad_options(tail) |= noad_option_no_sub_pre_script; + break; + case math_shifted_sub_pre_script_code: + noad_options(tail) |= noad_option_shifted_sub_pre_script; + break; + } + { + halfword n = tex_new_node(math_char_node, 0); + noad_subprescr(tail) = n; + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_subscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class); + } + break; + } + case math_super_script_code: + case math_no_super_script_code: + case math_shifted_super_script_code: + { + if (noad_supscr(tail)) { + tail = tex_math_double_atom(); + if (math_double_script_mode_par < 0) { + tex_handle_error( + normal_error_type, + "Double superscript", + "I treat 'x^1^2' essentially like 'x^1{}^2'." + ); + } + } + switch (code) { + case math_no_super_script_code: + noad_options(tail) |= noad_option_no_super_script; + break; + case math_shifted_super_script_code: + noad_options(tail) |= noad_option_shifted_super_script; + break; + } + { + halfword n = tex_new_node(math_char_node, 0); + noad_supscr(tail) = n; + if (! noad_script_order(tail)) { + noad_script_order(tail) = script_superscript_first; + } + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class); + } + break; + } + case math_super_pre_script_code: + case math_no_super_pre_script_code: + case math_shifted_super_pre_script_code: + { + if (noad_supprescr(tail)) { + int limitation = node_type(tail) == fraction_noad; /*tex See remark at node definition. */ + tail = tex_math_double_atom(); + if (math_double_script_mode_par < 0) { + tex_handle_error( + normal_error_type, + limitation ? "Fractions take no pre superscript directly" : "Double pre superscript", + "I just ignore it; consider wrapping this element." + ); + } + } + switch (code) { + case math_no_super_script_code: + noad_options(tail) |= noad_option_no_super_pre_script; + break; + case math_shifted_super_pre_script_code: + noad_options(tail) |= noad_option_shifted_super_pre_script; + break; + } + { + halfword n = tex_new_node(math_char_node, 0); + noad_supprescr(tail) = n; + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class); + } + break; + } + case math_prime_script_code: + { + if (noad_prime(tail)) { + tail = tex_math_double_atom(); + if (math_double_script_mode_par < 0) { + tex_handle_error( + normal_error_type, + "Double prime script", + "I'll add a dummy nucleus." + ); + } + } + { + halfword n = tex_new_node(math_char_node, 0); + noad_prime(tail) = n; + if (! noad_script_order(tail)) { + noad_script_order(tail) = script_primescript_first; + } + /* maybe it's own variant */ + tex_aux_scan_math(n, tex_math_style_variant(cur_list.math_style, math_parameter_superscript_variant), 0, 0, 0, 1, unset_noad_class, unset_noad_class); + } + break; + } + } +} + +/*tex + + An operation like |\over| causes the current mlist to go into a state of suspended animation: + |incomplete_noad| points to a |fraction_noad| that contains the mlist-so-far as its numerator, + while the denominator is yet to come. Finally when the mlist is finished, the denominator will + go into the incomplete fraction noad, and that noad will become the whole formula, unless it is + surrounded by |\left| and |\right| delimiters. + + We can probably replace the |incomplete_noad_par| trickery because we can now look back in the + list using the |alink| field. But not now. + +*/ + +void tex_run_math_fraction(void) +{ + /*tex The type of generalized fraction we are scanning: */ + halfword code = cur_chr; + if (cur_list.incomplete_noad) { + /*tex Recovery code. */ + switch (code) { + case math_above_delimited_code: + case math_over_delimited_code: + case math_atop_delimited_code: + case math_u_above_delimited_code: + case math_u_over_delimited_code: + case math_u_atop_delimited_code: + case math_u_skewed_delimited_code: + case math_u_stretched_delimited_code: + tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class); + tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class); + break; + } + switch (code) { + case math_above_code: + case math_above_delimited_code: + case math_u_above_code: + case math_u_above_delimited_code: + tex_scan_dimen(0, 0, 0, 0, NULL); + break; + } + /*tex This is somewhat weird, this error here. */ + tex_handle_error( + normal_error_type, + "Ambiguous; you need another { and }", + "I'm ignoring this fraction specification, since I don't know whether a\n" + "construction like 'x \\over y \\over z' means '{x \\over y} \\over z' or\n" + "'x \\over {y \\over z}'." + ); + } else { + halfword fraction = tex_new_node(fraction_noad, 0); + halfword numerator = tex_new_node(sub_mlist_node, 0); + halfword denominator = null; + halfword autostyle = tex_math_style_variant(cur_list.math_style, math_parameter_fraction_variant); + halfword userstyle = -1; + halfword attrlist = null; + halfword options = 0; + halfword class = fraction_noad_subtype; + halfword rulethickness = preset_rule_thickness; + int ruledone = 0; + fraction_h_factor(fraction) = 1000; + fraction_v_factor(fraction) = 1000; + switch (code) { + case math_above_code: + case math_above_delimited_code: + node_subtype(fraction) = above_fraction_subtype; + goto NEXTSTEP1; + case math_over_code: + case math_over_delimited_code: + node_subtype(fraction) = over_fraction_subtype; + goto NEXTSTEP1; + case math_atop_code: + case math_atop_delimited_code: + node_subtype(fraction) = atop_fraction_subtype; + NEXTSTEP1: + { + cur_list.incomplete_noad = fraction; + fraction_numerator(fraction) = numerator; + kernel_math_list(numerator) = node_next(cur_list.head); + node_next(cur_list.head) = null; + cur_list.tail = cur_list.head; + cur_list.math_style = autostyle; + break; + } + case math_u_above_code: + case math_u_above_delimited_code: + node_subtype(fraction) = above_fraction_subtype; + goto NEXTSTEP2; + case math_u_over_code: + case math_u_over_delimited_code: + node_subtype(fraction) = over_fraction_subtype; + goto NEXTSTEP2; + case math_u_atop_code: + case math_u_atop_delimited_code: + node_subtype(fraction) = atop_fraction_subtype; + goto NEXTSTEP2; + case math_u_skewed_code: + case math_u_skewed_delimited_code: + node_subtype(fraction) = skewed_fraction_subtype; + goto NEXTSTEP2; + case math_u_stretched_code: + case math_u_stretched_delimited_code: + node_subtype(fraction) = stretched_fraction_subtype; + NEXTSTEP2: + { + cur_list.incomplete_noad = null; + denominator = tex_new_node(sub_mlist_node, 0); + tex_tail_append(fraction); + fraction_numerator(fraction) = numerator; + fraction_denominator(fraction) = denominator; + break; + } + } + switch (code) { + case math_u_skewed_code: + case math_u_skewed_delimited_code: + case math_u_stretched_code: + case math_u_stretched_delimited_code: + { + halfword q = tex_new_node(delimiter_node, 0); + fraction_middle_delimiter(fraction) = q; + tex_aux_scan_delimiter(q, no_mathcode, unset_noad_class); + break; + } + } + switch (code) { + case math_above_delimited_code: + case math_over_delimited_code: + case math_atop_delimited_code: + case math_u_above_delimited_code: + case math_u_over_delimited_code: + case math_u_atop_delimited_code: + case math_u_skewed_delimited_code: + case math_u_stretched_delimited_code: + { + halfword left = tex_new_node(delimiter_node, 0); + halfword right = tex_new_node(delimiter_node, 0); + fraction_left_delimiter(fraction) = left; + fraction_right_delimiter(fraction) = right; + tex_aux_scan_delimiter(left, no_mathcode, open_noad_subtype); + tex_aux_scan_delimiter(right, no_mathcode, close_noad_subtype); + break; + } + } + switch (code) { + /*tex We can't have keyword here because of compatibility reasons. */ + case math_above_code: + case math_above_delimited_code: + rulethickness = tex_scan_dimen(0, 0, 0, 0, NULL); + break; + case math_over_code: + case math_over_delimited_code: + rulethickness = preset_rule_thickness; + break; + case math_atop_code: + case math_atop_delimited_code: + break; + /*tex + But here we can! For practical reasons we accept the rule related options + and in principle we cold do with one command. + */ + case math_u_atop_code: + case math_u_atop_delimited_code: + case math_u_above_code: + case math_u_above_delimited_code: + goto OPTIONS; + case math_u_over_code: + case math_u_over_delimited_code: + ruledone = 1; + goto OPTIONS; + case math_u_stretched_code: + case math_u_stretched_delimited_code: + case math_u_skewed_code: + case math_u_skewed_delimited_code: + ruledone = 1; + OPTIONS: + while (1) { + switch (tex_scan_character("acefhnstvACEFHNSTV", 0, 1, 0)) { + case 'a': case 'A': + if (tex_scan_mandate_keyword("attr", 1)) { + attrlist = tex_scan_attribute(attrlist); + } + break; + case 'c': case 'C': + if (tex_scan_mandate_keyword("class", 1)) { + halfword c = (quarterword) tex_scan_math_class_number(0); + if (valid_math_class_code(c)) { + class = c; + } + } + break; + case 'e': case 'E': + if (tex_scan_mandate_keyword("exact", 1)) { + options |= noad_option_exact; + } + break; + case 'n': case 'N': + /*tex A bit over the top, three steps but a push back is still worse. */ + if (tex_scan_character("oO", 0, 0, 0)) { + switch (tex_scan_character("aoAO", 0, 0, 0)) { + case 'a': case 'A': + if (tex_scan_mandate_keyword("noaxis", 3)) { + options |= noad_option_no_axis; + } + break; + case 'o': case 'O': + if (tex_scan_mandate_keyword("nooverflow", 3)) { + options |= noad_option_no_overflow; + } + break; + default: + tex_aux_show_keyword_error("noaxis|nooverflow"); + goto DONE; + } + } + break; + case 't': case 'T': + if (tex_scan_mandate_keyword("thickness", 1)) { + ruledone = 1; + rulethickness = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'f': case 'F': + if (tex_scan_mandate_keyword("font", 1)) { + ruledone = 1; + options |= noad_option_prefer_font_thickness; + } + break; + case 's': case 'S': + switch (tex_scan_character("toTO", 0, 0, 0)) { + case 't': case 'T': + if (tex_scan_mandate_keyword("style", 2)) { + halfword style = tex_scan_math_style_identifier(1, 0); + if (denominator) { + userstyle = style; + } else { + /* just ignore */ + } + } + break; + case 'o': case 'O': + if (tex_scan_mandate_keyword("source", 2)) { + noad_source(fraction) = tex_scan_int(0, NULL); + } + break; + default: + tex_aux_show_keyword_error("style|source"); + goto DONE; + } + break; + case 'h': case 'H': + if (tex_scan_mandate_keyword("hfactor", 1)) { + fraction_h_factor(fraction) = tex_scan_int(0, NULL); + } + break; + case 'v': case 'V': + if (tex_scan_mandate_keyword("vfactor", 1)) { + fraction_v_factor(fraction) = tex_scan_int(0, NULL); + } + break; + default: + goto DONE; + } + } + DONE: + if (! ruledone) { + rulethickness = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + } + fraction_rule_thickness(fraction) = rulethickness; + noad_options(fraction) = options; + set_noad_main_class(fraction, class); + if (attrlist) { + tex_attach_attribute_list_attribute(fraction, attrlist); + } + if (denominator) { + /*tex + In this case we need to pick up two math groups, and after some playing around using + a variant of choices made most sense. + */ + tex_set_saved_record(saved_fraction_item_variant, saved_fraction_variant, 0, math_numerator_above); + tex_set_saved_record(saved_fraction_item_autostyle, saved_fraction_auto_style, 0, autostyle); + tex_set_saved_record(saved_fraction_item_userstyle, saved_fraction_user_style, 0, userstyle); + lmt_save_state.save_stack_data.ptr += saved_fraction_n_of_items; + cur_list.math_flatten = 0; + tex_aux_push_math(math_fraction_group, autostyle); + tex_scan_left_brace(); + } else { + /*tex + This is the pre/post variant. Actually, this variant is the reason why math scanning + code is somewhat complex, this |incomplete_noad| stuff. + */ + } + } +} + +/*tex + + At the end of a math formula or subformula, the |finish_math_list| routine is called upon to + return a halfword to the newly completed mlist, and to pop the nest back to the enclosing + semantic level. The parameter to |finish_math_list|, if not null, points to a |fence_noad| that + ends the current mlist; this |fence_noad| has not yet been appended. + +*/ + +static halfword tex_aux_finish_math_list(halfword p) +{ + halfword q; + if (cur_list.incomplete_noad) { + halfword denominator = fraction_denominator(cur_list.incomplete_noad); + if (denominator) { + node_type(denominator) = sub_mlist_node; + } else { + denominator = tex_new_node(sub_mlist_node, 0); + fraction_denominator(cur_list.incomplete_noad) = denominator; + q = denominator; + } + kernel_math_list(denominator) = node_next(cur_list.head); + if (p) { + halfword numerator = fraction_numerator(cur_list.incomplete_noad); + q = kernel_math_list(numerator); + if ((node_type(q) != fence_noad) || (node_subtype(q) != left_fence_side) || (! cur_list.delim)) { + tex_confusion("right fence"); + } + kernel_math_list(numerator) = node_next(cur_list.delim); + node_next(cur_list.delim) = cur_list.incomplete_noad; + node_next(cur_list.incomplete_noad) = p; + } else { + q = cur_list.incomplete_noad; + } + } else { + node_next(cur_list.tail) = p; + q = node_next(cur_list.head); + } + tex_pop_nest(); + return q; +} + +/*tex + Here traditional \TEX\ does some flattening but it can interfrere. It is for instance needed + in order to find the skew of an accented character which happens at the outer level but that + bit of code now does that recursively. I need to check why the accent was flattened so we + keep the original code here for testing. + + A \CONTEXT\ test case: |$\tilde{x}'$| i.e.\ primes! +*/ + +static void tex_aux_flatten_math_list(halfword parent) +{ + halfword p = kernel_math_list(parent); + if (p && ! node_next(p)) { + switch (node_type(p)) { + case simple_noad: + { + if (! noad_has_following_scripts(p) && tex_math_has_class_option(node_subtype(p), flatten_class_option)) { + halfword n = noad_nucleus(p); + halfword s = parent; + node_type(s) = node_type(n); + tex_math_copy_char_data(s, n, 1); + tex_attach_attribute_list_copy(s, n); + tex_flush_node(p); + } + break; + } + case accent_noad: + { + halfword tail = cur_list.tail; + if (saved_value(saved_math_group_item_pointer) == noad_nucleus(tail) && node_type(tail) == simple_noad) { + switch (node_subtype(tail)) { + case ordinary_noad_subtype: + tex_couple_nodes(node_prev(tail), p); + noad_nucleus(tail) = null; + noad_subscr(tail) = null; + noad_supscr(tail) = null; + noad_prime(tail) = null; + tex_attach_attribute_list_copy(p, tail); + tex_flush_node(tail); + cur_list.tail = p; + break; + } + } + break; + } + } + } +} + +/*tex + + Now at last we're ready to see what happens when a right brace occurs in a math formula. Two + special cases are simplified here: braces are effectively removed when they surround a single + Ord without sub- and/or superscripts, or when they surround an accent that is the nucleus of + an Ord atom. + +*/ + +void tex_finish_math_group(void) +{ + int old_style = cur_list.math_style; + halfword p, parent; + quarterword allclass; + tex_aux_unsave_math(); + lmt_save_state.save_stack_data.ptr -= saved_math_group_n_of_items; + parent = saved_value(saved_math_group_item_pointer); + allclass = (quarterword) saved_value(saved_math_group_all_class); + node_type(parent) = sub_mlist_node; /* can be math_char_node */ + p = tex_aux_finish_math_list(null); /* this incomplete trickery */ + kernel_math_list(parent) = p; + if (cur_list.math_flatten) { + tex_aux_flatten_math_list(parent); + } + /*tex + If needed, here we pickup a next \quote {argument}, so we sort of finish a group and reopen + a new one. It is somewhat curious that we use a character node here. + */ + if (allclass != unset_noad_class) { + while (p) { + if (node_type(p) == simple_noad) { + // node_subtype(p) = allclass; + if (get_noad_main_class(p) == unset_noad_class) { + set_noad_main_class(p, allclass); + } + if (get_noad_left_class(p) == unset_noad_class) { + set_noad_left_class(p, allclass); + } + if (get_noad_right_class(p) == unset_noad_class) { + set_noad_right_class(p, allclass); + } + } + p = node_next(p); + } + /* */ + } + if (node_next(saved_value(saved_math_group_item_pointer)) > 0) { + halfword q = tex_new_node(math_char_node, 0); /* hm */ + noad_nucleus(node_next(saved_value(saved_math_group_item_pointer))) = q; + node_next(saved_value(saved_math_group_item_pointer)) = null; + saved_value(saved_math_group_item_pointer) = q; + tex_aux_scan_math(q, old_style, 0, 0, 0, 0, unset_noad_class, unset_noad_class); + /*tex restart */ + } +} + +/*tex + + We have dealt with all constructions of math mode except |\left| and |\right|, so the picture is + completed by the following sections of the program. The |middle| feature of \ETEX\ allows one or + several |\middle| delimiters to appear between |\left| and |\right|. + +*/ + +void tex_run_math_fence(void) +{ + halfword ht = 0; + halfword dp = 0; + halfword options = 0; + halfword mainclass = unset_noad_class; + halfword leftclass = unset_noad_class; + halfword rightclass = unset_noad_class; + halfword source = 0; + halfword attrlist = null; + quarterword st = (quarterword) cur_chr; + halfword style = cur_list.math_style; + if (math_check_fences_par) { + options |= noad_option_no_check; + } + switch (st) { + case left_operator_side: + case no_fence_side: + break; + case extended_left_fence_side: /*tex |\Uleft| */ + st = left_fence_side; + break; + case extended_middle_fence_side: /*tex |\Umiddle| */ + st = middle_fence_side; + break; + case extended_right_fence_side: /*tex |\Uright| */ + st = right_fence_side; + break; + default : + goto CHECK_PAIRING; + } + while (1) { + /* todo: break down */ + switch (tex_scan_character("hdanlevpcrsuHDANLEVPCRSU", 0, 1, 0)) { + case 0: + goto CHECK_PAIRING; + case 'h': case 'H': + if (tex_scan_mandate_keyword("height", 1)) { + ht = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'd': case 'D': + if (tex_scan_mandate_keyword("depth", 1)) { + dp = tex_scan_dimen(0, 0, 0, 0, NULL); + } + break; + case 'a': case 'A': + switch (tex_scan_character("uxtUXT", 0, 0, 0)) { + case 'u': case 'U': + if (tex_scan_mandate_keyword("auto", 2)) { + options |= noad_option_auto; + } + break; + case 't': case 'T': + if (tex_scan_mandate_keyword("attr", 2)) { + attrlist = tex_scan_attribute(attrlist); + } + break; + case 'x': case 'X': + if (tex_scan_mandate_keyword("axis", 2)) { + options |= noad_option_axis; + } + break; + default: + tex_aux_show_keyword_error("auto|attr|axis"); + goto CHECK_PAIRING; + } + break; + case 'n': case 'N': + switch (tex_scan_character("oO", 0, 0, 0)) { + case 'o': case 'O': + switch (tex_scan_character("alcoALCO", 0, 0, 0)) { + case 'a': case 'A': + if (tex_scan_mandate_keyword("noaxis", 3)) { + options |= noad_option_no_axis; + } + break; + case 'l': case 'L': + if (tex_scan_mandate_keyword("nolimits", 3)) { + options = unset_option(options, noad_option_limits); + options |= noad_option_no_limits; + } + break; + case 'c': case 'C': + if (tex_scan_mandate_keyword("nocheck", 3)) { + options |= noad_option_no_check; + } + break; + case 'o': case 'O': + if (tex_scan_mandate_keyword("nooverflow", 3)) { + options |= noad_option_no_overflow; + } + break; + default: + tex_aux_show_keyword_error("noaxis|nolimits|nocheck|nooverflow"); + goto CHECK_PAIRING; + } + break; + default: + goto CHECK_PAIRING; + } + break; + case 'l': case 'L': + switch (tex_scan_character("ieIE", 0, 0, 0)) { + case 'e': case 'E': + if (tex_scan_mandate_keyword("leftclass", 2)) { + halfword c = tex_scan_math_class_number(0); + // if (! valid_math_class_code(c)) { + if (valid_math_class_code(c)) { + leftclass = c; + } + } + break; + case 'i': case 'I': + if (tex_scan_mandate_keyword("limits", 2)) { + options = unset_option(options, noad_option_no_limits); + options |= noad_option_limits; + } + break; + default: + tex_aux_show_keyword_error("leftclass|limits"); + goto CHECK_PAIRING; + } + break; + case 'e': case 'E': + if (tex_scan_mandate_keyword("exact", 1)) { + options |= noad_option_exact; + } + break; + case 'v': case 'V': + if (tex_scan_mandate_keyword("void", 1)) { + options |= noad_option_void; + } + break; + case 'p': case 'P': + if (tex_scan_mandate_keyword("phantom", 1)) { + options |= noad_option_phantom; + } + break; + case 'c': case 'C': + if (tex_scan_mandate_keyword("class", 1)) { + mainclass = tex_scan_math_class_number(0); + } + break; + case 'r': case 'R': + if (tex_scan_mandate_keyword("rightclass", 1)) { + halfword c = tex_scan_math_class_number(0); + // if (valid_math_class_code(c)) { + if (valid_math_class_code(c)) { + rightclass = c; + } + } + break; + case 's': case 'S': + if (tex_scan_mandate_keyword("source", 1)) { + source = tex_scan_int(0, NULL); + } + break; + default: + goto CHECK_PAIRING; + } + } + CHECK_PAIRING: + switch (st) { + case no_fence_side: + case left_fence_side: + break; + case left_operator_side: + { + /* becomes a class option */ + int indisplay = style == display_style || style == cramped_display_style; + /* options |= noad_option_no_check; */ /*tex Best just expect a dummy right. */ + if (! (has_option(options, noad_option_limits) || has_option(options, noad_option_no_limits))) { + /* otherwise we don't enter the placement function */ + options |= indisplay ? noad_option_limits : noad_option_no_limits; + } + } + break; + default: + if (cur_group != math_fence_group) { + tex_aux_append_math_fence_val((mathcodeval) { 0, 0, 0 }, (mathdictval) { 0, 0, 0 }, open_noad_subtype); + } + switch (cur_group) { + case math_fence_group: + break; + case math_shift_group: + tex_aux_scan_delimiter(null, no_mathcode, unset_noad_class); + if (st == middle_fence_side) { + tex_handle_error( + normal_error_type, + "Extra \\middle", + "I'm ignoring a \\middle that had no matching \\left." + ); + } else { + tex_handle_error( + normal_error_type, + "Extra \\right", + "I'm ignoring a \\right that had no matching \\left." + ); + } + break; + default: + tex_off_save(); + } + } + /*tex + Now we only have a no, left, middle or right case left. + */ + { + halfword fence = tex_new_node(fence_noad, st); + halfword delimiter = tex_new_node(delimiter_node, 0); + halfword autoclass = unset_noad_class; + fence_delimiter_list(fence) = delimiter; + noad_height(fence) = ht; + noad_depth(fence) = dp; + noad_options(fence) = options; + set_noad_classes(fence, mainclass); + if (leftclass != unset_noad_class) { + set_noad_left_class(fence, leftclass); + } + if (rightclass != unset_noad_class) { + set_noad_right_class(fence, rightclass); + } + noad_italic(fence) = 0; + noad_source(fence) = source; + /*tex + By setting this here, we can get rid of the hard coded values in |mlist_to_hlist| which + sort of interfere (or at least confuse) things there. When set, the |leftclass| and + |rightclass| settings win anyway. + */ + if (mainclass == unset_noad_class) { + mainclass = node_subtype(delimiter); + if (mainclass == unset_noad_class || mainclass == ordinary_noad_subtype) { + switch (st) { + case left_fence_side: + mainclass = open_noad_subtype; + break; + case middle_fence_side: + mainclass = middle_noad_subtype; + break; + case right_fence_side: + mainclass = close_noad_subtype; + break; + } + } + set_noad_main_class(fence, mainclass); + } + /* */ + switch (st) { + case left_fence_side: + autoclass = open_noad_subtype; + break; + case middle_fence_side: + autoclass = middle_noad_subtype; /* we need a way to overload this */ + break; + case right_fence_side: + autoclass = close_noad_subtype; + break; + } + /* */ + tex_aux_scan_delimiter(delimiter, no_mathcode, autoclass); + /* */ + if (attrlist) { + tex_attach_attribute_list_attribute(fence, attrlist); + tex_attach_attribute_list_attribute(delimiter, attrlist); + } + switch (st) { + case left_fence_side: + tex_aux_append_math_fence(fence, open_noad_subtype); + break; + case middle_fence_side: + tex_aux_append_math_fence(fence, middle_noad_subtype); + break; + case right_fence_side: + tex_aux_append_math_fence(fence, close_noad_subtype); + break; + case left_operator_side: + { + halfword top = tex_new_node(sub_mlist_node, 0); + halfword bottom = tex_new_node(sub_mlist_node, 0); + fence_delimiter_top(fence) = top; + fence_delimiter_bottom(fence) = bottom; + tex_aux_push_math(math_fence_group, style); + node_next(cur_list.head) = fence; + cur_list.tail = fence; + cur_list.delim = fence; + tex_set_saved_record(saved_operator_item_variant, saved_operator_variant, 0, math_limits_top); + lmt_save_state.save_stack_data.ptr += saved_operator_n_of_items; + tex_aux_push_math(math_operator_group, tex_math_style_variant(style, math_parameter_superscript_variant)); + tex_scan_left_brace(); + } + break; + case no_fence_side: + { + /* halfword n = tex_new_node(simple_noad, math_fences_mode_par ? fenced_noad_subtype : inner_noad_subtype); */ + halfword n = tex_new_node(simple_noad, fenced_noad_subtype); + halfword l = tex_new_node(sub_mlist_node, 0); + tex_tail_append(n); + set_noad_main_class(n, mainclass); /*tex Really needed here! */ + noad_nucleus(n) = l; + kernel_math_list(noad_nucleus(n)) = fence; + } + break; + default: + tex_confusion("left right fence"); + break; + } + } +} + +/*tex + + \TEX\ gets to the following part of the program when the first |$| ending a display has been + scanned. + +*/ + +static void tex_aux_check_second_math_shift(void) +{ + tex_get_x_token(); + if (cur_cmd != math_shift_cmd) { + tex_back_input(cur_tok); + tex_handle_error( + normal_error_type, + "Display math should end with $$", + "The '$' that I just saw supposedly matches a previous '$$'. So I shall assume\n" + "that you typed '$$' both times." + ); + } +} + +static void tex_aux_check_display_math_end(void) +{ + switch (cur_chr) { + case end_display_math_code: + case end_math_mode_code: + return; + } + tex_handle_error( + normal_error_type, + "Display math should end with \\Ustopdisplaymath or \\Ustopmathmode", + "I shall assume that you typed that." + ); +} + +static void tex_aux_check_inline_math_end(void) +{ + switch (cur_chr) { + case end_inline_math_code: + case end_math_mode_code: + return; + } + tex_handle_error( + normal_error_type, + "Inline math should end with \\Ustopmath or \\Ustopmathmode", + "I shall assume that you typed that." + ); +} + +static void tex_aux_resume_after_display(void) +{ + if (cur_group == math_shift_group) { + tex_aux_unsave_math(); + cur_list.prev_graf += 3; + tex_push_nest(); + cur_list.mode = hmode; + cur_list.space_factor = 1000; + /*tex This needs to be intercepted in the display math start! Todo! */ + tex_tail_append(tex_new_par_node(penalty_par_subtype)); + tex_get_x_token(); + if (cur_cmd != spacer_cmd) { + tex_back_input(cur_tok); + } + if (lmt_nest_state.nest_data.ptr == 1) { + lmt_page_filter_callback(after_display_page_context, 0); + tex_build_page(); + } + } else { + tex_confusion("finishing display math"); + } +} + +/*tex + + The fuziest part of math mode processing occurs when a displayed formula is being centered and + placed with an optional equation number. At this time we are in vertical mode (or internal + vertical mode). + + \starttabulate + \NC \type {p} \NC points to the mlist for the formula \NC \NR + \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR + \NC \type {l} \NC is true if there was an |\leqno| (so |a| is a horizontal box) \NC \NR + \stoptabulate + + Per 2022 we ditched display mode in \CONTEXT\ LMTX\ so the code related to display math is now + completely frozen, if only because testing has become unreasonable. There is anyway not much more + to do here. + +*/ + +inline static void tex_aux_inject_display_skip_before(quarterword param, quarterword subtype) +{ + if (param > 0) { + switch (display_skip_mode_par) { + case display_skip_default : + case display_skip_always : + tex_tail_append(tex_new_param_glue_node(param, subtype)); + break; + case display_skip_non_zero: + if (! tex_glue_is_zero(glue_parameter(param))) { + tex_tail_append(tex_new_param_glue_node(param, subtype)); + } + break; + case display_skip_ignore: + break; + default: + /*tex > 3 reserved for future use */ + tex_tail_append(tex_new_param_glue_node(param, subtype)); + break; + } + } +} + +inline static void tex_aux_inject_display_skip_after(quarterword param, quarterword subtype) +{ + if (param > 0) { + switch (display_skip_mode_par) { + case display_skip_default : + case display_skip_always : + tex_tail_append(tex_new_param_glue_node(param, subtype)); + break; + case display_skip_non_zero: + if (! tex_glue_is_zero(glue_parameter(param))) { + tex_tail_append(tex_new_param_glue_node(param, subtype)); + } + break; + case display_skip_ignore: + break; + default: + /*tex > 3 reserved for future use */ + tex_tail_append(tex_new_param_glue_node(param, subtype)); + break; + } + } +} + +static void tex_aux_finish_displayed_math(int atleft, halfword eqnumber, halfword equation) +{ + /*tex box containing the equation */ + halfword equation_box; + /*tex width of the equation */ + scaled equation_width; + /*tex width of the line */ + scaled line_width; + /*tex width of equation number */ + scaled number_width; + /*tex width of equation number plus space to separate from equation */ + scaled number_plus_gap_width; + /*tex move the line right this much */ + scaled indent; + /*tex displacement of equation in the line */ + scaled displacement; + /*tex glue parameter codes for before and after */ + quarterword glue_above, glue_below; + /*tex glue parameter subtypes for before and after */ + quarterword subtype_above, subtype_below; + /*tex tail of adjustment lists */ + halfword post_adjust_tail, pre_adjust_tail; + /*tex tail of migration lists */ + halfword post_migrate_tail, pre_migrate_tail; + /*tex for equation numbers */ + scaled eqno_width; + /*tex true if the math and surrounding (par) dirs are different */ + int swap_dir = math_direction_par != pre_display_direction_par; + if (eqnumber && swap_dir) { + atleft = ! atleft; + } + /* */ + lmt_packaging_state.post_adjust_tail = post_adjust_head; + lmt_packaging_state.pre_adjust_tail = pre_adjust_head; + lmt_packaging_state.post_migrate_tail = post_migrate_head; + lmt_packaging_state.pre_migrate_tail = pre_migrate_head; + /* */ + equation_box = tex_hpack(equation, 0, packing_additional, direction_unknown, holding_none_option); + node_subtype(equation_box) = equation_list; + attach_current_attribute_list(equation_box); + equation = box_list(equation_box); + /* */ + post_adjust_tail = lmt_packaging_state.post_adjust_tail; + pre_adjust_tail = lmt_packaging_state.pre_adjust_tail; + post_migrate_tail = lmt_packaging_state.post_migrate_tail; + pre_migrate_tail = lmt_packaging_state.pre_migrate_tail; + lmt_packaging_state.post_adjust_tail = null; + lmt_packaging_state.pre_adjust_tail = null; + lmt_packaging_state.post_migrate_tail = null; + lmt_packaging_state.pre_migrate_tail = null; + /* */ + equation_width = box_width(equation_box); + line_width = display_width_par; + indent = display_indent_par; + if (eqnumber) { + number_width = box_width(eqnumber); + eqno_width = number_width; + number_plus_gap_width = number_width + tex_round_xn_over_d(math_eqno_gap_step_par, tex_get_math_quad_style(text_style), 1000); + node_subtype(eqnumber) = equation_number_list; + /*tex attach_current_attribute_list(eqno_box); */ + } else { + number_width = 0; + eqno_width = 0; + number_plus_gap_width = 0; + } + if (equation_width + number_plus_gap_width > line_width) { + /*tex + + The user can force the equation number to go on a separate line by causing its width to + be zero. + + */ + if ((number_width != 0) && ((equation_width - lmt_packaging_state.total_shrink[normal_glue_order] + number_plus_gap_width <= line_width) + || (lmt_packaging_state.total_shrink[fi_glue_order] != 0) + || (lmt_packaging_state.total_shrink[fil_glue_order] != 0) + || (lmt_packaging_state.total_shrink[fill_glue_order] != 0) + || (lmt_packaging_state.total_shrink[filll_glue_order] != 0))) { + box_list(equation_box) = null; + tex_flush_node(equation_box); + equation_box = tex_hpack(equation, line_width - number_plus_gap_width, packing_exactly, direction_unknown, holding_none_option); + node_subtype(equation_box) = equation_list; + attach_current_attribute_list(equation_box); + } else { + number_width = 0; + if (equation_width > line_width) { + box_list(equation_box) = null; + tex_flush_node(equation_box); + equation_box = tex_hpack(equation, line_width, packing_exactly, direction_unknown, holding_none_option); + node_subtype(equation_box) = equation_list; + attach_current_attribute_list(equation_box); + } + } + equation_width = box_width(equation_box); + } + /*tex + + We try first to center the display without regard to the existence of the equation number. + If that would make it too close (where \quote {too close} means that the space between + display and equation number is less than the width of the equation number), we either + center it in the remaining space or move it as far from the equation number as possible. + The latter alternative is taken only if the display begins with glue, since we assume that + the user put glue there to control the spacing precisely. + + */ + displacement = tex_half_scaled(line_width - equation_width); + if ((number_width > 0) && (displacement < 2 * number_width)) { + /*tex too close */ + displacement = tex_half_scaled(line_width - equation_width - number_width); + /* + if (p && !is_char_node(p) && node_type(p) == glue_node) + d = 0; + */ /* kind of weird this, so why not just */ + if (equation && node_type(equation) == glue_node) { + displacement = 0; + } + } + tex_tail_append(tex_new_penalty_node(pre_display_penalty_par, before_display_penalty_subtype)); + if ((displacement + indent <= pre_display_size_par) || ((cur_list.math_dir == dir_lefttoright) && atleft) + || ((cur_list.math_dir == dir_righttoleft) && ! atleft)) { + /*tex not enough clearance */ + glue_above = above_display_skip_code; + subtype_above = above_display_skip_glue; + glue_below = below_display_skip_code; + subtype_below = below_display_skip_glue; + } else { + glue_above = above_display_short_skip_code; + subtype_above = above_display_short_skip_glue; + glue_below = below_display_short_skip_code; + subtype_below = below_display_short_skip_glue; + } + /*tex + + If the equation number is set on a line by itself, either before or after the formula, we + append an infinite penalty so that no page break will separate the display from its number; + and we use the same size and displacement for all three potential lines of the display, + even though |\parshape| may specify them differently; |\leqno| on a forced single line due + to |width=0|; it follows that |type(a) = hlist_node|. + + */ + if (eqnumber && atleft && (number_width == 0)) { + /* if (math_direction_par == dir_lefttoright) { */ + box_shift_amount(eqnumber) = 0; + /* } else { */ + /* } */ + tex_append_to_vlist(eqnumber, lua_key_index(equation_number), NULL); + tex_tail_append(tex_new_penalty_node(infinite_penalty, equation_number_penalty_subtype)); + } else { + tex_aux_inject_display_skip_before(glue_above, subtype_above); + } + if (number_width != 0) { + scaled shift = line_width - equation_width - number_width - displacement; + halfword move = tex_new_kern_node(shift, explicit_kern_subtype); + if (atleft) { + if (swap_dir) { + if (math_direction_par == dir_lefttoright) { + /*tex TRT + TLT + \eqno: (swap_dir=true, math_direction_par=TLT, l=true) */ + halfword kern = tex_new_kern_node(shift + number_width, explicit_kern_subtype); + tex_try_couple_nodes(eqnumber, move); + tex_try_couple_nodes(move, equation_box); + tex_try_couple_nodes(equation_box, kern); + } else { + /*tex TLT + TRT + \eqno: (swap_dir=true, math_direction_par=TRT, l=true) */ + tex_try_couple_nodes(eqnumber, move); + tex_try_couple_nodes(move, equation_box); + } + } else { + halfword kern; + if (math_direction_par == dir_lefttoright) { + /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */ + kern = tex_new_kern_node(shift + number_width, explicit_kern_subtype); + } else { + /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */ + kern = tex_new_kern_node(shift, explicit_kern_subtype); + } + tex_try_couple_nodes(eqnumber, move); + tex_try_couple_nodes(move, equation_box); + tex_try_couple_nodes(equation_box, kern); + } + equation_box = eqnumber; + } else { + if (swap_dir) { + if (math_direction_par == dir_lefttoright) { + /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */ + } else { + /*tex TLT + TRT + \leqno: (swap_dir=true, math_direction_par=TRT, l=false) */ + } + tex_try_couple_nodes(equation_box, move); + tex_try_couple_nodes(move, eqnumber); + } else { + halfword kern; + if (math_direction_par == dir_lefttoright) { + /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */ + kern = tex_new_kern_node(displacement, explicit_kern_subtype); + } else { + /*tex TRT + TRT + \eqno: (swap_dir=false, math_direction_par=TRT, l=false) */ + kern = tex_new_kern_node(shift + number_width, explicit_kern_subtype); + } + tex_try_couple_nodes(kern, equation_box); + tex_try_couple_nodes(equation_box, move); + tex_try_couple_nodes(move, eqnumber); + equation_box = kern; + } + } + equation_box = tex_hpack(equation_box, 0, packing_additional, direction_unknown, holding_none_option); + node_subtype(equation_box) = equation_list; /* new */ + attach_current_attribute_list(equation_box); + box_shift_amount(equation_box) = indent; + } else { + box_shift_amount(equation_box) = indent + displacement; + } + /*tex check for prev: */ + tex_append_to_vlist(equation_box, lua_key_index(equation), NULL); + if (eqnumber && number_width == 0 && ! atleft) { + tex_tail_append(tex_new_penalty_node(infinite_penalty, equation_number_penalty_subtype)); + /* if (math_direction_par == dir_lefttoright) { */ + box_shift_amount(eqnumber) = indent + line_width - eqno_width ; + /* } else { */ + /* } */ + tex_append_to_vlist(eqnumber, lua_key_index(equation_number), NULL); + glue_below = 0; /* shouldn't this be an option */ + } + /*tex Migrating material comes after equation number: is this ok? */ + if (post_migrate_tail != post_migrate_head) { + node_next(cur_list.tail) = node_next(post_migrate_head); + node_prev(lmt_packaging_state.post_migrate_tail) = node_prev(cur_list.tail); + cur_list.tail = post_migrate_tail; + } + if (post_adjust_tail != post_adjust_head) { + node_next(cur_list.tail) = node_next(post_adjust_head); + node_prev(lmt_packaging_state.post_adjust_tail) = node_prev(cur_list.tail); + cur_list.tail = post_adjust_tail; + } + /*tex A weird place: is this ok? */ + if (pre_adjust_tail != pre_adjust_head) { + node_next(cur_list.tail) = node_next(pre_adjust_head); + node_prev(lmt_packaging_state.pre_adjust_tail) = node_prev(cur_list.tail); + cur_list.tail = pre_adjust_tail; + } + if (pre_migrate_tail != pre_migrate_head) { + node_next(cur_list.tail) = node_next(pre_migrate_head); + node_prev(lmt_packaging_state.pre_migrate_tail) = node_prev(cur_list.tail); + cur_list.tail = pre_migrate_tail; + } + tex_tail_append(tex_new_penalty_node(post_display_penalty_par, after_display_penalty_subtype)); + tex_aux_inject_display_skip_after(glue_below, subtype_below); + tex_aux_resume_after_display(); +} + +/*tex +* + A |math_node|, which occurs only in horizontal lists, appears before and after mathematical + formulas. The |subtype| field is |before| before the formula and |after| after it. There is a + |surround| field, which represents the amount of surrounding space inserted by |\mathsurround|. + + As an outcome of the math upgrading sub project that Mikael Sundqvist and I undertook end 2021 + and beginning 2022 Mikael suggested penalties surrounding inline formulas so there you have it: + |\preinlinepanelty| and |\postinlinepanelty|. + +*/ + +void tex_run_math_shift(void) { + if (cur_group == math_shift_group) { + /*tex box containing equation number */ + halfword eqnumber = null; + /*tex Use |\leqno| instead of |\eqno|, we default to right. */ + int atleft = 0; + /*tex |mmode| or |-mmode| */ + int mode = cur_list.mode; + int mathmode = cur_list.math_mode; + /*tex this pops the nest, the formula */ + halfword p = tex_aux_finish_math_list(null); + int mathleft = cur_list.math_begin; + int mathright = cur_list.math_end; + if (cur_cmd == math_shift_cs_cmd) { + switch (cur_chr) { + case begin_inline_math_code: + case begin_display_math_code: + case begin_math_mode_code: + tex_you_cant_error(NULL); + break; + } + } + if (cur_list.mode == -mode) { + /*tex end of equation number */ + AGAIN: + switch (cur_cmd) { + case math_shift_cmd: + tex_aux_check_second_math_shift(); + break; + case end_paragraph_cmd: + tex_get_x_token(); + goto AGAIN; + default: + tex_aux_check_display_math_end(); + break; + } + tex_run_mlist_to_hlist(p, 0, text_style, unset_noad_class, unset_noad_class); + eqnumber = tex_hpack(node_next(temp_head), 0, packing_additional, direction_unknown, holding_none_option); + attach_current_attribute_list(eqnumber); + tex_aux_unsave_math(); + /*tex now |cur_group = math_shift_group| */ + lmt_save_state.save_stack_data.ptr -= saved_equation_number_n_of_items; + if (saved_type(saved_equation_number_item_location) == saved_equation_number_location) { + atleft = saved_value(saved_equation_number_item_location) == left_location_code; + mode = cur_list.mode; + p = tex_aux_finish_math_list(null); + } else { + tex_confusion("after math"); + } + } + if (mode < 0) { + /*tex + + The |unsave| is done after everything else here; hence an appearance of |\mathsurround| + inside of |$...$| affects the spacing at these particular |$|'s. This is consistent + with the conventions of |$$ ... $$|, since |\abovedisplayskip| inside a display affects + the space above that display. + + */ + halfword math = tex_new_node(math_node, begin_inline_math); + if (mathmode) { + switch (cur_cmd) { + case math_shift_cs_cmd: + if (cur_chr != end_display_math_code && cur_chr != end_math_mode_code) { + tex_aux_check_second_math_shift(); + } + break; + case math_shift_cmd: + tex_aux_check_second_math_shift(); + break; + } + } else if (cur_cmd == math_shift_cs_cmd) { + tex_aux_check_inline_math_end(); + } + tex_tail_append(math); + math_penalty(math) = pre_inline_penalty_par; + /*tex begin mathskip code */ + switch (math_skip_mode_par) { + case math_skip_surround_when_zero: + if (! tex_glue_is_zero(math_skip_par)) { + tex_copy_glue_values(math, math_skip_par); + } else { + math_surround(math) = math_surround_par; + } + break ; + case math_skip_always_left: + case math_skip_always_both: + case math_skip_only_when_skip: + tex_copy_glue_values(math, math_skip_par); + break ; + case math_skip_always_right: + case math_skip_ignore: + break ; + case math_skip_always_surround: + default: + math_surround(math) = math_surround_par; + break; + } + /*tex end mathskip code */ + if (cur_list.math_dir) { + tex_tail_append(tex_new_dir(normal_dir_subtype, math_direction_par)); + } + tex_run_mlist_to_hlist(p, cur_list.mode > nomode, is_valid_math_style(cur_list.math_main_style) ? cur_list.math_main_style : text_style, cur_list.math_begin, cur_list.math_end); + tex_try_couple_nodes(cur_list.tail, node_next(temp_head)); + cur_list.tail = tex_tail_of_node_list(cur_list.tail); + if (cur_list.math_dir) { + tex_tail_append(tex_new_dir(cancel_dir_subtype, math_direction_par)); + } + cur_list.math_dir = 0; + math = tex_new_node(math_node, end_inline_math); + tex_tail_append(math); + math_penalty(math) = post_inline_penalty_par; + /*tex begin mathskip code */ + switch (math_skip_mode_par) { + case math_skip_surround_when_zero : + if (! tex_glue_is_zero(math_skip_par)) { + tex_copy_glue_values(math, math_skip_par); + math_surround(math) = 0; + } else { + math_surround(math) = math_surround_par; + } + break; + case math_skip_always_right: + case math_skip_always_both: + case math_skip_only_when_skip: + tex_copy_glue_values(math, math_skip_par); + break; + case math_skip_always_left: + case math_skip_ignore: + break; + case math_skip_always_surround: + default: + math_surround(math) = math_surround_par; + break; + } + /*tex end mathskip code */ + cur_list.space_factor = 1000; + mathleft = cur_list.math_begin; + mathright = cur_list.math_end; + tex_aux_unsave_math(); + } else { + if (! eqnumber) { + if (cur_cmd == math_shift_cmd) { + tex_aux_check_second_math_shift(); + } else { + tex_aux_check_display_math_end(); + } + } + tex_run_mlist_to_hlist(p, 0, display_style, cur_list.math_begin, cur_list.math_end); + mathleft = cur_list.math_begin; + mathright = cur_list.math_end; + tex_aux_finish_displayed_math(atleft, eqnumber, node_next(temp_head)); + } + /* local */ + update_tex_math_left_class(mathleft); + update_tex_math_right_class(mathright); + /* global */ + lmt_math_state.last_left = mathleft; + lmt_math_state.last_right = mathright; + } else { + tex_off_save(); + } +} + +/*tex + + When |\halign| appears in a display, the alignment routines operate essentially as they do in + vertical mode. Then the following program is activated, with |p| and |q| pointing to the + beginning and end of the resulting list, and with |aux_save| holding the |prev_depth| value. + +*/ + +void tex_finish_display_alignment(halfword head, halfword tail, halfword prevdepth) +{ + tex_handle_assignments(); + AGAIN: + switch (cur_cmd) { + case math_shift_cmd: + tex_aux_check_second_math_shift(); + break; + case end_paragraph_cmd: + tex_get_x_token(); + goto AGAIN; + default: + tex_aux_check_display_math_end(); + break; + } + tex_pop_nest(); + tex_tail_append(tex_new_penalty_node(pre_display_penalty_par, before_display_penalty_subtype)); + tex_aux_inject_display_skip_before(above_display_skip_code, above_display_skip_glue); + node_next(cur_list.tail) = head; + if (head && tail) { + cur_list.tail = tail; + } + tex_tail_append(tex_new_penalty_node(post_display_penalty_par, after_display_penalty_subtype)); + tex_aux_inject_display_skip_after(below_display_skip_code, below_display_skip_glue); + cur_list.prev_depth = prevdepth; + tex_aux_resume_after_display(); +} + +/* + + Turning macros into functions brought the mingw64 bin down from 2548224 to 2511360 bytes but + not the linux one, so I guess mingw doesn't inline (yet, in 2020). + +*/ + +static void tex_aux_define_inl_math_parameters(int size, int param, scaled value, int level) +{ + switch (size) { + case script_size: + tex_def_math_parameter(script_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_script_style, param, value, level, indirect_math_regular); + break; + case script_script_size: + tex_def_math_parameter(script_script_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_script_script_style, param, value, level, indirect_math_regular); + break; + default: + tex_def_math_parameter(text_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_text_style, param, value, level, indirect_math_regular); + break; + } +} + +static void tex_aux_define_dis_math_parameters(int size, int param, scaled value, int level) +{ + if (size == text_size) { + tex_def_math_parameter(display_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_display_style, param, value, level, indirect_math_regular); + } +} + +static void tex_aux_define_all_math_parameters(int size, int param, scaled value, int level) +{ + switch (size) { + case script_size: + tex_def_math_parameter(script_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_script_style, param, value, level, indirect_math_regular); + break; + case script_script_size: + tex_def_math_parameter(script_script_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_script_script_style, param, value, level, indirect_math_regular); + break; + default: + tex_def_math_parameter(text_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_text_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(display_style, param, value, level, indirect_math_regular); + tex_def_math_parameter(cramped_display_style, param, value, level, indirect_math_regular); + break; + } +} + +/*tex + + Here are the math parameters that are font-dependant. Before an mlist is converted to an hlist, + \TEX\ makes sure that the fonts in family~2 have enough parameters to be math symbol fonts, and + that the fonts in family~3 have enough parameters to be math extension fonts. The math-symbol + parameters are referred to by using the following macros, which take a size code as their + parameter; for example, |num1 (cur_size)| gives the value of the |num1| parameter for the + current size. + + The math extension parameters have similar macros, but the size code is omitted (since it is + always |cur_size| when we refer to such parameters). + +*/ + +# define total_mathsy_parameters 22 +# define total_mathex_parameters 13 + +# define mathsy(A,B) font_parameter(tex_fam_fnt(2,A),B) +# define mathex(A,B) font_parameter(tex_fam_fnt(3,A),B) + +# define math_x_height(A) mathsy(A,5) /*tex height of |x| */ +# define math_quad(A) mathsy(A,6) /*tex |18mu| */ +# define num1(A) mathsy(A,8) /*tex numerator shift-up in display styles */ +# define num2(A) mathsy(A,9) /*tex numerator shift-up in non-display, non-|\atop| */ +# define num3(A) mathsy(A,10) /*tex numerator shift-up in non-display |\atop| */ +# define denom1(A) mathsy(A,11) /*tex denominator shift-down in display styles */ +# define denom2(A) mathsy(A,12) /*tex denominator shift-down in non-display styles */ +# define sup1(A) mathsy(A,13) /*tex superscript shift-up in uncramped display style */ +# define sup2(A) mathsy(A,14) /*tex superscript shift-up in uncramped non-display */ +# define sup3(A) mathsy(A,15) /*tex superscript shift-up in cramped styles */ +# define sub1(A) mathsy(A,16) /*tex subscript shift-down if superscript is absent */ +# define sub2(A) mathsy(A,17) /*tex subscript shift-down if superscript is present */ +# define sup_drop(A) mathsy(A,18) /*tex superscript baseline below top of large box */ +# define sub_drop(A) mathsy(A,19) /*tex subscript baseline below bottom of large box */ +# define delim1(A) mathsy(A,20) /*tex size of |\atopwithdelims| delimiters in display styles */ +# define delim2(A) mathsy(A,21) /*tex size of |\atopwithdelims| delimiters in non-displays */ +# define axis_height(A) mathsy(A,22) /*tex height of fraction lines above the baseline */ + +# define default_rule_thickness(A) mathex(A,8) /*tex thickness of |\over| bars */ +# define big_operator_spacing1(A) mathex(A,9) /*tex minimum clearance above a displayed op */ +# define big_operator_spacing2(A) mathex(A,10) /*tex minimum clearance below a displayed op */ +# define big_operator_spacing3(A) mathex(A,11) /*tex minimum baselineskip above displayed op */ +# define big_operator_spacing4(A) mathex(A,12) /*tex minimum baselineskip below displayed op */ +# define big_operator_spacing5(A) mathex(A,13) /*tex padding above and below displayed limits */ + +/*tex + Somehow a scale > 1000 results in extreme values. +*/ + +/* +inline static int tex_aux_get_font_math_parameter(scaled scale, halfword f, int id) +{ + scaled v = get_font_math_par(f, id); +// return scale == 1000 ? v : round_xn_over_d(v, scale, 1000); + if (v) { + double d = 0.001 * scale * v; + return (d < 0.0) ? (int) (d - 0.5) : (int) (d + 0.5); + } else { + return 0; + } +} + +inline static int tex_aux_get_font_math_quantity(scaled scale, halfword v) +{ +// return scale == 1000 ? v : round_xn_over_d(v, scale, 1000); + if (v) { + double d = 0.001 * scale * v; + return (d < 0.0) ? (int) (d - 0.5) : (int) (d + 0.5); + } else { + return 0; + } +} +*/ + +# define math_parameter(a,b) ((font_math_parameter_count(a) >= b) ? font_math_parameter(a,b) : undefined_math_parameter) + +inline static scaled tex_aux_get_font_math_parameter(scaled scale, halfword f, int id) +{ + scaled v = math_parameter(f, id); + if (v == undefined_math_parameter) { + return v; + } else { + return v ? scaledround(0.001 * scale * v) : 0; + } +} + +inline static scaled tex_aux_get_font_math_quantity(scaled scale, halfword v) +{ + return v ? scaledround(0.001 * scale * v) : 0; +} + +/*tex + The next function is called when we define a family, but first we define a few helpers + for identifying traditional math fonts. Watch the hard codes family check! +*/ + +void tex_fixup_math_parameters(int fam, int size, int f, int level) +{ + scaled scale = tex_get_math_font_scale(f, size); + + if (tracing_math_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[math: fixing up font, family %i, size %i, font %i, level %i]", fam, size, f, level); + tex_end_diagnostic(); + } + + /*tex These apply to all: */ + + tex_aux_define_all_math_parameters(size, math_parameter_quad, tex_aux_get_font_math_quantity (scale, font_size(f)), level); + tex_aux_define_all_math_parameters(size, math_parameter_axis, tex_aux_get_font_math_parameter(scale, f, AxisHeight), level); + + tex_aux_define_all_math_parameters(size, math_parameter_accent_base_height, tex_aux_get_font_math_parameter(scale, f, AccentBaseHeight), level); + tex_aux_define_all_math_parameters(size, math_parameter_accent_base_depth, tex_aux_get_font_math_parameter(scale, f, AccentBaseDepth), level); /* engine, reserved */ + tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_base_height, tex_aux_get_font_math_parameter(scale, f, FlattenedAccentBaseHeight), level); + tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_base_depth, tex_aux_get_font_math_parameter(scale, f, FlattenedAccentBaseDepth), level); /* engine, reserved */ + tex_aux_define_all_math_parameters(size, math_parameter_overbar_kern, tex_aux_get_font_math_parameter(scale, f, OverbarExtraAscender), level); + tex_aux_define_all_math_parameters(size, math_parameter_overbar_rule, tex_aux_get_font_math_parameter(scale, f, OverbarRuleThickness), level); + tex_aux_define_all_math_parameters(size, math_parameter_overbar_vgap, tex_aux_get_font_math_parameter(scale, f, OverbarVerticalGap), level); + tex_aux_define_all_math_parameters(size, math_parameter_underbar_kern, tex_aux_get_font_math_parameter(scale, f, UnderbarExtraDescender), level); + tex_aux_define_all_math_parameters(size, math_parameter_underbar_rule, tex_aux_get_font_math_parameter(scale, f, UnderbarRuleThickness ), level); + tex_aux_define_all_math_parameters(size, math_parameter_underbar_vgap, tex_aux_get_font_math_parameter(scale, f, UnderbarVerticalGap), level); + tex_aux_define_all_math_parameters(size, math_parameter_under_delimiter_vgap, tex_aux_get_font_math_parameter(scale, f, StretchStackGapAboveMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_under_delimiter_bgap, tex_aux_get_font_math_parameter(scale, f, StretchStackBottomShiftDown), level); + tex_aux_define_all_math_parameters(size, math_parameter_over_delimiter_vgap, tex_aux_get_font_math_parameter(scale, f, StretchStackGapBelowMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_over_delimiter_bgap, tex_aux_get_font_math_parameter(scale, f, StretchStackTopShiftUp), level); + tex_aux_define_all_math_parameters(size, math_parameter_radical_kern, tex_aux_get_font_math_parameter(scale, f, RadicalExtraAscender), level); + tex_aux_define_all_math_parameters(size, math_parameter_radical_rule, tex_aux_get_font_math_parameter(scale, f, RadicalRuleThickness), level); + tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_before, tex_aux_get_font_math_parameter(scale, f, RadicalKernBeforeDegree), level); + tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_after, tex_aux_get_font_math_parameter(scale, f, RadicalKernAfterDegree), level); + tex_aux_define_all_math_parameters(size, math_parameter_subscript_shift_drop, tex_aux_get_font_math_parameter(scale, f, SubscriptBaselineDropMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_superscript_shift_drop, tex_aux_get_font_math_parameter(scale, f, SuperscriptBaselineDropMax), level); + tex_aux_define_all_math_parameters(size, math_parameter_subscript_shift_down, tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDown), level); + tex_aux_define_all_math_parameters(size, math_parameter_prime_shift_drop, tex_aux_get_font_math_parameter(scale, f, PrimeBaselineDropMax), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_subscript_top_max, tex_aux_get_font_math_parameter(scale, f, SubscriptTopMax), level); + tex_aux_define_all_math_parameters(size, math_parameter_superscript_bottom_min, tex_aux_get_font_math_parameter(scale, f, SuperscriptBottomMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_superscript_subscript_bottom_max, tex_aux_get_font_math_parameter(scale, f, SuperscriptBottomMaxWithSubscript), level); + tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_vgap, tex_aux_get_font_math_parameter(scale, f, SubSuperscriptGapMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_limit_above_vgap, tex_aux_get_font_math_parameter(scale, f, UpperLimitGapMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_limit_above_bgap, tex_aux_get_font_math_parameter(scale, f, UpperLimitBaselineRiseMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_limit_below_vgap, tex_aux_get_font_math_parameter(scale, f, LowerLimitGapMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_limit_below_bgap, tex_aux_get_font_math_parameter(scale, f, LowerLimitBaselineDropMin), level); + tex_aux_define_all_math_parameters(size, math_parameter_nolimit_sub_factor, tex_aux_get_font_math_parameter(scale, f, NoLimitSubFactor), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_nolimit_sup_factor, tex_aux_get_font_math_parameter(scale, f, NoLimitSupFactor), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_skewed_fraction_hgap, tex_aux_get_font_math_parameter(scale, f, SkewedFractionHorizontalGap), level); + tex_aux_define_all_math_parameters(size, math_parameter_skewed_fraction_vgap, tex_aux_get_font_math_parameter(scale, f, SkewedFractionVerticalGap), level); + tex_aux_define_all_math_parameters(size, math_parameter_space_before_script, tex_aux_get_font_math_parameter(scale, f, SpaceBeforeScript), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_space_after_script, tex_aux_get_font_math_parameter(scale, f, SpaceAfterScript), level); + tex_aux_define_all_math_parameters(size, math_parameter_connector_overlap_min, tex_aux_get_font_math_parameter(scale, f, MinConnectorOverlap), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_fraction_rule, tex_aux_get_font_math_parameter(scale, f, FractionRuleThickness), level); + + tex_aux_define_all_math_parameters(size, math_parameter_radical_degree_raise, math_parameter(f, RadicalDegreeBottomRaisePercent), level); + tex_aux_define_all_math_parameters(size, math_parameter_prime_raise, math_parameter(f, PrimeRaisePercent), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_prime_raise_composed, math_parameter(f, PrimeRaiseComposedPercent), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_prime_space_after, math_parameter(f, PrimeSpaceAfter), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_prime_width, math_parameter(f, PrimeWidthPercent), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_skewed_delimiter_tolerance, math_parameter(f, SkewedDelimiterTolerance), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_top_shift_up, math_parameter(f, AccentTopShiftUp), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_bottom_shift_down, math_parameter(f, AccentBottomShiftDown), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_top_overshoot, math_parameter(f, AccentTopOvershoot), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_bottom_overshoot, math_parameter(f, AccentBottomOvershoot), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_superscript_drop, math_parameter(f, AccentSuperscriptDrop), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_superscript_percent, math_parameter(f, AccentSuperscriptPercent), level); /* engine, default 0 */ + tex_aux_define_all_math_parameters(size, math_parameter_accent_extend_margin, math_parameter(f, AccentExtendMargin), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_top_shift_up, math_parameter(f, FlattenedAccentTopShiftUp), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_flattened_accent_bottom_shift_down, math_parameter(f, FlattenedAccentBottomShiftDown), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_delimiter_percent, math_parameter(f, DelimiterPercent), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_delimiter_shortfall, math_parameter(f, DelimiterShortfall), level); /* engine, undefined */ + + tex_aux_define_all_math_parameters(size, math_parameter_radical_extensible_after, math_parameter(f, RadicalKernAfterExtensible), level); /* engine, undefined */ + tex_aux_define_all_math_parameters(size, math_parameter_radical_extensible_before, math_parameter(f, RadicalKernBeforeExtensible), level); /* engine, undefined */ + + /*tex Not all are official \OPENTYPE: */ + + tex_aux_define_all_math_parameters(size, math_parameter_x_scale, 1000, level); + tex_aux_define_all_math_parameters(size, math_parameter_y_scale, 1000, level); + + /*tex Most are zero and have to be set at by the macro package (if at all):. */ + + tex_aux_define_all_math_parameters(size, math_parameter_limit_above_kern, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_limit_below_kern, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_superscript_shift, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_subscript_shift, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_superprescript_shift, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_subprescript_shift, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_rule_height, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_rule_depth, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_superscript_shift_distance, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_subscript_shift_distance, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_superprescript_shift_distance, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_subprescript_shift_distance, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_superscript_space, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_subscript_space, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_superprescript_space, 0, level); + tex_aux_define_all_math_parameters(size, math_parameter_extra_subprescript_space, 0, level); + + /*tex A special one: */ + + if (math_parameter(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) { /* engine */ + tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_shift_down, tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDownWithSuperscript), level); + } else { + tex_aux_define_all_math_parameters(size, math_parameter_subscript_superscript_shift_down, tex_aux_get_font_math_parameter(scale, f, SubscriptShiftDown), level); + } + + /*tex These differentiate between display and inline: */ + + tex_aux_define_dis_math_parameters(size, math_parameter_operator_size, tex_aux_get_font_math_parameter(scale, f, DisplayOperatorMinHeight), level); + tex_aux_define_inl_math_parameters(size, math_parameter_radical_vgap, tex_aux_get_font_math_parameter(scale, f, RadicalVerticalGap), level); + tex_aux_define_dis_math_parameters(size, math_parameter_radical_vgap, tex_aux_get_font_math_parameter(scale, f, RadicalDisplayStyleVerticalGap), level); + tex_aux_define_inl_math_parameters(size, math_parameter_stack_num_up, tex_aux_get_font_math_parameter(scale, f, StackTopShiftUp), level); + tex_aux_define_dis_math_parameters(size, math_parameter_stack_num_up, tex_aux_get_font_math_parameter(scale, f, StackTopDisplayStyleShiftUp), level); + tex_aux_define_inl_math_parameters(size, math_parameter_stack_denom_down, tex_aux_get_font_math_parameter(scale, f, StackBottomShiftDown), level); + tex_aux_define_dis_math_parameters(size, math_parameter_stack_denom_down, tex_aux_get_font_math_parameter(scale, f, StackBottomDisplayStyleShiftDown), level); + tex_aux_define_inl_math_parameters(size, math_parameter_stack_vgap, tex_aux_get_font_math_parameter(scale, f, StackGapMin), level); + tex_aux_define_dis_math_parameters(size, math_parameter_stack_vgap, tex_aux_get_font_math_parameter(scale, f, StackDisplayStyleGapMin), level); + tex_aux_define_inl_math_parameters(size, math_parameter_fraction_num_vgap, tex_aux_get_font_math_parameter(scale, f, FractionNumeratorGapMin), level); + tex_aux_define_dis_math_parameters(size, math_parameter_fraction_num_vgap, tex_aux_get_font_math_parameter(scale, f, FractionNumeratorDisplayStyleGapMin), level); + tex_aux_define_inl_math_parameters(size, math_parameter_fraction_num_up, tex_aux_get_font_math_parameter(scale, f, FractionNumeratorShiftUp), level); + tex_aux_define_dis_math_parameters(size, math_parameter_fraction_num_up, tex_aux_get_font_math_parameter(scale, f, FractionNumeratorDisplayStyleShiftUp), level); + tex_aux_define_inl_math_parameters(size, math_parameter_fraction_denom_vgap, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorGapMin), level); + tex_aux_define_dis_math_parameters(size, math_parameter_fraction_denom_vgap, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorDisplayStyleGapMin), level); + tex_aux_define_inl_math_parameters(size, math_parameter_fraction_denom_down, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorShiftDown), level); + tex_aux_define_dis_math_parameters(size, math_parameter_fraction_denom_down, tex_aux_get_font_math_parameter(scale, f, FractionDenominatorDisplayStyleShiftDown), level); + tex_aux_define_inl_math_parameters(size, math_parameter_fraction_del_size, tex_aux_get_font_math_parameter(scale, f, FractionDelimiterSize), level); /* engine, undefined */ + tex_aux_define_dis_math_parameters(size, math_parameter_fraction_del_size, tex_aux_get_font_math_parameter(scale, f, FractionDelimiterDisplayStyleSize), level); /* engine, undefined */ + + /*tex A few more specials: */ + + switch (size) { + case script_size: + tex_def_math_parameter(script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp), level, indirect_math_regular); + tex_def_math_parameter(cramped_script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular); + tex_def_math_parameter(script_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp), level, indirect_math_regular); /* engine, default 0 */ + tex_def_math_parameter(cramped_script_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped), level, indirect_math_regular); /* engine, default 0 */ + break; + case script_script_size: + tex_def_math_parameter(script_script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp), level, indirect_math_regular); + tex_def_math_parameter(cramped_script_script_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular); + tex_def_math_parameter(script_script_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp), level, indirect_math_regular); /* engine, default 0 */ + tex_def_math_parameter(cramped_script_script_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped), level, indirect_math_regular); /* engine, default 0 */ + break; + default: + tex_def_math_parameter(display_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp), level, indirect_math_regular); + tex_def_math_parameter(cramped_display_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular); + tex_def_math_parameter(text_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUp), level, indirect_math_regular); + tex_def_math_parameter(cramped_text_style, math_parameter_superscript_shift_up, tex_aux_get_font_math_parameter(scale, f, SuperscriptShiftUpCramped), level, indirect_math_regular); + tex_def_math_parameter(display_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp), level, indirect_math_regular); /* engine, default 0 */ + tex_def_math_parameter(cramped_display_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped), level, indirect_math_regular); /* engine, default 0 */ + tex_def_math_parameter(text_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUp), level, indirect_math_regular); /* engine, default 0 */ + tex_def_math_parameter(cramped_text_style, math_parameter_prime_shift_up, tex_aux_get_font_math_parameter(scale, f, PrimeShiftUpCramped), level, indirect_math_regular); /* engine, default 0 */ + break; + } + +} + +/*tex + + There is some trickery here. The values are actually pointers and in \LUATEX\ the predefined + muglue ones are small numbers that are way below the normal node values. So, they are kind + of save signals. However, in \LUAMETATEX\ we use zero based internal codes (because that is + nicer for the interface. + +*/ + +void tex_set_display_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + tex_def_math_parameter(display_style, code, value, level, indirect); + tex_def_math_parameter(cramped_display_style, code, value, level, indirect); +} + +void tex_set_text_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + tex_def_math_parameter(text_style, code, value, level, indirect); + tex_def_math_parameter(cramped_text_style, code, value, level, indirect); +} + +void tex_set_script_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + tex_def_math_parameter(script_style, code, value, level, indirect); + tex_def_math_parameter(cramped_script_style, code, value, level, indirect); +} + +void tex_set_script_script_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + tex_def_math_parameter(script_script_style, code, value, level, indirect); + tex_def_math_parameter(cramped_script_script_style, code, value, level, indirect); +} + +void tex_set_all_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + for (int style = display_style; style <= cramped_script_script_style; style++) { + tex_def_math_parameter(style, code, value, level, indirect); + } +} + +void tex_set_uncramped_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + for (int style = display_style; style <= script_script_style; style += 2) { + tex_def_math_parameter(style, code, value, level, indirect); + } +} + +void tex_set_cramped_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + for (int style = cramped_display_style; style <= cramped_script_script_style; style += 2) { + tex_def_math_parameter(style, code, value, level, indirect); + } +} + +void tex_set_split_styles(halfword code, halfword value, halfword level, halfword indirect) +{ + tex_set_display_styles (code, value, level, indirect); + tex_set_text_styles (code, value, level, indirect); + tex_set_script_styles (code, 0, level, indirect); + tex_set_script_script_styles(code, 0, level, indirect); +} + +void tex_reset_all_styles(halfword level) +{ + for (int code = math_parameter_atom_pairs_first; code <= math_parameter_atom_pairs_last; code++) { + tex_set_all_styles(code, zero_mu_skip_code, level, indirect_math_unset); + } +} + +inline static halfword tex_aux_math_class_default(halfword class) { + return (class << 24) + (class << 16) + (class << 8) + class; +} + +inline static void tex_set_math_class_default(halfword class, halfword parent, halfword options) +{ + tex_word_define(0, internal_int_location(first_math_class_code + class), tex_aux_math_class_default(parent)); + tex_word_define(0, internal_int_location(first_math_atom_code + class), tex_aux_math_class_default(class)); + tex_word_define(0, internal_int_location(first_math_options_code + class), options); + tex_word_define(0, internal_int_location(first_math_parent_code + class), tex_aux_math_class_default(class)); +} + +static void tex_aux_set_math_atom_rule(halfword left, halfword right, halfword newleft, halfword newright) +{ + tex_set_all_styles(math_parameter_rules_pair(left, right), (newleft << 16) + newright, level_one, indirect_math_regular); +} + +void tex_initialize_math_spacing(void) +{ + + for (int class = 0; class <= max_math_class_code; class++) { + tex_set_math_class_default(class, class, no_class_options); + /*tex We do this here as there is no real need for yet another initializer. */ + tex_word_define(0, internal_int_location(first_math_pre_penalty_code + class), infinite_penalty); + tex_word_define(0, internal_int_location(first_math_post_penalty_code + class), infinite_penalty); + tex_word_define(0, internal_int_location(first_math_display_pre_penalty_code + class), infinite_penalty); + tex_word_define(0, internal_int_location(first_math_display_post_penalty_code + class), infinite_penalty); + } + + tex_reset_all_styles(level_one); + + tex_set_math_class_default(ordinary_noad_subtype, ordinary_noad_subtype, no_italic_correction_class_option | + check_ligature_class_option | + check_kern_pair_class_option | + flatten_class_option); + tex_set_math_class_default(operator_noad_subtype, operator_noad_subtype, check_ligature_class_option | + check_kern_pair_class_option); + tex_set_math_class_default(binary_noad_subtype, binary_noad_subtype, no_italic_correction_class_option | + check_ligature_class_option | + check_kern_pair_class_option | + flatten_class_option); + tex_set_math_class_default(relation_noad_subtype, relation_noad_subtype, no_italic_correction_class_option | + check_ligature_class_option | + check_kern_pair_class_option | + flatten_class_option | + omit_penalty_class_option); + tex_set_math_class_default(open_noad_subtype, open_noad_subtype, no_italic_correction_class_option | + /* open_fence_class_option | */ + check_ligature_class_option | + check_kern_pair_class_option); + tex_set_math_class_default(close_noad_subtype, close_noad_subtype, no_italic_correction_class_option | + /* close_fence_class_option | */ + check_ligature_class_option | + check_kern_pair_class_option); + tex_set_math_class_default(punctuation_noad_subtype, punctuation_noad_subtype, no_italic_correction_class_option | + check_ligature_class_option | + check_kern_pair_class_option | + flatten_class_option); + tex_set_math_class_default(variable_noad_subtype, ordinary_noad_subtype, no_italic_correction_class_option); + tex_set_math_class_default(active_noad_subtype, ordinary_noad_subtype, no_italic_correction_class_option); + tex_set_math_class_default(inner_noad_subtype, inner_noad_subtype, flatten_class_option); + tex_set_math_class_default(under_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(over_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(fraction_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(radical_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(middle_noad_subtype, open_noad_subtype, no_italic_correction_class_option); /* | middle_fence_class_option= */ + tex_set_math_class_default(accent_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(fenced_noad_subtype, inner_noad_subtype , no_class_options); + tex_set_math_class_default(ghost_noad_subtype, ordinary_noad_subtype, no_class_options); + tex_set_math_class_default(vcenter_noad_subtype, ordinary_noad_subtype, no_class_options); + + tex_aux_set_math_atom_rule(math_begin_class, binary_noad_subtype, ordinary_noad_subtype, ordinary_noad_subtype); + tex_aux_set_math_atom_rule(binary_noad_subtype, math_end_class, ordinary_noad_subtype, ordinary_noad_subtype); + + tex_aux_set_math_atom_rule(binary_noad_subtype, binary_noad_subtype, binary_noad_subtype, ordinary_noad_subtype); + tex_aux_set_math_atom_rule(operator_noad_subtype, binary_noad_subtype, operator_noad_subtype, ordinary_noad_subtype); + tex_aux_set_math_atom_rule(open_noad_subtype, binary_noad_subtype, open_noad_subtype, ordinary_noad_subtype); + tex_aux_set_math_atom_rule(punctuation_noad_subtype, binary_noad_subtype, punctuation_noad_subtype, ordinary_noad_subtype); + tex_aux_set_math_atom_rule(relation_noad_subtype, binary_noad_subtype, relation_noad_subtype, ordinary_noad_subtype); + + tex_aux_set_math_atom_rule(binary_noad_subtype, close_noad_subtype, ordinary_noad_subtype, close_noad_subtype); + tex_aux_set_math_atom_rule(binary_noad_subtype, punctuation_noad_subtype, ordinary_noad_subtype, punctuation_noad_subtype); + tex_aux_set_math_atom_rule(binary_noad_subtype, relation_noad_subtype, ordinary_noad_subtype, relation_noad_subtype); + + tex_aux_set_math_atom_rule(relation_noad_subtype, close_noad_subtype, ordinary_noad_subtype, close_noad_subtype); + tex_aux_set_math_atom_rule(relation_noad_subtype, punctuation_noad_subtype, ordinary_noad_subtype, punctuation_noad_subtype); + + /* */ + +// math_parameter_spacing_pair(ordinary_noad_subtype,ordinary_noad_subtype) + + tex_set_all_styles (math_parameter_spacing_pair(ordinary_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(ordinary_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_all_styles (math_parameter_spacing_pair(operator_noad_subtype, ordinary_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_spacing_pair(operator_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(operator_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(operator_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_all_styles (math_parameter_spacing_pair(operator_noad_subtype, fraction_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_spacing_pair(operator_noad_subtype, radical_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_spacing_pair(fraction_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_spacing_pair(radical_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, ordinary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, operator_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, open_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, inner_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, middle_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, fraction_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(binary_noad_subtype, radical_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, ordinary_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, operator_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, open_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, inner_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, middle_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, fraction_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(relation_noad_subtype, radical_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + + tex_set_all_styles (math_parameter_spacing_pair(close_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(close_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, ordinary_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, relation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, open_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, close_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, punctuation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, fraction_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, middle_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(punctuation_noad_subtype, radical_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype, punctuation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype, punctuation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype, punctuation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, ordinary_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_spacing_pair(inner_noad_subtype, operator_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, binary_noad_subtype), med_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, relation_noad_subtype), thick_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, open_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, punctuation_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + tex_set_split_styles (math_parameter_spacing_pair(inner_noad_subtype, middle_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(middle_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(fraction_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + tex_set_split_styles (math_parameter_spacing_pair(radical_noad_subtype, inner_noad_subtype), thin_mu_skip_code, level_one, indirect_math_regular); + + /* */ + + tex_set_all_styles (math_parameter_x_scale, 1000, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_y_scale, 1000, level_one, indirect_math_regular); + + /* could be initialize_math_defaults */ + + tex_set_all_styles (math_parameter_over_line_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_under_line_variant, math_normal_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_over_delimiter_variant, math_small_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_under_delimiter_variant, math_small_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_delimiter_over_variant, math_normal_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_delimiter_under_variant, math_normal_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_h_extensible_variant, math_normal_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_v_extensible_variant, math_normal_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_fraction_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_radical_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_degree_variant, math_double_superscript_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_accent_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_top_accent_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_bottom_accent_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_overlay_accent_variant, math_cramped_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_numerator_variant, math_numerator_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_denominator_variant, math_denominator_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_superscript_variant, math_superscript_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_subscript_variant, math_subscript_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_prime_variant, math_superscript_style_variant, level_one, indirect_math_regular); + tex_set_all_styles (math_parameter_stack_variant, math_numerator_style_variant, level_one, indirect_math_regular); +} + +/*tex + + This needs to be called just at the start of |mlist_to_hlist|, for backward compatibility with + |\scriptspace|. + +*/ + +void tex_finalize_math_parameters(void) +{ + int saved_trace = tracing_assigns_par; + tracing_assigns_par = 0; + if (tex_get_math_parameter(display_style, math_parameter_space_after_script, NULL) == undefined_math_parameter) { + tex_def_math_parameter(display_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(text_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(script_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(script_script_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(cramped_display_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(cramped_text_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(cramped_script_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + tex_def_math_parameter(cramped_script_script_style, math_parameter_space_after_script, script_space_par, level_one, indirect_math_regular); + } + tracing_assigns_par = saved_trace; +} + +static void tex_aux_math_parameter_error(int style, int param, const char *name) +{ + char msg[256] = { 0 }; + if (param >= 0) { + snprintf(msg, 256, "Math error: parameter '%s' with id %i in style %d is not set", name, param, style); + } else { + snprintf(msg, 256, "Math error: parameter '%s' style %d is not set", name, style); + } + tex_handle_error( + normal_error_type, + msg, + "Sorry, but I can't typeset math unless various parameters have been set. This is\n" + "normally done by loading special math fonts into the math family slots. Your font\n" + "set is lacking at least the parameter mentioned earlier." + ); + return; +} + +/*tex + + For the moment this is experimental. + +*/ + +inline static scaled tex_aux_max_scale(int style, int param) +{ + scaled scale = tex_get_math_parameter(style, param, NULL); + if (scale > 5000) { + return 5000; + } else if (scale < 0) { + return 0; + } else { + return scale; + } +} + +/*tex + + The non-staticness of this function is for the benefit of |texmath.w|. Watch out, this one + uses the style! The style and size numbers don't match because we have cramped styles. + +*/ + +scaled tex_get_math_quad_style(int style) +{ + scaled scale = tex_aux_max_scale(style, math_parameter_x_scale); + scaled value = tex_get_math_parameter(style, math_parameter_quad, NULL); + if (value == undefined_math_parameter) { + tex_aux_math_parameter_error(style, -1, "quad"); + return 0; + } else { + return scaledround(0.001 * value * scale); + } +} + +/*tex + + For this reason the next one is different because it is called with a size specifier instead + of a style specifier. + +*/ + +scaled tex_math_axis_size(int size) +{ + scaled value; + switch (size) { + case script_size : size = script_style; break; + case script_script_size: size = script_script_style; break; + default : size = text_style; break; + } + value = tex_get_math_parameter(size, math_parameter_axis, NULL); + if (value == undefined_math_parameter) { + tex_aux_math_parameter_error(size, -1, "axis"); + return 0; + } else { + return value; + } +} + +scaled tex_get_math_quad_size(int size) /* used in degree before and after */ +{ + switch (size) { + case script_size : size = script_style; break; + case script_script_size: size = script_script_style; break; + default : size = text_style; break; + } + return tex_get_math_parameter(size, math_parameter_quad, NULL); +} + +scaled tex_get_math_quad_size_scaled(int size) /* used in cur_mu */ +{ + scaled value, scale; + switch (size) { + case script_size : size = script_style; break; + case script_script_size: size = script_script_style; break; + default : size = text_style; break; + } + value = tex_get_math_parameter(size, math_parameter_quad, NULL); + scale = tex_aux_max_scale(size, math_parameter_x_scale); + /* return tex_x_over_n(scaledround(0.001 * value * scale), 18); */ + return scaledround(0.001 * value * scale / 18.0); +} + +static int tex_aux_math_parameter_okay(int param) +{ + if (ignore_math_parameter(param)) { + if (tracing_math_par > 1) { + tex_begin_diagnostic(); + tex_print_format("[math: parameter, name %s, ignored]", lmt_name_of_math_parameter(param)); + tex_end_diagnostic(); + } + return 0; + } else { + return 1; + } +} + +scaled tex_get_math_parameter_checked(int style, int param) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param)); + return 0; + } else { + return value; + } + } else { + return 0; + } +} + +scaled tex_get_math_parameter_default(int style, int param, scaled dflt) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + return dflt; + } else { + return value; + } + } else { + return dflt; + } +} + +void tex_run_math_italic_correction(void) { + tex_tail_append(tex_new_kern_node(0, explicit_kern_subtype)); /* maybe math_shape_kern */ +} + +/* */ + +scaled tex_get_math_x_parameter(int style, int param) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_x_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + return value; // ?? scaledround(value * scale * 0.001); + } else { + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0; + } + } else { + return 0; + } +} + +scaled tex_get_math_x_parameter_checked(int style, int param) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_x_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param)); + return 0; + } else { + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0; + } + } else { + return 0; + } +} + +scaled tex_get_math_x_parameter_default(int style, int param, scaled dflt) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_x_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + return dflt; + } else{ + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_x_scale_par * value * scale) : 0; + } + } else { + return dflt; + } +} + +scaled tex_get_math_y_parameter(int style, int param) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_y_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + return value; + } else{ + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0; + } + } else { + return 0; + } +} + +scaled tex_get_math_y_parameter_checked(int style, int param) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_y_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + tex_aux_math_parameter_error(style, param, lmt_name_of_math_parameter(param)); + return 0; + } else { + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0; + } + } else { + return 0; + } +} + +scaled tex_get_math_y_parameter_default(int style, int param, scaled dflt) +{ + if (tex_aux_math_parameter_okay(param)) { + scaled scale = tex_aux_max_scale(style, math_parameter_y_scale); + scaled value = tex_get_math_parameter(style, param, NULL); + if (value == undefined_math_parameter) { + return dflt; + } else{ + return value ? scaledround(0.000000001 * glyph_scale_par * glyph_y_scale_par * value * scale) : 0; + } + } else { + return dflt; + } +} + +scaled tex_get_font_math_parameter(int font, int size, int param) +{ + scaled scale = tex_get_math_font_scale(font, size); + scaled value = tex_aux_get_font_math_parameter(scale, font, param); + if (value == undefined_math_parameter) { + return undefined_math_parameter; + } else { + return value ? scaledround(0.001 * glyph_scale_par * value) : 0; + } +} + +/* maybe more precission, so multiply all and divide by 0.000000001 */ + +scaled tex_get_font_math_y_parameter(int font, int size, int param) +{ + scaled scale = tex_get_math_font_scale(font, size); + scaled value = tex_aux_get_font_math_parameter(scale, font, param); + if (value == undefined_math_parameter) { + return undefined_math_parameter; + } else { + return value ? scaledround(0.000001 * glyph_scale_par * glyph_y_scale_par * value) : 0; + } +} + +scaled tex_get_font_math_x_parameter(int font, int size, int param) +{ + scaled scale = tex_get_math_font_scale(font, size); + scaled value = tex_aux_get_font_math_parameter(scale, font, param); + if (value == undefined_math_parameter) { + return undefined_math_parameter; + } else { + return value ? scaledround(0.000001 * glyph_scale_par * glyph_x_scale_par * value) : 0; + } +} + +halfword tex_to_math_spacing_parameter(halfword left, halfword right) +{ + halfword param = math_parameter_spacing_pair(left,right); + return (param >= math_parameter_atom_pairs_first && param <= math_parameter_atom_pairs_last) ? param : -1; +} + +halfword tex_to_math_rules_parameter(halfword left, halfword right) +{ + halfword param = math_parameter_rules_pair(left,right); + return (param >= math_parameter_atom_rules_first && param <= math_parameter_atom_rules_last) ? param : -1; +} + +void tex_set_default_math_codes(void) +{ + mathcodeval mval = { 0, 0, 0 }; + /*tex This will remap old font families at runtime. */ + mval.class_value = math_use_current_family_code; + /*tex Upright math digts come from family 0. */ + for (int d = '0'; d <= '9'; d++) { + mval.character_value = d; + tex_set_math_code(d, mval, level_one); + } + /* In traditional fonts math italic has family 1. */ + mval.family_value = 1; + for (int u = 'A'; u <= 'Z'; u++) { + mval.character_value = u; + tex_set_math_code(u, mval, level_one); + } + for (int l = 'a'; l <= 'z'; l++) { + mval.character_value = l; + tex_set_math_code(l, mval, level_one); + } + /*tex This is kind of standard. */ + tex_set_del_code('.', (delcodeval) { { 0, 0, 0, }, { 0, 0, 0 } }, level_one); +} + +int tex_in_main_math_style(halfword style) +{ + switch (style) { + case display_style: + case text_style: + return 1; + /* + case cramped_display_style: + case cramped_text_style: + return 0; // could be parameter driven + */ + default: + return 0; + } +} |