diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/texmath.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texmath.w | 5138 |
1 files changed, 2569 insertions, 2569 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.w b/Build/source/texk/web2c/luatexdir/tex/texmath.w index 769fbe8dd35..eeeb75bd792 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texmath.w +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.w @@ -1,2569 +1,2569 @@ -% texmath.w
-%
-% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
-%
-% This file is part of LuaTeX.
-%
-% LuaTeX is free software; you can redistribute it and/or modify it under
-% the terms of the GNU General Public License as published by the Free
-% Software Foundation; either version 2 of the License, or (at your
-% option) any later version.
-%
-% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
-% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-% License for more details.
-%
-% You should have received a copy of the GNU General Public License along
-% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
-
-@ @c
-#include "ptexlib.h"
-
-@ @c
-#define mode mode_par
-#define tail tail_par
-#define head head_par
-#define dir_save dirs_par
-
-/*
-
- \mathdisplayskipmode
-
- tex normally always inserts before and only after when larger than zero
-
- 0 = normal tex
- 1 = always
- 2 = non-zero
- 3 = ignore
-
-*/
-
-@ TODO: not sure if this is the right order
-@c
-#define back_error(A,B) do { \
- OK_to_interrupt=false; \
- back_input(); \
- OK_to_interrupt=true; \
- tex_error(A,B); \
- } while (0)
-
-@ @c
-int scan_math(pointer, int);
-int scan_math_style(pointer, int);
-pointer fin_mlist(pointer);
-
-@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
-{\sl 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 ``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 ``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 ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
-similar ``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|.
-
-\yskip\hang|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.
-
-\yskip\hang|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.)
-
-\yskip\hang|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.
-
-\yskip\hang|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.
-
-\yskip\noindent 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
-``italic correction'' added to the width of |P|, but the ``script skip''
-will be added).
-
-@c
-static void unsave_math(void)
-{
- unsave();
- decr(save_ptr);
- flush_node_list(text_dir_ptr);
- assert(saved_type(0) == saved_textdir);
- text_dir_ptr = saved_value(0);
-}
-
-@ Sometimes it is necessary to destroy an mlist. The following
-subroutine empties the current list, assuming that |abs(mode)=mmode|.
-
-@c
-void flush_math(void)
-{
- flush_node_list(vlink(head));
- flush_node_list(incompleat_noad_par);
- vlink(head) = null;
- tail = head;
- incompleat_noad_par = null;
-}
-
-@ Before we can do anything in math mode, we need fonts.
-
-@c
-#define MATHFONTSTACK 8
-#define MATHFONTDEFAULT 0 /* == nullfont */
-
-static sa_tree math_fam_head = NULL;
-
-@ @c
-int fam_fnt(int fam_id, int size_id)
-{
- int n = fam_id + (256 * size_id);
- return (int) get_sa_item(math_fam_head, n).int_value;
-}
-
-void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
-{
- int n = fam_id + (256 * size_id);
- sa_tree_item sa_value = { 0 };
- sa_value.int_value = f;
- set_sa_item(math_fam_head, n, sa_value, lvl);
- fixup_math_parameters(fam_id, size_id, f, lvl);
- if (tracing_assigns_par > 1) {
- begin_diagnostic();
- tprint("{assigning");
- print_char(' ');
- print_cmd_chr(def_family_cmd, size_id);
- print_int(fam_id);
- print_char('=');
- print_font_identifier(fam_fnt(fam_id, size_id));
- print_char('}');
- end_diagnostic(false);
- }
-}
-
-@ @c
-static void unsave_math_fam_data(int gl)
-{
- sa_stack_item st;
- if (math_fam_head->stack == NULL)
- return;
- while (math_fam_head->stack_ptr > 0 &&
- abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
- >= (int) gl) {
- st = math_fam_head->stack[math_fam_head->stack_ptr];
- if (st.level > 0) {
- rawset_sa_item(math_fam_head, st.code, st.value);
- /* now do a trace message, if requested */
- if (tracing_restores_par > 1) {
- int size_id = st.code / 256;
- int fam_id = st.code % 256;
- begin_diagnostic();
- tprint("{restoring");
- print_char(' ');
- print_cmd_chr(def_family_cmd, size_id);
- print_int(fam_id);
- print_char('=');
- print_font_identifier(fam_fnt(fam_id, size_id));
- print_char('}');
- end_diagnostic(false);
- }
- }
- (math_fam_head->stack_ptr)--;
- }
-}
-
-@ and parameters
-
-@c
-#define MATHPARAMSTACK 8
-#define MATHPARAMDEFAULT undefined_math_parameter
-
-static sa_tree math_param_head = NULL;
-
-@ @c
-void def_math_param(int param_id, int style_id, scaled value, int lvl)
-{
- int n = param_id + (256 * style_id);
- sa_tree_item sa_value = { 0 };
- sa_value.int_value = (int) value;
- set_sa_item(math_param_head, n, sa_value, lvl);
- if (tracing_assigns_par > 1) {
- begin_diagnostic();
- tprint("{assigning");
- print_char(' ');
- print_cmd_chr(set_math_param_cmd, param_id);
- print_cmd_chr(math_style_cmd, style_id);
- print_char('=');
- print_int(value);
- print_char('}');
- end_diagnostic(false);
- }
-}
-
-scaled get_math_param(int param_id, int style_id)
-{
- int n = param_id + (256 * style_id);
- return (scaled) get_sa_item(math_param_head, n).int_value;
-}
-
-@ @c
-static void unsave_math_param_data(int gl)
-{
- sa_stack_item st;
- if (math_param_head->stack == NULL)
- return;
- while (math_param_head->stack_ptr > 0 &&
- abs(math_param_head->stack[math_param_head->stack_ptr].level)
- >= (int) gl) {
- st = math_param_head->stack[math_param_head->stack_ptr];
- if (st.level > 0) {
- rawset_sa_item(math_param_head, st.code, st.value);
- /* now do a trace message, if requested */
- if (tracing_restores_par > 1) {
- int param_id = st.code % 256;
- int style_id = st.code / 256;
- begin_diagnostic();
- tprint("{restoring");
- print_char(' ');
- print_cmd_chr(set_math_param_cmd, param_id);
- print_cmd_chr(math_style_cmd, style_id);
- print_char('=');
- print_int(get_math_param(param_id, style_id));
- print_char('}');
- end_diagnostic(false);
- }
- }
- (math_param_head->stack_ptr)--;
- }
-}
-
-@ saving and unsaving of both
-
-@c
-void unsave_math_data(int gl)
-{
- unsave_math_fam_data(gl);
- unsave_math_param_data(gl);
-}
-
-@ Dumping and undumping
-@c
-void dump_math_data(void)
-{
- sa_tree_item sa_value = { 0 };
- if (math_fam_head == NULL) {
- sa_value.int_value = MATHFONTDEFAULT;
- math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
- }
- dump_sa_tree(math_fam_head, "mathfonts");
- if (math_param_head == NULL) {
- sa_value.int_value = MATHPARAMDEFAULT;
- math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
- }
- dump_sa_tree(math_param_head, "mathparameters");
-}
-
-void undump_math_data(void)
-{
- math_fam_head = undump_sa_tree("mathfonts");
- math_param_head = undump_sa_tree("mathparameters");
-}
-
-@ @c
-void initialize_math(void)
-{
- sa_tree_item sa_value = { 0 };
- if (math_fam_head == NULL) {
- sa_value.int_value = MATHFONTDEFAULT;
- math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
- }
- if (math_param_head == NULL) {
- sa_value.int_value = MATHPARAMDEFAULT;
- math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
- initialize_math_spacing();
- }
- return;
-}
-
-@ 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 ``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 ``small'' and ``large''
-starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
-@:TeXbook}{\sl 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 |default_code| is used to stand for the
-|default_rule_thickness| of the current size. The |numerator| and
-|denominator| point to mlists that define a fraction; we always have
-$$\hbox{|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
-
-@c
-pointer new_noad(void)
-{
- pointer p;
- p = new_node(simple_noad, ord_noad_type);
- /* all noad fields are zero after this */
- return p;
-}
-
-@ @c
-pointer new_sub_box(pointer curbox)
-{
- pointer p, q;
- p = new_noad();
- q = new_node(sub_box_node, 0);
- nucleus(p) = q;
- math_list(nucleus(p)) = curbox;
- return p;
-}
-
-@ 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
-``cramped'' versions of these styles. This gives a numerical order that
-is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
-i.e., a smaller style has a larger numerical value.
-@:TeXbook}{\sl The \TeX book@>
-
-@c
-const char *math_style_names[] = {
- "display", "crampeddisplay",
- "text", "crampedtext",
- "script", "crampedscript",
- "scriptscript", "crampedscriptscript",
- NULL
-};
-
-const char *math_param_names[] = {
- "quad", "axis", "operatorsize",
- "overbarkern", "overbarrule", "overbarvgap",
- "underbarkern", "underbarrule", "underbarvgap",
- "radicalkern", "radicalrule", "radicalvgap",
- "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
- "stackvgap", "stacknumup", "stackdenomdown",
- "fractionrule", "fractionnumvgap", "fractionnumup",
- "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
- "limitabovevgap", "limitabovebgap", "limitabovekern",
- "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
- "nolimitsubfactor", "nolimitsupfactor", /* bonus */
- "underdelimitervgap", "underdelimiterbgap",
- "overdelimitervgap", "overdelimiterbgap",
- "subshiftdrop", "supshiftdrop", "subshiftdown",
- "subsupshiftdown", "subtopmax", "supshiftup",
- "supbottommin", "supsubbottommax", "subsupvgap",
- "spaceafterscript", "connectoroverlapmin",
- "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
- "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
- "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
- "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
- "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
- "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
- "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
- "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
- "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
- "openopenspacing", "openclosespacing", "openpunctspacing",
- "openinnerspacing",
- "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
- "closeopenspacing", "closeclosespacing", "closepunctspacing",
- "closeinnerspacing",
- "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
- "punctopenspacing", "punctclosespacing", "punctpunctspacing",
- "punctinnerspacing",
- "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
- "inneropenspacing", "innerclosespacing", "innerpunctspacing",
- "innerinnerspacing",
- NULL
-};
-
-@ @c
-pointer new_style(small_number s)
-{ /* create a style node */
- m_style = s;
- return new_node(style_node, s);
-}
-
-@ 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.
-
-@c
-static pointer new_choice(void)
-{ /* create a choice node */
- return new_node(choice_node, 0); /* the |subtype| is not used */
-}
-
-@ 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 ``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\}\}\}\}\$}'.
-
-@c
-void display_normal_noad(pointer p); /* forward */
-void display_fence_noad(pointer p); /* forward */
-void display_fraction_noad(pointer p); /* forward */
-
-void show_math_node(pointer p)
-{
- switch (type(p)) {
- case style_node:
- print_cmd_chr(math_style_cmd, subtype(p));
- break;
- case choice_node:
- tprint_esc("mathchoice");
- append_char('D');
- show_node_list(display_mlist(p));
- flush_char();
- append_char('T');
- show_node_list(text_mlist(p));
- flush_char();
- append_char('S');
- show_node_list(script_mlist(p));
- flush_char();
- append_char('s');
- show_node_list(script_script_mlist(p));
- flush_char();
- break;
- case simple_noad:
- case radical_noad:
- case accent_noad:
- display_normal_noad(p);
- break;
- case fence_noad:
- display_fence_noad(p);
- break;
- case fraction_noad:
- display_fraction_noad(p);
- break;
- default:
- tprint("Unknown node type!");
- break;
- }
-}
-
-@ Here are some simple routines used in the display of noads.
-
-@c
-static void print_fam_and_char(pointer p)
-{ /* prints family and character */
- tprint_esc("fam");
- print_int(math_fam(p));
- print_char(' ');
- print(math_character(p));
-}
-
-@ @c
-static void print_delimiter(pointer p)
-{
- int a;
- if (delimiteroptionset(p)) {
- tprint(" [ ");
- if (delimiteraxis(p))
- tprint("axis ");
- if (delimiternoaxis(p))
- tprint("noaxis ");
- if (delimiterexact(p))
- tprint("exact ");
- tprint("]");
- }
- if (delimiterheight(p)) {
- tprint("height=");
- print_scaled(delimiterheight(p));
- tprint(" ");
- }
- if (delimiterdepth(p)) {
- tprint("depth=");
- print_scaled(delimiterdepth(p));
- tprint(" ");
- }
- if (delimiterclass(p)) {
- tprint("class=");
- print_int(delimiterclass(p));
- tprint(" ");
- }
- if (small_fam(p) < 0) {
- print_int(-1); /* this should never happen */
- } else if (small_fam(p) < 16 && large_fam(p) < 16 &&
- small_char(p) < 256 && large_char(p) < 256) {
- /* traditional tex style */
- a = small_fam(p) * 256 + small_char(p);
- a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
- print_hex(a);
- } else if ((large_fam(p) == 0 && large_char(p) == 0) ||
- small_char(p) > 65535 || large_char(p) > 65535) {
- /* modern xetex/luatex style */
- print_hex(small_fam(p));
- print_hex(small_char(p));
- }
-}
-
-@ 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).
-@^recursion@>
-
-@c
-static void print_subsidiary_data(pointer p, ASCII_code c)
-{ /* display a noad field */
- if ((int) cur_length >= depth_threshold) {
- if (p != null)
- tprint(" []");
- } else {
- append_char(c); /* include |c| in the recursion history */
- if (p != null) {
- switch (type(p)) {
- case math_char_node:
- print_ln();
- print_current_string();
- print_fam_and_char(p);
- break;
- case sub_box_node:
- show_node_list(math_list(p));
- break;
- case sub_mlist_node:
- if (math_list(p) == null) {
- print_ln();
- print_current_string();
- tprint("{}");
- } else {
- show_node_list(math_list(p));
- }
- break;
- }
- }
- flush_char(); /* remove |c| from the recursion history */
- }
-}
-
-@ @c
-void display_normal_noad(pointer p)
-{
- switch (type(p)) {
- case simple_noad:
- switch (subtype(p)) {
- case ord_noad_type:
- tprint_esc("mathord");
- break;
- case op_noad_type_normal:
- case op_noad_type_limits:
- case op_noad_type_no_limits:
- tprint_esc("mathop");
- if (subtype(p) == op_noad_type_limits)
- tprint_esc("limits");
- else if (subtype(p) == op_noad_type_no_limits)
- tprint_esc("nolimits");
- break;
- case bin_noad_type:
- tprint_esc("mathbin");
- break;
- case rel_noad_type:
- tprint_esc("mathrel");
- break;
- case open_noad_type:
- tprint_esc("mathopen");
- break;
- case close_noad_type:
- tprint_esc("mathclose");
- break;
- case punct_noad_type:
- tprint_esc("mathpunct");
- break;
- case inner_noad_type:
- tprint_esc("mathinner");
- break;
- case over_noad_type:
- tprint_esc("overline");
- break;
- case under_noad_type:
- tprint_esc("underline");
- break;
- case vcenter_noad_type:
- tprint_esc("vcenter");
- break;
- default:
- tprint("<unknown noad type!>");
- break;
- }
- break;
- case radical_noad:
- if (subtype(p) == 6)
- tprint_esc("Udelimiterover");
- else if (subtype(p) == 5)
- tprint_esc("Udelimiterunder");
- else if (subtype(p) == 4)
- tprint_esc("Uoverdelimiter");
- else if (subtype(p) == 3)
- tprint_esc("Uunderdelimiter");
- else if (subtype(p) == 2)
- tprint_esc("Uroot");
- else
- tprint_esc("radical");
- print_delimiter(left_delimiter(p));
- if (degree(p) != null) {
- print_subsidiary_data(degree(p), '/');
- }
- if (radicalwidth(p)) {
- tprint("width=");
- print_scaled(radicalwidth(p));
- tprint(" ");
- }
- if (radicaloptionset(p)) {
- tprint(" [ ");
- if (radicalexact(p))
- tprint("exact ");
- if (radicalleft(p))
- tprint("left ");
- if (radicalmiddle(p))
- tprint("middle ");
- if (radicalright(p))
- tprint("right ");
- tprint("]");
- }
- break;
- case accent_noad:
- if (top_accent_chr(p) != null) {
- if (bot_accent_chr(p) != null) {
- tprint_esc("Umathaccent both");
- } else {
- tprint_esc("Umathaccent");
- }
- } else if (bot_accent_chr(p) != null) {
- tprint_esc("Umathaccent bottom");
- } else {
- tprint_esc("Umathaccent overlay");
- }
- if (accentfraction(p)) {
- tprint(" fraction=");
- print_int(accentfraction(p));
- tprint(" ");
- }
- switch (subtype(p)) {
- case 0:
- if (top_accent_chr(p) != null) {
- if (bot_accent_chr(p) != null) {
- print_fam_and_char(top_accent_chr(p));
- print_fam_and_char(bot_accent_chr(p));
- } else {
- print_fam_and_char(top_accent_chr(p));
- }
- } else if (bot_accent_chr(p) != null) {
- print_fam_and_char(bot_accent_chr(p));
- } else {
- print_fam_and_char(overlay_accent_chr(p));
- }
- break;
- case 1:
- if (top_accent_chr(p) != null) {
- tprint(" fixed ");
- print_fam_and_char(top_accent_chr(p));
- if (bot_accent_chr(p) != null) {
- print_fam_and_char(bot_accent_chr(p));
- }
- } else {
- confusion("display_accent_noad");
- }
- break;
- case 2:
- if (bot_accent_chr(p) != null) {
- if (top_accent_chr(p) != null) {
- print_fam_and_char(top_accent_chr(p));
- }
- tprint(" fixed ");
- print_fam_and_char(bot_accent_chr(p));
- } else{
- confusion("display_accent_noad");
- }
- break;
- case 3:
- if (top_accent_chr(p) != null && bot_accent_chr(p) != null) {
- tprint(" fixed ");
- print_fam_and_char(top_accent_chr(p));
- tprint(" fixed ");
- print_fam_and_char(bot_accent_chr(p));
- } else {
- confusion("display_accent_noad");
- }
- break;
- }
- break;
- }
- print_subsidiary_data(nucleus(p), '.');
- print_subsidiary_data(supscr(p), '^');
- print_subsidiary_data(subscr(p), '_');
-}
-
-@ @c
-void display_fence_noad(pointer p)
-{
- if (subtype(p) == right_noad_side)
- tprint_esc("right");
- else if (subtype(p) == left_noad_side)
- tprint_esc("left");
- else
- tprint_esc("middle");
- print_delimiter(delimiter(p));
-}
-
-@ @c
-void display_fraction_noad(pointer p)
-{
- tprint_esc("fraction, thickness ");
- if (thickness(p) == default_code)
- tprint("= default");
- else
- print_scaled(thickness(p));
- if ((left_delimiter(p) != null) &&
- ((small_fam(left_delimiter(p)) != 0) ||
- (small_char(left_delimiter(p)) != 0) ||
- (large_fam(left_delimiter(p)) != 0) ||
- (large_char(left_delimiter(p)) != 0))) {
- tprint(", left-delimiter ");
- print_delimiter(left_delimiter(p));
- }
- if ((right_delimiter(p) != null) &&
- ((small_fam(right_delimiter(p)) != 0) ||
- (small_char(right_delimiter(p)) != 0) ||
- (large_fam(right_delimiter(p)) != 0) ||
- (large_char(right_delimiter(p)) != 0))) {
- tprint(", right-delimiter ");
- print_delimiter(right_delimiter(p));
- }
- print_subsidiary_data(numerator(p), '\\');
- print_subsidiary_data(denominator(p), '/');
-}
-
-@ 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 ``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|.
-
-@c
-static void new_save_level_math(group_code c)
-{
- set_saved_record(0, saved_textdir, 0, text_dir_ptr);
- text_dir_ptr = new_dir(math_direction_par);
- incr(save_ptr);
- new_save_level(c);
- eq_word_define(int_base + body_direction_code, math_direction_par);
- eq_word_define(int_base + par_direction_code, math_direction_par);
- eq_word_define(int_base + text_direction_code, math_direction_par);
-}
-
-@ @c
-static void push_math(group_code c, int mstyle)
-{
- if (math_direction_par != text_direction_par)
- dir_math_save = true;
- push_nest();
- mode = -mmode;
- incompleat_noad_par = null;
- m_style = mstyle;
- new_save_level_math(c);
-}
-
-@ @c
-static void enter_ordinary_math(void)
-{
- push_math(math_shift_group, text_style);
- eq_word_define(int_base + cur_fam_code, -1);
- if (every_math_par != null)
- begin_token_list(every_math_par, every_math_text);
-}
-
-@ @c
-void enter_display_math(void);
-
-@ 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.
-
-@c
-void init_math(void)
-{
- if (cur_cmd == math_shift_cmd) {
- get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
- if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
- enter_display_math();
- } else {
- back_input();
- enter_ordinary_math();
- }
- } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) {
- enter_display_math();
- } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
- enter_ordinary_math();
- } else {
- you_cant();
- }
-}
-
-@ 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.
-
-@c
-void start_eq_no(void)
-{
- set_saved_record(0, saved_eqno, 0, cur_chr);
- incr(save_ptr);
- enter_ordinary_math();
-}
-
-@ 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.
-
-@c
-void math_left_brace(void)
-{
- pointer q;
- tail_append(new_noad());
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- back_input();
- (void) scan_math(nucleus(tail), m_style);
-}
-
-@ 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.
-
-@c
-static boolean math_and_text_reversed_p(void)
-{
- int i = save_ptr - 1;
- while (save_type(i) != level_boundary)
- i--;
- while (i < save_ptr) {
- if (save_type(i) == restore_old_value &&
- save_value(i) == int_base + par_direction_code) {
- if (textdir_opposite(math_direction_par, save_value(i - 1)))
- return true;
- }
- i++;
- }
- return false;
-}
-
-@ 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|.
-
-@c
-void enter_display_math(void)
-{
- scaled w; /* new or partial |pre_display_size| */
- scaled l; /* new |display_width| */
- scaled s; /* new |display_indent| */
- pointer p;
- int n; /* scope of paragraph shape specification */
- if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
- (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
- type(tail) == local_par_node && vlink(tail) == null)) {
- if (vlink(head) == tail) {
- /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
- there is another display immediately following, we have to get rid
- of that node */
- flush_node(tail);
- }
- pop_nest();
- w = -max_dimen;
- } else {
- line_break(true, math_shift_group);
- w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par);
- }
- /* 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_ptr == null) {
- if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) {
- halfword used_hang_indent = swap_hang_indent(hang_indent_par);
- l = hsize_par - abs(used_hang_indent);
- if (used_hang_indent > 0)
- s = used_hang_indent;
- else
- s = 0;
- } else {
- l = hsize_par;
- s = 0;
- }
- } else {
- n = vinfo(par_shape_par_ptr + 1);
- if (prev_graf_par + 2 >= n)
- p = par_shape_par_ptr + 2 * n + 1;
- else
- p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1;
- s = varmem[(p - 1)].cint;
- l = varmem[p].cint;
- s = swap_parshape_indent(s,l);
- }
-
- push_math(math_shift_group, display_style);
- mode = mmode;
- eq_word_define(int_base + cur_fam_code, -1);
- eq_word_define(dimen_base + pre_display_size_code, w);
- eq_word_define(dimen_base + display_width_code, l);
- eq_word_define(dimen_base + display_indent_code, s);
- eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0));
- if (every_display_par != null)
- begin_token_list(every_display_par, every_display_text);
- if (nest_ptr == 1) {
- checked_page_filter(before_display);
- build_page();
- }
-}
-
-@ The next routine parses all variations of a delimiter code. The |extcode|
- tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the
- |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.).
- (the class is passed on for conversion to \.{\\mathchar}).
-
-@c
-static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
-{
- const char *hlp[] = {
- "I'm going to use 0 instead of that illegal code value.",
- NULL
- };
- delcodeval d;
- int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
- if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */
- scan_int();
- /* "MFCCFCC or "FCCFCC */
- if (doclass) {
- mcls = (cur_val / 0x1000000);
- cur_val = (cur_val & 0xFFFFFF);
- }
- if (cur_val > 0xFFFFFF) {
- tex_error("Invalid delimiter code", hlp);
- cur_val = 0;
- }
- msfam = (cur_val / 0x100000);
- mschr = (cur_val % 0x100000) / 0x1000;
- mlfam = (cur_val & 0xFFF) / 0x100;
- mlchr = (cur_val % 0x100);
- } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */
- /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */
- if (doclass) {
- scan_int();
- mcls = cur_val;
- }
- scan_int();
- msfam = cur_val;
- scan_char_num();
- mschr = cur_val;
- if (msfam < 0 || msfam > 255) {
- tex_error("Invalid delimiter code", hlp);
- msfam = 0;
- mschr = 0;
- }
- mlfam = 0;
- mlchr = 0;
- } else if (extcode == 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) { /* such a primitive doesn't exist */
- confusion("umathnum_mathcode");
- }
- scan_int();
- msfam = (cur_val / 0x200000);
- mschr = cur_val & 0x1FFFFF;
- if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
- tex_error("Invalid delimiter code", hlp);
- msfam = 0;
- mschr = 0;
- }
- mlfam = 0;
- mlchr = 0;
- } else {
- /* something's gone wrong */
- confusion("unknown_extcode");
- }
- d.class_value = mcls;
- d.small_family_value = msfam;
- d.small_character_value = mschr;
- d.large_family_value = mlfam;
- d.large_character_value = mlchr;
- return d;
-}
-
-@ @c
-void scan_extdef_del_code(int level, int extcode)
-{
- delcodeval d;
- int p;
- scan_char_num();
- p = cur_val;
- scan_optional_equals();
- d = do_scan_extdef_del_code(extcode, false);
- set_del_code(p, d.small_family_value, d.small_character_value,
- d.large_family_value, d.large_character_value,
- (quarterword) (level));
-}
-
-@ @c
-mathcodeval scan_mathchar(int extcode)
-{
- char errstr[255] = { 0 };
- const char *hlp[] = {
- "I'm going to use 0 instead of that illegal code value.",
- NULL
- };
- mathcodeval d;
- int mcls = 0, mfam = 0, mchr = 0;
- if (extcode == tex_mathcode) { /* \.{\\mathcode} */
- /* "TFCC */
- scan_int();
- if (cur_val > 0x8000) {
- /*
- tex_error("Invalid math code", hlp);
- cur_val = 0;
- */
- /* needed for latex: fallback to umathnum_mathcode */
- mfam = (cur_val / 0x200000) & 0x7FF;
- mcls = mfam % 0x08;
- mfam = mfam / 0x08;
- mchr = cur_val & 0x1FFFFF;
- if (mchr > 0x10FFFF) {
- tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp);
- mcls = 0;
- mfam = 0;
- mchr = 0;
- }
- } else {
- if (cur_val < 0) {
- snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val);
- tex_error(errstr, hlp);
- cur_val = 0;
- }
- mcls = (cur_val / 0x1000);
- mfam = ((cur_val % 0x1000) / 0x100);
- mchr = (cur_val % 0x100);
- }
- } else if (extcode == umath_mathcode) {
- /* <0-0x7> <0-0xFF> <0-0x10FFFF> */
- scan_int();
- mcls = cur_val;
- scan_int();
- mfam = cur_val;
- scan_char_num();
- mchr = cur_val;
- if (mcls < 0 || mcls > 7 || mfam > 255) {
- tex_error("Invalid math code", hlp);
- mchr = 0;
- mfam = 0;
- mcls = 0;
- }
- } else if (extcode == umathnum_mathcode) {
- /* "FFT<21bits> */
- /* 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 */
- scan_int();
- mfam = (cur_val / 0x200000) & 0x7FF;
- mcls = mfam % 0x08;
- mfam = mfam / 0x08;
- mchr = cur_val & 0x1FFFFF;
- if (mchr > 0x10FFFF) {
- tex_error("Invalid math code", hlp);
- mcls = 0;
- mfam = 0;
- mchr = 0;
- }
- } else {
- /* something's gone wrong */
- confusion("unknown_extcode");
- }
- d.class_value = mcls;
- d.family_value = mfam;
- d.character_value = mchr;
- return d;
-}
-
-@ @c
-void scan_extdef_math_code(int level, int extcode)
-{
- mathcodeval d;
- int p;
- scan_char_num();
- p = cur_val;
- scan_optional_equals();
- d = scan_mathchar(extcode);
- set_math_code(p, d.class_value,
- d.family_value, d.character_value, (quarterword) (level));
-}
-
-@ this reads in a delcode when actually a mathcode is needed
-@c
-mathcodeval scan_delimiter_as_mathchar(int extcode)
-{
- delcodeval dval;
- mathcodeval mval;
- dval = do_scan_extdef_del_code(extcode, true);
- mval.class_value = dval.class_value;
- mval.family_value = dval.small_family_value;
- mval.character_value = dval.small_character_value;
- return mval;
-}
-
-@ 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|.
-
-@c
-#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
-
-int scan_math_style(pointer p, int mstyle)
-{
- get_next_nb_nr();
- back_input();
- scan_left_brace();
- set_saved_record(0, saved_math, 0, p);
- incr(save_ptr);
- push_math(math_group, mstyle);
- return 1;
-}
-
-int scan_math(pointer p, int mstyle)
-{
- /* label restart,reswitch,exit; */
- mathcodeval mval = { 0, 0, 0 };
- assert(p != null);
- RESTART:
- get_next_nb_nr();
- RESWITCH:
- switch (cur_cmd) {
- case letter_cmd:
- case other_char_cmd:
- case char_given_cmd:
- mval = get_math_code(cur_chr);
- if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
- cur_cs = active_to_cs(cur_chr, true);
- cur_cmd = eq_type(cur_cs);
- cur_chr = equiv(cur_cs);
- x_token();
- back_input();
- goto RESTART;
- }
- break;
- case char_num_cmd:
- scan_char_num();
- cur_chr = cur_val;
- cur_cmd = char_given_cmd;
- goto RESWITCH;
- break;
- case math_char_num_cmd:
- if (cur_chr == 0)
- mval = scan_mathchar(tex_mathcode);
- else if (cur_chr == 1)
- mval = scan_mathchar(umath_mathcode);
- else if (cur_chr == 2)
- mval = scan_mathchar(umathnum_mathcode);
- else
- confusion("scan_math");
- break;
- case math_given_cmd:
- mval = mathchar_from_integer(cur_chr, tex_mathcode);
- break;
- case xmath_given_cmd:
- mval = mathchar_from_integer(cur_chr, umath_mathcode);
- break;
- case delim_num_cmd:
- if (cur_chr == 0)
- mval = scan_delimiter_as_mathchar(tex_mathcode);
- else if (cur_chr == 1)
- mval = scan_delimiter_as_mathchar(umath_mathcode);
- else
- confusion("scan_math");
- break;
- default:
- /* The pointer |p| is placed on |save_stack| while a complex subformula
- is being scanned. */
- back_input();
- scan_left_brace();
- set_saved_record(0, saved_math, 0, p);
- incr(save_ptr);
- push_math(math_group, mstyle);
- return 1;
- }
- type(p) = math_char_node;
- math_character(p) = mval.character_value;
- if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range)
- math_fam(p) = cur_fam_par;
- else
- math_fam(p) = mval.family_value;
- return 0;
-}
-
-@ The |set_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.
-
-@c
-void set_math_char(mathcodeval mval)
-{
- pointer p; /* the new noad */
- if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
- cur_cs = active_to_cs(cur_chr, true);
- cur_cmd = eq_type(cur_cs);
- cur_chr = equiv(cur_cs);
- x_token();
- back_input();
- } else {
- pointer q;
- p = new_noad();
- q = new_node(math_char_node, 0);
- nucleus(p) = q;
- math_character(nucleus(p)) = mval.character_value;
- math_fam(nucleus(p)) = mval.family_value;
- if (mval.class_value == math_use_current_family_code) {
- if (cur_fam_par_in_range)
- math_fam(nucleus(p)) = cur_fam_par;
- subtype(p) = ord_noad_type;
- } else {
- switch (mval.class_value) {
- /* *INDENT-OFF* */
- case 0: subtype(p) = ord_noad_type; break;
- case 1: subtype(p) = op_noad_type_normal; break;
- case 2: subtype(p) = bin_noad_type; break;
- case 3: subtype(p) = rel_noad_type; break;
- case 4: subtype(p) = open_noad_type; break;
- case 5: subtype(p) = close_noad_type; break;
- case 6: subtype(p) = punct_noad_type; break;
- /* *INDENT-ON* */
- }
- }
- vlink(tail) = p;
- tail = p;
- }
-}
-
-@ The |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.
-
-@c
-void math_char_in_text(mathcodeval mval)
-{
- pointer p; /* the new node */
- if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
- cur_cs = active_to_cs(cur_chr, true);
- cur_cmd = eq_type(cur_cs);
- cur_chr = equiv(cur_cs);
- x_token();
- back_input();
- } else {
- p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value);
- vlink(tail) = p;
- tail = p;
- }
-}
-
-@ @c
-void math_math_comp(void)
-{
- pointer q;
- tail_append(new_noad());
- subtype(tail) = (quarterword) cur_chr;
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- if (cur_chr == over_noad_type)
- (void) scan_math(nucleus(tail), cramped_style(m_style));
- else
- (void) scan_math(nucleus(tail), m_style);
-}
-
-@ @c
-void math_limit_switch(void)
-{
- const char *hlp[] = {
- "I'm ignoring this misplaced \\limits or \\nolimits command.",
- NULL
- };
- if (head != tail) {
- if (type(tail) == simple_noad &&
- (subtype(tail) == op_noad_type_normal ||
- subtype(tail) == op_noad_type_limits ||
- subtype(tail) == op_noad_type_no_limits)) {
- subtype(tail) = (quarterword) cur_chr;
- return;
- }
- }
- tex_error("Limit controls must follow a math operator", hlp);
-}
-
-@ 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.
-
-@c
-static void scan_delimiter(pointer p, int r)
-{
- delcodeval dval = { 0, 0, 0, 0, 0 };
- if (r == tex_mathcode) { /* \.{\\radical} */
- dval = do_scan_extdef_del_code(tex_mathcode, true);
- } else if (r == umath_mathcode) { /* \.{\\Uradical} */
- dval = do_scan_extdef_del_code(umath_mathcode, false);
- } else if (r == no_mathcode) {
- get_next_nb_nr();
- switch (cur_cmd) {
- case letter_cmd:
- case other_char_cmd:
- dval = get_del_code(cur_chr);
- break;
- case delim_num_cmd:
- if (cur_chr == 0) /* \.{\\delimiter} */
- dval = do_scan_extdef_del_code(tex_mathcode, true);
- else if (cur_chr == 1) /* \.{\\Udelimiter} */
- dval = do_scan_extdef_del_code(umath_mathcode, true);
- else
- confusion("scan_delimiter1");
- break;
- default:
- dval.small_family_value = -1;
- break;
- }
- } else {
- confusion("scan_delimiter2");
- }
- if (p == null)
- return;
- if (dval.small_family_value < 0) {
- const char *hlp[] = {
- "I was expecting to see something like `(' or `\\{' or",
- "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
- "should probably delete the `{' by typing `1' now, so that",
- "braces don't get unbalanced. Otherwise just proceed",
- "Acceptable delimiters are characters whose \\delcode is",
- "nonnegative, or you can use `\\delimiter <delimiter code>'.",
- NULL
- };
- back_error("Missing delimiter (. inserted)", hlp);
- small_fam(p) = 0;
- small_char(p) = 0;
- large_fam(p) = 0;
- large_char(p) = 0;
- } else {
- small_fam(p) = dval.small_family_value;
- small_char(p) = dval.small_character_value;
- large_fam(p) = dval.large_family_value;
- large_char(p) = dval.large_character_value;
- }
- return;
-}
-
-@ @c
-void math_radical(void)
-{
- halfword q;
- int chr_code = cur_chr;
- halfword options = 0;
- tail_append(new_node(radical_noad, chr_code));
- q = new_node(delim_node, 0);
- left_delimiter(tail) = q;
- while (1) {
- if (scan_keyword("width")) {
- scan_dimen(false,false,false);
- radicalwidth(tail) = cur_val ;
- } else if (scan_keyword("left")) {
- options = options | noad_option_left ;
- } else if (scan_keyword("middle")) {
- options = options | noad_option_middle ;
- } else if (scan_keyword("right")) {
- options = options | noad_option_right ;
- } else {
- break;
- }
- }
- radicaloptions(tail) = options;
- if (chr_code == 0) /* \.{\\radical} */
- scan_delimiter(left_delimiter(tail), tex_mathcode);
- else if (chr_code == 1) /* \.{\\Uradical} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 2) /* \.{\\Uroot} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 3) /* \.{\\Uunderdelimiter} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 4) /* \.{\\Uoverdelimiter} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 5) /* \.{\\Udelimiterunder} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 6) /* \.{\\Udelimiterover} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 7) /* \.{\\Uhextensible} */
- scan_delimiter(left_delimiter(tail), umath_mathcode);
- else
- confusion("math_radical");
- if (chr_code == 7) {
- q = new_node(sub_box_node, 0); /* type will change */
- nucleus(tail) = q;
- return;
- } else if (chr_code == 2) {
- /* the trick with the |vlink(q)| is used by |scan_math|
- to decide whether it needs to go on */
- q = new_node(math_char_node, 0);
- vlink(q) = tail;
- degree(tail) = q;
- if (!scan_math(degree(tail), sup_sup_style(m_style))) {
- vlink(degree(tail)) = null;
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- (void) scan_math(nucleus(tail), cramped_style(m_style));
- }
- } else {
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- (void) scan_math(nucleus(tail), cramped_style(m_style));
- }
-}
-
-@ @c
-void math_ac(void)
-{
- halfword q;
- mathcodeval t = { 0, 0, 0 };
- mathcodeval b = { 0, 0, 0 };
- mathcodeval o = { 0, 0, 0 };
- if (cur_cmd == accent_cmd) {
- const char *hlp[] = {
- "I'm changing \\accent to \\mathaccent here; wish me luck.",
- "(Accents are not the same in formulas as they are in text.)",
- NULL
- };
- tex_error("Please use \\mathaccent for accents in math mode", hlp);
- }
- tail_append(new_node(accent_noad, 0));
- if (cur_chr == 0) { /* \.{\\mathaccent} */
- t = scan_mathchar(tex_mathcode);
- } else if (cur_chr == 1) { /* \.{\\Umathaccent} */
- if (scan_keyword("fixed")) {
- /* top */
- subtype(tail) = 1;
- t = scan_mathchar(umath_mathcode);
- } else if (scan_keyword("both")) {
- /* top bottom */
- if (scan_keyword("fixed")) {
- subtype(tail) = 1;
- }
- t = scan_mathchar(umath_mathcode);
- if (scan_keyword("fixed")) {
- subtype(tail) += 2;
- }
- b = scan_mathchar(umath_mathcode);
- } else if (scan_keyword("bottom")) {
- /* bottom */
- if (scan_keyword("fixed")) {
- subtype(tail) = 2;
- }
- b = scan_mathchar(umath_mathcode);
- } else if (scan_keyword("top")) {
- /* top */
- if (scan_keyword("fixed")) {
- subtype(tail) = 1;
- }
- t = scan_mathchar(umath_mathcode);
- } else if (scan_keyword("overlay")) {
- /* overlay */
- if (scan_keyword("fixed")) {
- subtype(tail) = 1;
- }
- o = scan_mathchar(umath_mathcode);
- } else {
- /* top */
- t = scan_mathchar(umath_mathcode);
- }
- if (scan_keyword("fraction")) {
- scan_int();
- accentfraction(tail) = cur_val;
- }
- } else {
- confusion("mathaccent");
- }
- if (!(t.character_value == 0 && t.family_value == 0)) {
- q = new_node(math_char_node, 0);
- top_accent_chr(tail) = q;
- math_character(top_accent_chr(tail)) = t.character_value;
- if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range)
- math_fam(top_accent_chr(tail)) = cur_fam_par;
- else
- math_fam(top_accent_chr(tail)) = t.family_value;
- }
- if (!(b.character_value == 0 && b.family_value == 0)) {
- q = new_node(math_char_node, 0);
- bot_accent_chr(tail) = q;
- math_character(bot_accent_chr(tail)) = b.character_value;
- if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range)
- math_fam(bot_accent_chr(tail)) = cur_fam_par;
- else
- math_fam(bot_accent_chr(tail)) = b.family_value;
- }
- if (!(o.character_value == 0 && o.family_value == 0)) {
- q = new_node(math_char_node, 0);
- overlay_accent_chr(tail) = q;
- math_character(overlay_accent_chr(tail)) = o.character_value;
- if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range)
- math_fam(overlay_accent_chr(tail)) = cur_fam_par;
- else
- math_fam(overlay_accent_chr(tail)) = o.family_value;
- }
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- (void) scan_math(nucleus(tail), cramped_style(m_style));
-}
-
-@ @c
-pointer math_vcenter_group(pointer p)
-{
- pointer q, r;
- q = new_noad();
- subtype(q) = vcenter_noad_type;
- r = new_node(sub_box_node, 0);
- nucleus(q) = r;
- math_list(nucleus(q)) = p;
- return q;
-}
-
-@ The routine that scans the four mlists of a \.{\\mathchoice} is very
-much like the routine that builds discretionary nodes.
-
-@c
-void append_choices(void)
-{
- tail_append(new_choice());
- incr(save_ptr);
- set_saved_record(-1, saved_choices, 0, 0);
- push_math(math_choice_group, display_style);
- scan_left_brace();
-}
-
-@ @c
-void build_choices(void)
-{
- pointer p; /* the current mlist */
- int prev_style;
- prev_style = m_style;
- unsave_math();
- p = fin_mlist(null);
- assert(saved_type(-1) == saved_choices);
- switch (saved_value(-1)) {
- case 0:
- display_mlist(tail) = p;
- break;
- case 1:
- text_mlist(tail) = p;
- break;
- case 2:
- script_mlist(tail) = p;
- break;
- case 3:
- script_script_mlist(tail) = p;
- decr(save_ptr);
- return;
- break;
- } /* there are no other cases */
- set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
- push_math(math_choice_group, (prev_style + 2));
- scan_left_brace();
-}
-
-@ Subscripts and superscripts are attached to the previous nucleus by the
-action procedure called |sub_sup|.
-
-@c
-void sub_sup(void)
-{
- pointer q;
- if (tail == head || (!scripts_allowed(tail))) {
- tail_append(new_noad());
- q = new_node(sub_mlist_node, 0);
- nucleus(tail) = q;
- }
- if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */
- if (supscr(tail) != null) {
- const char *hlp[] = {
- "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
- };
- tail_append(new_noad());
- q = new_node(sub_mlist_node, 0);
- nucleus(tail) = q;
- tex_error("Double superscript", hlp);
- }
- q = new_node(math_char_node, 0);
- supscr(tail) = q;
- (void) scan_math(supscr(tail), sup_style(m_style));
- } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
- if (subscr(tail) != null) {
- const char *hlp[] = {
- "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
- };
- tail_append(new_noad());
- q = new_node(sub_mlist_node, 0);
- nucleus(tail) = q;
- tex_error("Double subscript", hlp);
- }
- q = new_node(math_char_node, 0);
- subscr(tail) = q;
- (void) scan_math(subscr(tail), sub_style(m_style));
- }
-}
-
-@ An operation like `\.{\\over}' causes the current mlist to go into a
-state of suspended animation: |incompleat_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 incompleat fraction noad, and that noad will become the
-whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
-delimiters.
-
-@c
-void math_fraction(void)
-{
- halfword c; /* the type of generalized fraction we are scanning */
- pointer q;
- halfword options = 0;
- halfword temp_value;
- c = cur_chr;
- if (incompleat_noad_par != null) {
- const char *hlp[] = {
- "I'm ignoring this fraction specification, since I don't",
- "know whether a construction like `x \\over y \\over z'",
- "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
- NULL
- };
- if (c >= delimited_code) {
- scan_delimiter(null, no_mathcode);
- scan_delimiter(null, no_mathcode);
- }
- if ((c % delimited_code) == above_code)
- scan_normal_dimen();
- tex_error("Ambiguous; you need another { and }", hlp);
- } else {
- incompleat_noad_par = new_node(fraction_noad, 0);
- temp_value = new_node(sub_mlist_node, 0);
- numerator(incompleat_noad_par) = temp_value;
- math_list(numerator(incompleat_noad_par)) = vlink(head);
- vlink(head) = null;
- tail = head;
- m_style = cramped_style(m_style);
-
- if ((c % delimited_code) == skewed_code) {
- q = new_node(delim_node, 0);
- middle_delimiter(incompleat_noad_par) = q;
- scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode);
- }
- if (c >= delimited_code) {
- q = new_node(delim_node, 0);
- left_delimiter(incompleat_noad_par) = q;
- q = new_node(delim_node, 0);
- right_delimiter(incompleat_noad_par) = q;
- scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode);
- scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode);
- }
- switch (c % delimited_code) {
- case above_code:
- while (1) {
- if (scan_keyword("exact")) {
- options = options | noad_option_exact ;
- } else {
- break;
- }
- }
- fractionoptions(incompleat_noad_par) = options;
- scan_normal_dimen();
- thickness(incompleat_noad_par) = cur_val;
- break;
- case over_code:
- thickness(incompleat_noad_par) = default_code;
- break;
- case atop_code:
- thickness(incompleat_noad_par) = 0;
- break;
- case skewed_code:
- while (1) {
- if (scan_keyword("exact")) {
- options = options | noad_option_exact ;
- } else if (scan_keyword("noaxis")) {
- options = options | noad_option_no_axis ;
- } else {
- break;
- }
- }
- fractionoptions(incompleat_noad_par) = options;
- thickness(incompleat_noad_par) = 0;
- break;
- }
- }
-}
-
-@ At the end of a math formula or subformula, the |fin_mlist| routine is
-called upon to return a pointer to the newly completed mlist, and to
-pop the nest back to the enclosing semantic level. The parameter to
-|fin_mlist|, if not null, points to a |fence_noad| that ends the
-current mlist; this |fence_noad| has not yet been appended.
-
-@c
-pointer fin_mlist(pointer p)
-{
- pointer q; /* the mlist to return */
- if (incompleat_noad_par != null) {
- if (denominator(incompleat_noad_par) != null) {
- type(denominator(incompleat_noad_par)) = sub_mlist_node;
- } else {
- q = new_node(sub_mlist_node, 0);
- denominator(incompleat_noad_par) = q;
- }
- math_list(denominator(incompleat_noad_par)) = vlink(head);
- if (p == null) {
- q = incompleat_noad_par;
- } else {
- q = math_list(numerator(incompleat_noad_par));
- if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
- || (delim_par == null))
- confusion("right"); /* this can't happen */
- math_list(numerator(incompleat_noad_par)) = vlink(delim_par);
- vlink(delim_par) = incompleat_noad_par;
- vlink(incompleat_noad_par) = p;
- }
- } else {
- vlink(tail) = p;
- q = vlink(head);
- }
- pop_nest();
- return q;
-}
-
-@ 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/superscripts, or when they
-surround an accent that is the nucleus of an Ord atom.
-
-@c
-void close_math_group(pointer p)
-{
- int old_style = m_style;
- unsave_math();
-
- decr(save_ptr);
- assert(saved_type(0) == saved_math);
- type(saved_value(0)) = sub_mlist_node;
- p = fin_mlist(null);
- math_list(saved_value(0)) = p;
- if (p != null) {
- if (vlink(p) == null) {
- if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
- if (subscr(p) == null && supscr(p) == null) {
- type(saved_value(0)) = type(nucleus(p));
- if (type(nucleus(p)) == math_char_node) {
- math_fam(saved_value(0)) = math_fam(nucleus(p));
- math_character(saved_value(0)) =
- math_character(nucleus(p));
- } else {
- math_list(saved_value(0)) = math_list(nucleus(p));
- math_list(nucleus(p)) = null;
- }
- delete_attribute_ref(node_attr(saved_value(0)));
- node_attr(saved_value(0)) = node_attr(nucleus(p));
- node_attr(nucleus(p)) = null;
- flush_node(p);
- }
- } else if (type(p) == accent_noad) {
- if (saved_value(0) == nucleus(tail)) {
- if (type(tail) == simple_noad
- && subtype(tail) == ord_noad_type) {
- pointer q = head;
- while (vlink(q) != tail)
- q = vlink(q);
- vlink(q) = p;
- nucleus(tail) = null;
- subscr(tail) = null;
- supscr(tail) = null;
- delete_attribute_ref(node_attr(p));
- node_attr(p) = node_attr(tail);
- node_attr(tail) = null;
- flush_node(tail);
- tail = p;
- }
- }
- }
- }
- }
- if (vlink(saved_value(0)) > 0) {
- pointer q;
- q = new_node(math_char_node, 0);
- nucleus(vlink(saved_value(0))) = q;
- vlink(saved_value(0)) = null;
- saved_value(0) = q;
- (void) scan_math(saved_value(0), old_style);
- /* restart */
- }
-}
-
-@ 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 ore several \.{\\middle}
-delimiters to appear between \.{\\left} and \.{\\right}.
-
-@c
-void math_left_right(void)
-{
- halfword t; /* |left_noad_side| .. |right_noad_side| */
- pointer p; /* new noad */
- pointer q; /* resulting mlist */
- pointer r; /* temporary */
- halfword ht = 0;
- halfword dp = 0;
- halfword options = 0;
- halfword type = -1 ;
- t = cur_chr;
-
- if (t > 10) {
- /* we have \Uleft \Uright \Umiddle */
- t = t - 10;
- while (1) {
- if (scan_keyword("height")) {
- scan_dimen(false,false,false);
- ht = cur_val ;
- } else if (scan_keyword("depth")) {
- scan_dimen(false,false,false);
- dp = cur_val ;
- } else if (scan_keyword("axis")) {
- options = options | noad_option_axis ;
- } else if (scan_keyword("noaxis")) {
- options = options | noad_option_no_axis ;
- } else if (scan_keyword("exact")) {
- options = options | noad_option_exact ;
- } else if (scan_keyword("class")) {
- scan_int();
- type = cur_val ;
- } else {
- break;
- }
- }
- }
-
- if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) {
- if (cur_group == math_shift_group) {
- scan_delimiter(null, no_mathcode);
- if (t == middle_noad_side) {
- const char *hlp[] = {
- "I'm ignoring a \\middle that had no matching \\left.",
- NULL
- };
- tex_error("Extra \\middle", hlp);
- } else {
- const char *hlp[] = {
- "I'm ignoring a \\right that had no matching \\left.",
- NULL
- };
- tex_error("Extra \\right", hlp);
- }
- } else {
- off_save();
- }
- } else {
- p = new_noad();
- type(p) = fence_noad;
- subtype(p) = (quarterword) t;
- r = new_node(delim_node, 0);
- delimiter(p) = r;
-
- delimiterheight(p) = ht;
- delimiterdepth(p) = dp;
- delimiteroptions(p) = options;
- delimiterclass(p) = type;
- delimiteritalic(p) = 0;
-
- scan_delimiter(delimiter(p), no_mathcode);
-
- if (t == no_noad_side) {
- tail_append(new_noad());
- subtype(tail) = inner_noad_type;
- r = new_node(sub_mlist_node, 0);
- nucleus(tail) = r;
- math_list(nucleus(tail)) = p;
- return ;
- }
-
- if (t == left_noad_side) {
- q = p;
- } else {
- q = fin_mlist(p);
- unsave_math();
- }
- if (t != right_noad_side) {
- push_math(math_left_group, m_style);
- vlink(head) = q;
- tail = p;
- delim_par = p;
- } else {
- tail_append(new_noad());
- subtype(tail) = inner_noad_type;
- r = new_node(sub_mlist_node, 0);
- nucleus(tail) = r;
- math_list(nucleus(tail)) = q;
- }
- }
-}
-
-@ \TeX\ gets to the following part of the program when
-the first `\.\$' ending a display has been scanned.
-
-@c
-static void check_second_math_shift(void)
-{
- get_x_token();
- if (cur_cmd != math_shift_cmd) {
- const char *hlp[] = {
- "The `$' that I just saw supposedly matches a previous `$$'.",
- "So I shall assume that you typed `$$' both times.",
- NULL
- };
- back_error("Display math should end with $$", hlp);
- }
-}
-
-static void check_display_math_end(void)
-{
- if (cur_chr != cramped_display_style) {
- const char *hlp[] = {
- "I shall assume that you typed that.",
- NULL
- };
- tex_error("Display math should end with \\Ustopdisplaymath", hlp);
- }
-}
-
-static void check_inline_math_end(void)
-{
- if (cur_chr != cramped_text_style) {
- const char *hlp[] = {
- "I shall assume that you typed that.",
- NULL
- };
- tex_error("Inline math should end with \\Ustopmath", hlp);
- }
-}
-
-@ @c
-static void resume_after_display(void)
-{
- if (cur_group != math_shift_group)
- confusion("display");
- unsave_math();
- prev_graf_par = prev_graf_par + 3;
- push_nest();
- mode = hmode;
- space_factor_par = 1000;
- /* this needs to be intercepted in the display math start ! */
- tail_append(make_local_par_node(penalty_par_code));
- get_x_token();
- if (cur_cmd != spacer_cmd)
- back_input();
- if (nest_ptr == 1) {
- normal_page_filter(after_display);
- build_page();
- }
-}
-
-@ The fussiest 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).
-
- |p| points to the mlist for the formula.
- |a| is either |null| or it points to a box containing the equation number.
- |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
-
-@c
-#define inject_display_skip_before(g) \
- switch (display_skip_mode_par) { \
- case 0 : /* normal tex */ \
- tail_append(new_param_glue(g)); \
- break;\
- case 1 : /* always */ \
- tail_append(new_param_glue(g)); \
- break; \
- case 2 : /* non-zero */ \
- if (g != 0 && ! glue_is_zero(glue_par(g))) \
- tail_append(new_param_glue(g)); \
- break; \
- case 3: /* ignore */ \
- break; \
- }
-
-#define inject_display_skip_after(g) \
- switch (display_skip_mode_par) { \
- case 0 : /* normal tex */ \
- if (g != 0 && glue_is_positive(glue_par(g))) \
- tail_append(new_param_glue(g)); \
- break; \
- case 1 : /* always */ \
- tail_append(new_param_glue(g)); \
- break; \
- case 2 : /* non-zero */ \
- if (g != 0 && ! glue_is_zero(glue_par(g))) \
- tail_append(new_param_glue(g)); \
- break; \
- case 3: /* ignore */ \
- break; \
- }
-
-static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
-{
- pointer eq_box; /* box containing the equation */
- scaled eq_w; /* width of the equation */
- scaled line_w; /* width of the line */
- scaled eqno_w; /* width of equation number */
- scaled eqno_w2; /* width of equation number plus space to separate from equation */
- scaled line_s; /* move the line right this much */
- scaled d; /* displacement of equation in the line */
- small_number g1, g2; /* glue parameter codes for before and after */
- pointer r,s; /* kern nodes used to position the display */
- pointer t; /* tail of adjustment list */
- pointer pre_t; /* tail of pre-adjustment list */
- boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
- scaled eqno_width;
- swap_dir = (pre_display_direction_par < 0 ? true : false );
- if (eqno_box != null && swap_dir)
- l = !l;
- adjust_tail = adjust_head;
- pre_adjust_tail = pre_adjust_head;
- eq_box = hpack(p, 0, additional, -1);
- subtype(eq_box) = equation_list; /* new */
- build_attribute_list(eq_box);
- p = list_ptr(eq_box);
- t = adjust_tail;
- adjust_tail = null;
- pre_t = pre_adjust_tail;
- pre_adjust_tail = null;
- eq_w = width(eq_box);
- line_w = display_width_par;
- line_s = display_indent_par;
- if (eqno_box == null) {
- eqno_w = 0;
- eqno_width = 0;
- eqno_w2 = 0;
- } else {
- eqno_w = width(eqno_box);
- eqno_width = eqno_w;
- eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000);
- subtype(eqno_box) = equation_number_list; /* new */
- /* build_attribute_list(eqno_box); */ /* probably already set */
- }
- if (eq_w + eqno_w2 > line_w) {
- /* The user can force the equation number to go on a separate line
- by causing its width to be zero. */
- if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
- || (total_shrink[sfi] != 0)
- || (total_shrink[fil] != 0)
- || (total_shrink[fill] != 0)
- || (total_shrink[filll] != 0))) {
- list_ptr(eq_box) = null;
- flush_node(eq_box);
- eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
- subtype(eq_box) = equation_list; /* new */
- build_attribute_list(eq_box);
- } else {
- eqno_w = 0;
- if (eq_w > line_w) {
- list_ptr(eq_box) = null;
- flush_node(eq_box);
- eq_box = hpack(p, line_w, exactly, -1);
- subtype(eq_box) = equation_list; /* new */
- build_attribute_list(eq_box);
- }
- }
- eq_w = width(eq_box);
- }
- /* We try first to center the display without regard to the existence of
- the equation number. If that would make it too close (where ``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.
- */
- d = half(line_w - eq_w);
- if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */
- d = half(line_w - eq_w - eqno_w);
- if (p != null)
- if (!is_char_node(p))
- if (type(p) == glue_node)
- d = 0;
- }
-
- tail_append(new_penalty(pre_display_penalty_par));
- if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */
- g1 = above_display_skip_code;
- g2 = below_display_skip_code;
- } else {
- g1 = above_display_short_skip_code;
- g2 = below_display_short_skip_code;
- }
-
- /* 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 (eqno_box && l && (eqno_w == 0)) {
- /* if (math_direction_par==dir_TLT) { */
- shift_amount(eqno_box) = 0;
- /* } else { */
- /* } */
- append_to_vlist(eqno_box,lua_key_index(equation_number));
- tail_append(new_penalty(inf_penalty));
- } else {
- inject_display_skip_before(g1);
- }
-
- if (eqno_w != 0) {
- r = new_kern(line_w - eq_w - eqno_w - d);
- if (l) {
- if (swap_dir) {
- if (math_direction_par==dir_TLT) {
- /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 1\n");
-#endif
- s = new_kern(width(r) + eqno_w);
- try_couple_nodes(eqno_box,r);
- try_couple_nodes(r,eq_box);
- try_couple_nodes(eq_box,s);
- } else {
- /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 2\n");
-#endif
- try_couple_nodes(eqno_box,r);
- try_couple_nodes(r,eq_box);
- }
- } else {
- if (math_direction_par==dir_TLT) {
- /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 3\n");
-#endif
- s = new_kern(width(r) + eqno_w);
- } else {
- /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 4\n");
-#endif
- s = new_kern(width(r));
- }
- try_couple_nodes(eqno_box,r);
- try_couple_nodes(r,eq_box);
- try_couple_nodes(eq_box,s);
- }
- eq_box = eqno_box;
- } else {
- if (swap_dir) {
- if (math_direction_par==dir_TLT) {
- /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 5\n");
-#endif
- } else {
- /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 6\n");
-#endif
- }
- try_couple_nodes(eq_box,r);
- try_couple_nodes(r,eqno_box);
- } else {
- if (math_direction_par==dir_TLT) {
- /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 7\n");
-#endif
- s = new_kern(d);
- } else {
- /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 8\n");
-#endif
- s = new_kern(width(r) + eqno_w);
- }
- try_couple_nodes(s,eq_box);
- try_couple_nodes(eq_box,r);
- try_couple_nodes(r,eqno_box);
- eq_box = s;
- }
- }
- eq_box = hpack(eq_box, 0, additional, -1);
- subtype(eq_box) = equation_list; /* new */
- build_attribute_list(eq_box);
- shift_amount(eq_box) = line_s;
- } else {
- shift_amount(eq_box) = line_s + d;
- }
-/* check for prev: */
- append_to_vlist(eq_box,lua_key_index(equation));
-
- if ((eqno_box != null) && (eqno_w == 0) && !l) {
- tail_append(new_penalty(inf_penalty));
- /* if (math_direction_par==dir_TLT) { */
- shift_amount(eqno_box) = line_s + line_w - eqno_width ;
- /* } else { */
- /* } */
- append_to_vlist(eqno_box,lua_key_index(equation_number));
- g2 = 0;
- }
- if (t != adjust_head) { /* migrating material comes after equation number */
- vlink(tail) = vlink(adjust_head);
- /* needs testing */
- alink(adjust_tail) = alink(tail);
- tail = t;
- }
- if (pre_t != pre_adjust_head) {
- vlink(tail) = vlink(pre_adjust_head);
- /* needs testing */
- alink(pre_adjust_tail) = alink(tail);
- tail = pre_t;
- }
- tail_append(new_penalty(post_display_penalty_par));
- inject_display_skip_after(g2);
- resume_after_display();
-}
-
-@ @c
-void after_math(void)
-{
- int m; /* |mmode| or |-mmode| */
- pointer p; /* the formula */
- pointer a = null; /* box containing equation number */
- boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
- m = mode;
- p = fin_mlist(null); /* this pops the nest */
- if (cur_cmd == math_shift_cs_cmd &&
- (cur_chr == text_style || cur_chr == display_style)) {
- you_cant();
- }
- if (mode == -m) { /* end of equation number */
- if (cur_cmd == math_shift_cmd) {
- check_second_math_shift();
- } else {
- check_display_math_end();
- }
- run_mlist_to_hlist(p, false, text_style);
- a = hpack(vlink(temp_head), 0, additional, -1);
- build_attribute_list(a);
- unsave_math();
- decr(save_ptr); /* now |cur_group=math_shift_group| */
- assert(saved_type(0) == saved_eqno);
- if (saved_value(0) == 1)
- l = true;
- m = mode;
- p = fin_mlist(null);
-
- }
- if (m < 0) {
- /* 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.
- */
- if (cur_cmd == math_shift_cs_cmd) {
- check_inline_math_end();
- }
- tail_append(new_math(math_surround_par, before));
- /* begin mathskip code */
- switch (math_skip_mode) {
- case 0 :
- /* obey mathsurround when zero glue */
- if (! glue_is_zero(math_skip_par)) {
- copy_glue_values(tail,math_skip_par);
- surround(tail) = 0;
- }
- break ;
- case 1 :
- /* always left */
- case 3 :
- /* always both */
- case 6 :
- /* only when skip */
- copy_glue_values(tail,math_skip_par);
- surround(tail) = 0;
- break ;
- case 2 :
- /* only right */
- surround(tail) = 0;
- break ;
- case 4 :
- /* ignore, obey marthsurround */
- break ;
- case 5:
- /* all spacing disabled */
- surround(tail) = 0;
- break ;
- }
- /* end mathskip code */
- if (dir_math_save) {
- tail_append(new_dir(math_direction_par));
- }
- run_mlist_to_hlist(p, (mode > 0), text_style);
- vlink(tail) = vlink(temp_head);
- while (vlink(tail) != null) {
- tail = vlink(tail);
- }
- if (dir_math_save) {
- tail_append(new_dir(math_direction_par - dir_swap));
- }
- dir_math_save = false;
- tail_append(new_math(math_surround_par, after));
- /* begin mathskip code */
- switch (math_skip_mode) {
- case 0 :
- /* obey mathsurround when zero glue */
- if (! glue_is_zero(math_skip_par)) {
- copy_glue_values(tail,math_skip_par);
- surround(tail) = 0;
- }
- break ;
- case 2 :
- /* always right */
- case 3 :
- /* always both */
- case 6 :
- /* only when skip */
- copy_glue_values(tail,math_skip_par);
- surround(tail) = 0;
- break ;
- case 1 :
- /* only left */
- surround(tail) = 0;
- break ;
- case 4 :
- /* ignore, obey marthsurround */
- break ;
- case 5:
- /* all spacing disabled */
- surround(tail) = 0;
- break ;
- }
- /* end mathskip code */
- space_factor_par = 1000;
- unsave_math();
- } else {
- if (a == null) {
- if (cur_cmd == math_shift_cmd) {
- check_second_math_shift();
- } else {
- check_display_math_end();
- }
- }
- run_mlist_to_hlist(p, false, display_style);
- finish_displayed_math(l, a, vlink(temp_head));
- }
-}
-
-@ 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.
-
-@c
-void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
-{
- do_assignments();
- if (cur_cmd == math_shift_cmd) {
- check_second_math_shift();
- } else {
- check_display_math_end();
- }
- pop_nest();
- tail_append(new_penalty(pre_display_penalty_par));
- inject_display_skip_before(above_display_skip_code);
- vlink(tail) = p;
- if (p != null)
- tail = q;
- tail_append(new_penalty(post_display_penalty_par));
- inject_display_skip_after(below_display_skip_code);
- cur_list.prev_depth_field = saved_prevdepth;
- resume_after_display();
-}
-
-@ Interface to \.{\\Umath} and \.{\\mathstyle}
-
-@c
-void setup_math_style(void)
-{
- pointer q;
- tail_append(new_noad());
- q = new_node(math_char_node, 0);
- nucleus(tail) = q;
- (void) scan_math_style(nucleus(tail), num_style(m_style));
-}
-
-@ @c
-void print_math_style(void)
-{
- if (abs(mode) == mmode)
- print_int(m_style);
- else
- print_int(-1);
-}
+% texmath.w +% +% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +@ @c +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/* + + \mathdisplayskipmode + + tex normally always inserts before and only after when larger than zero + + 0 = normal tex + 1 = always + 2 = non-zero + 3 = ignore + +*/ + +@ TODO: not sure if this is the right order +@c +#define back_error(A,B) do { \ + OK_to_interrupt=false; \ + back_input(); \ + OK_to_interrupt=true; \ + tex_error(A,B); \ + } while (0) + +@ @c +int scan_math(pointer, int); +int scan_math_style(pointer, int); +pointer fin_mlist(pointer); + +@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an +{\sl 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 ``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 ``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 ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat +similar ``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|. + +\yskip\hang|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. + +\yskip\hang|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.) + +\yskip\hang|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. + +\yskip\hang|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. + +\yskip\noindent 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 +``italic correction'' added to the width of |P|, but the ``script skip'' +will be added). + +@c +static void unsave_math(void) +{ + unsave(); + decr(save_ptr); + flush_node_list(text_dir_ptr); + assert(saved_type(0) == saved_textdir); + text_dir_ptr = saved_value(0); +} + +@ Sometimes it is necessary to destroy an mlist. The following +subroutine empties the current list, assuming that |abs(mode)=mmode|. + +@c +void flush_math(void) +{ + flush_node_list(vlink(head)); + flush_node_list(incompleat_noad_par); + vlink(head) = null; + tail = head; + incompleat_noad_par = null; +} + +@ Before we can do anything in math mode, we need fonts. + +@c +#define MATHFONTSTACK 8 +#define MATHFONTDEFAULT 0 /* == nullfont */ + +static sa_tree math_fam_head = NULL; + +@ @c +int fam_fnt(int fam_id, int size_id) +{ + int n = fam_id + (256 * size_id); + return (int) get_sa_item(math_fam_head, n).int_value; +} + +void def_fam_fnt(int fam_id, int size_id, int f, int lvl) +{ + int n = fam_id + (256 * size_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = f; + set_sa_item(math_fam_head, n, sa_value, lvl); + fixup_math_parameters(fam_id, size_id, f, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } +} + +@ @c +static void unsave_math_fam_data(int gl) +{ + sa_stack_item st; + if (math_fam_head->stack == NULL) + return; + while (math_fam_head->stack_ptr > 0 && + abs(math_fam_head->stack[math_fam_head->stack_ptr].level) + >= (int) gl) { + st = math_fam_head->stack[math_fam_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_fam_head, st.code, st.value); + /* now do a trace message, if requested */ + if (tracing_restores_par > 1) { + int size_id = st.code / 256; + int fam_id = st.code % 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_fam_head->stack_ptr)--; + } +} + +@ and parameters + +@c +#define MATHPARAMSTACK 8 +#define MATHPARAMDEFAULT undefined_math_parameter + +static sa_tree math_param_head = NULL; + +@ @c +void def_math_param(int param_id, int style_id, scaled value, int lvl) +{ + int n = param_id + (256 * style_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) value; + set_sa_item(math_param_head, n, sa_value, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(value); + print_char('}'); + end_diagnostic(false); + } +} + +scaled get_math_param(int param_id, int style_id) +{ + int n = param_id + (256 * style_id); + return (scaled) get_sa_item(math_param_head, n).int_value; +} + +@ @c +static void unsave_math_param_data(int gl) +{ + sa_stack_item st; + if (math_param_head->stack == NULL) + return; + while (math_param_head->stack_ptr > 0 && + abs(math_param_head->stack[math_param_head->stack_ptr].level) + >= (int) gl) { + st = math_param_head->stack[math_param_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_param_head, st.code, st.value); + /* now do a trace message, if requested */ + if (tracing_restores_par > 1) { + int param_id = st.code % 256; + int style_id = st.code / 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(get_math_param(param_id, style_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_param_head->stack_ptr)--; + } +} + +@ saving and unsaving of both + +@c +void unsave_math_data(int gl) +{ + unsave_math_fam_data(gl); + unsave_math_param_data(gl); +} + +@ Dumping and undumping +@c +void dump_math_data(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + dump_sa_tree(math_fam_head, "mathfonts"); + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + } + dump_sa_tree(math_param_head, "mathparameters"); +} + +void undump_math_data(void) +{ + math_fam_head = undump_sa_tree("mathfonts"); + math_param_head = undump_sa_tree("mathparameters"); +} + +@ @c +void initialize_math(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + initialize_math_spacing(); + } + return; +} + +@ 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 ``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 ``small'' and ``large'' +starting characters, as explained in Chapter~17 of {\sl The \TeX book}. +@:TeXbook}{\sl 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 |default_code| is used to stand for the +|default_rule_thickness| of the current size. The |numerator| and +|denominator| point to mlists that define a fraction; we always have +$$\hbox{|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 + +@c +pointer new_noad(void) +{ + pointer p; + p = new_node(simple_noad, ord_noad_type); + /* all noad fields are zero after this */ + return p; +} + +@ @c +pointer new_sub_box(pointer curbox) +{ + pointer p, q; + p = new_noad(); + q = new_node(sub_box_node, 0); + nucleus(p) = q; + math_list(nucleus(p)) = curbox; + return p; +} + +@ 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 +``cramped'' versions of these styles. This gives a numerical order that +is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; +i.e., a smaller style has a larger numerical value. +@:TeXbook}{\sl The \TeX book@> + +@c +const char *math_style_names[] = { + "display", "crampeddisplay", + "text", "crampedtext", + "script", "crampedscript", + "scriptscript", "crampedscriptscript", + NULL +}; + +const char *math_param_names[] = { + "quad", "axis", "operatorsize", + "overbarkern", "overbarrule", "overbarvgap", + "underbarkern", "underbarrule", "underbarvgap", + "radicalkern", "radicalrule", "radicalvgap", + "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", + "stackvgap", "stacknumup", "stackdenomdown", + "fractionrule", "fractionnumvgap", "fractionnumup", + "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", + "limitabovevgap", "limitabovebgap", "limitabovekern", + "limitbelowvgap", "limitbelowbgap", "limitbelowkern", + "nolimitsubfactor", "nolimitsupfactor", /* bonus */ + "underdelimitervgap", "underdelimiterbgap", + "overdelimitervgap", "overdelimiterbgap", + "subshiftdrop", "supshiftdrop", "subshiftdown", + "subsupshiftdown", "subtopmax", "supshiftup", + "supbottommin", "supsubbottommax", "subsupvgap", + "spaceafterscript", "connectoroverlapmin", + "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", + "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", + "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", + "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", + "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", + "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", + "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", + "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", + "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", + "openopenspacing", "openclosespacing", "openpunctspacing", + "openinnerspacing", + "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", + "closeopenspacing", "closeclosespacing", "closepunctspacing", + "closeinnerspacing", + "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", + "punctopenspacing", "punctclosespacing", "punctpunctspacing", + "punctinnerspacing", + "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", + "inneropenspacing", "innerclosespacing", "innerpunctspacing", + "innerinnerspacing", + NULL +}; + +@ @c +pointer new_style(small_number s) +{ /* create a style node */ + m_style = s; + return new_node(style_node, s); +} + +@ 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. + +@c +static pointer new_choice(void) +{ /* create a choice node */ + return new_node(choice_node, 0); /* the |subtype| is not used */ +} + +@ 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 ``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\}\}\}\}\$}'. + +@c +void display_normal_noad(pointer p); /* forward */ +void display_fence_noad(pointer p); /* forward */ +void display_fraction_noad(pointer p); /* forward */ + +void show_math_node(pointer p) +{ + switch (type(p)) { + case style_node: + print_cmd_chr(math_style_cmd, subtype(p)); + break; + case choice_node: + tprint_esc("mathchoice"); + append_char('D'); + show_node_list(display_mlist(p)); + flush_char(); + append_char('T'); + show_node_list(text_mlist(p)); + flush_char(); + append_char('S'); + show_node_list(script_mlist(p)); + flush_char(); + append_char('s'); + show_node_list(script_script_mlist(p)); + flush_char(); + break; + case simple_noad: + case radical_noad: + case accent_noad: + display_normal_noad(p); + break; + case fence_noad: + display_fence_noad(p); + break; + case fraction_noad: + display_fraction_noad(p); + break; + default: + tprint("Unknown node type!"); + break; + } +} + +@ Here are some simple routines used in the display of noads. + +@c +static void print_fam_and_char(pointer p) +{ /* prints family and character */ + tprint_esc("fam"); + print_int(math_fam(p)); + print_char(' '); + print(math_character(p)); +} + +@ @c +static void print_delimiter(pointer p) +{ + int a; + if (delimiteroptionset(p)) { + tprint(" [ "); + if (delimiteraxis(p)) + tprint("axis "); + if (delimiternoaxis(p)) + tprint("noaxis "); + if (delimiterexact(p)) + tprint("exact "); + tprint("]"); + } + if (delimiterheight(p)) { + tprint("height="); + print_scaled(delimiterheight(p)); + tprint(" "); + } + if (delimiterdepth(p)) { + tprint("depth="); + print_scaled(delimiterdepth(p)); + tprint(" "); + } + if (delimiterclass(p)) { + tprint("class="); + print_int(delimiterclass(p)); + tprint(" "); + } + if (small_fam(p) < 0) { + print_int(-1); /* this should never happen */ + } else if (small_fam(p) < 16 && large_fam(p) < 16 && + small_char(p) < 256 && large_char(p) < 256) { + /* traditional tex style */ + a = small_fam(p) * 256 + small_char(p); + a = a * 0x1000 + large_fam(p) * 256 + large_char(p); + print_hex(a); + } else if ((large_fam(p) == 0 && large_char(p) == 0) || + small_char(p) > 65535 || large_char(p) > 65535) { + /* modern xetex/luatex style */ + print_hex(small_fam(p)); + print_hex(small_char(p)); + } +} + +@ 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). +@^recursion@> + +@c +static void print_subsidiary_data(pointer p, ASCII_code c) +{ /* display a noad field */ + if ((int) cur_length >= depth_threshold) { + if (p != null) + tprint(" []"); + } else { + append_char(c); /* include |c| in the recursion history */ + if (p != null) { + switch (type(p)) { + case math_char_node: + print_ln(); + print_current_string(); + print_fam_and_char(p); + break; + case sub_box_node: + show_node_list(math_list(p)); + break; + case sub_mlist_node: + if (math_list(p) == null) { + print_ln(); + print_current_string(); + tprint("{}"); + } else { + show_node_list(math_list(p)); + } + break; + } + } + flush_char(); /* remove |c| from the recursion history */ + } +} + +@ @c +void display_normal_noad(pointer p) +{ + switch (type(p)) { + case simple_noad: + switch (subtype(p)) { + case ord_noad_type: + tprint_esc("mathord"); + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + tprint_esc("mathop"); + if (subtype(p) == op_noad_type_limits) + tprint_esc("limits"); + else if (subtype(p) == op_noad_type_no_limits) + tprint_esc("nolimits"); + break; + case bin_noad_type: + tprint_esc("mathbin"); + break; + case rel_noad_type: + tprint_esc("mathrel"); + break; + case open_noad_type: + tprint_esc("mathopen"); + break; + case close_noad_type: + tprint_esc("mathclose"); + break; + case punct_noad_type: + tprint_esc("mathpunct"); + break; + case inner_noad_type: + tprint_esc("mathinner"); + break; + case over_noad_type: + tprint_esc("overline"); + break; + case under_noad_type: + tprint_esc("underline"); + break; + case vcenter_noad_type: + tprint_esc("vcenter"); + break; + default: + tprint("<unknown noad type!>"); + break; + } + break; + case radical_noad: + if (subtype(p) == 6) + tprint_esc("Udelimiterover"); + else if (subtype(p) == 5) + tprint_esc("Udelimiterunder"); + else if (subtype(p) == 4) + tprint_esc("Uoverdelimiter"); + else if (subtype(p) == 3) + tprint_esc("Uunderdelimiter"); + else if (subtype(p) == 2) + tprint_esc("Uroot"); + else + tprint_esc("radical"); + print_delimiter(left_delimiter(p)); + if (degree(p) != null) { + print_subsidiary_data(degree(p), '/'); + } + if (radicalwidth(p)) { + tprint("width="); + print_scaled(radicalwidth(p)); + tprint(" "); + } + if (radicaloptionset(p)) { + tprint(" [ "); + if (radicalexact(p)) + tprint("exact "); + if (radicalleft(p)) + tprint("left "); + if (radicalmiddle(p)) + tprint("middle "); + if (radicalright(p)) + tprint("right "); + tprint("]"); + } + break; + case accent_noad: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent both"); + } else { + tprint_esc("Umathaccent"); + } + } else if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent bottom"); + } else { + tprint_esc("Umathaccent overlay"); + } + if (accentfraction(p)) { + tprint(" fraction="); + print_int(accentfraction(p)); + tprint(" "); + } + switch (subtype(p)) { + case 0: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(top_accent_chr(p)); + } + } else if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(overlay_accent_chr(p)); + } + break; + case 1: + if (top_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } + } else { + confusion("display_accent_noad"); + } + break; + case 2: + if (bot_accent_chr(p) != null) { + if (top_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + } + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else{ + confusion("display_accent_noad"); + } + break; + case 3: + if (top_accent_chr(p) != null && bot_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else { + confusion("display_accent_noad"); + } + break; + } + break; + } + print_subsidiary_data(nucleus(p), '.'); + print_subsidiary_data(supscr(p), '^'); + print_subsidiary_data(subscr(p), '_'); +} + +@ @c +void display_fence_noad(pointer p) +{ + if (subtype(p) == right_noad_side) + tprint_esc("right"); + else if (subtype(p) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + print_delimiter(delimiter(p)); +} + +@ @c +void display_fraction_noad(pointer p) +{ + tprint_esc("fraction, thickness "); + if (thickness(p) == default_code) + tprint("= default"); + else + print_scaled(thickness(p)); + if ((left_delimiter(p) != null) && + ((small_fam(left_delimiter(p)) != 0) || + (small_char(left_delimiter(p)) != 0) || + (large_fam(left_delimiter(p)) != 0) || + (large_char(left_delimiter(p)) != 0))) { + tprint(", left-delimiter "); + print_delimiter(left_delimiter(p)); + } + if ((right_delimiter(p) != null) && + ((small_fam(right_delimiter(p)) != 0) || + (small_char(right_delimiter(p)) != 0) || + (large_fam(right_delimiter(p)) != 0) || + (large_char(right_delimiter(p)) != 0))) { + tprint(", right-delimiter "); + print_delimiter(right_delimiter(p)); + } + print_subsidiary_data(numerator(p), '\\'); + print_subsidiary_data(denominator(p), '/'); +} + +@ 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 ``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|. + +@c +static void new_save_level_math(group_code c) +{ + set_saved_record(0, saved_textdir, 0, text_dir_ptr); + text_dir_ptr = new_dir(math_direction_par); + incr(save_ptr); + new_save_level(c); + eq_word_define(int_base + body_direction_code, math_direction_par); + eq_word_define(int_base + par_direction_code, math_direction_par); + eq_word_define(int_base + text_direction_code, math_direction_par); +} + +@ @c +static void push_math(group_code c, int mstyle) +{ + if (math_direction_par != text_direction_par) + dir_math_save = true; + push_nest(); + mode = -mmode; + incompleat_noad_par = null; + m_style = mstyle; + new_save_level_math(c); +} + +@ @c +static void enter_ordinary_math(void) +{ + push_math(math_shift_group, text_style); + eq_word_define(int_base + cur_fam_code, -1); + if (every_math_par != null) + begin_token_list(every_math_par, every_math_text); +} + +@ @c +void enter_display_math(void); + +@ 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. + +@c +void init_math(void) +{ + if (cur_cmd == math_shift_cmd) { + get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + if ((cur_cmd == math_shift_cmd) && (mode > 0)) { + enter_display_math(); + } else { + back_input(); + enter_ordinary_math(); + } + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { + enter_display_math(); + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { + enter_ordinary_math(); + } else { + you_cant(); + } +} + +@ 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. + +@c +void start_eq_no(void) +{ + set_saved_record(0, saved_eqno, 0, cur_chr); + incr(save_ptr); + enter_ordinary_math(); +} + +@ 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. + +@c +void math_left_brace(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + back_input(); + (void) scan_math(nucleus(tail), m_style); +} + +@ 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. + +@c +static boolean math_and_text_reversed_p(void) +{ + int i = save_ptr - 1; + while (save_type(i) != level_boundary) + i--; + while (i < save_ptr) { + if (save_type(i) == restore_old_value && + save_value(i) == int_base + par_direction_code) { + if (textdir_opposite(math_direction_par, save_value(i - 1))) + return true; + } + i++; + } + return false; +} + +@ 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|. + +@c +void enter_display_math(void) +{ + scaled w; /* new or partial |pre_display_size| */ + scaled l; /* new |display_width| */ + scaled s; /* new |display_indent| */ + pointer p; + int n; /* scope of paragraph shape specification */ + if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ + (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ + type(tail) == local_par_node && vlink(tail) == null)) { + if (vlink(head) == tail) { + /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if + there is another display immediately following, we have to get rid + of that node */ + flush_node(tail); + } + pop_nest(); + w = -max_dimen; + } else { + line_break(true, math_shift_group); + w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par); + } + /* 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_ptr == null) { + if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { + halfword used_hang_indent = swap_hang_indent(hang_indent_par); + l = hsize_par - abs(used_hang_indent); + if (used_hang_indent > 0) + s = used_hang_indent; + else + s = 0; + } else { + l = hsize_par; + s = 0; + } + } else { + n = vinfo(par_shape_par_ptr + 1); + if (prev_graf_par + 2 >= n) + p = par_shape_par_ptr + 2 * n + 1; + else + p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1; + s = varmem[(p - 1)].cint; + l = varmem[p].cint; + s = swap_parshape_indent(s,l); + } + + push_math(math_shift_group, display_style); + mode = mmode; + eq_word_define(int_base + cur_fam_code, -1); + eq_word_define(dimen_base + pre_display_size_code, w); + eq_word_define(dimen_base + display_width_code, l); + eq_word_define(dimen_base + display_indent_code, s); + eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); + if (every_display_par != null) + begin_token_list(every_display_par, every_display_text); + if (nest_ptr == 1) { + checked_page_filter(before_display); + build_page(); + } +} + +@ The next routine parses all variations of a delimiter code. The |extcode| + tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the + |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). + (the class is passed on for conversion to \.{\\mathchar}). + +@c +static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) +{ + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + delcodeval d; + int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; + if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ + scan_int(); + /* "MFCCFCC or "FCCFCC */ + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); + } + if (cur_val > 0xFFFFFF) { + tex_error("Invalid delimiter code", hlp); + cur_val = 0; + } + msfam = (cur_val / 0x100000); + mschr = (cur_val % 0x100000) / 0x1000; + mlfam = (cur_val & 0xFFF) / 0x100; + mlchr = (cur_val % 0x100); + } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */ + /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ + if (doclass) { + scan_int(); + mcls = cur_val; + } + scan_int(); + msfam = cur_val; + scan_char_num(); + mschr = cur_val; + if (msfam < 0 || msfam > 255) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else if (extcode == 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) { /* such a primitive doesn't exist */ + confusion("umathnum_mathcode"); + } + scan_int(); + msfam = (cur_val / 0x200000); + mschr = cur_val & 0x1FFFFF; + if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else { + /* something's gone wrong */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.small_family_value = msfam; + d.small_character_value = mschr; + d.large_family_value = mlfam; + d.large_character_value = mlchr; + return d; +} + +@ @c +void scan_extdef_del_code(int level, int extcode) +{ + delcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = do_scan_extdef_del_code(extcode, false); + set_del_code(p, d.small_family_value, d.small_character_value, + d.large_family_value, d.large_character_value, + (quarterword) (level)); +} + +@ @c +mathcodeval scan_mathchar(int extcode) +{ + char errstr[255] = { 0 }; + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + mathcodeval d; + int mcls = 0, mfam = 0, mchr = 0; + if (extcode == tex_mathcode) { /* \.{\\mathcode} */ + /* "TFCC */ + scan_int(); + if (cur_val > 0x8000) { + /* + tex_error("Invalid math code", hlp); + cur_val = 0; + */ + /* needed for latex: fallback to umathnum_mathcode */ + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + if (cur_val < 0) { + snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); + tex_error(errstr, hlp); + cur_val = 0; + } + mcls = (cur_val / 0x1000); + mfam = ((cur_val % 0x1000) / 0x100); + mchr = (cur_val % 0x100); + } + } else if (extcode == umath_mathcode) { + /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ + scan_int(); + mcls = cur_val; + scan_int(); + mfam = cur_val; + scan_char_num(); + mchr = cur_val; + if (mcls < 0 || mcls > 7 || mfam > 255) { + tex_error("Invalid math code", hlp); + mchr = 0; + mfam = 0; + mcls = 0; + } + } else if (extcode == umathnum_mathcode) { + /* "FFT<21bits> */ + /* 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 */ + scan_int(); + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + /* something's gone wrong */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.family_value = mfam; + d.character_value = mchr; + return d; +} + +@ @c +void scan_extdef_math_code(int level, int extcode) +{ + mathcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = scan_mathchar(extcode); + set_math_code(p, d.class_value, + d.family_value, d.character_value, (quarterword) (level)); +} + +@ this reads in a delcode when actually a mathcode is needed +@c +mathcodeval scan_delimiter_as_mathchar(int extcode) +{ + delcodeval dval; + mathcodeval mval; + dval = do_scan_extdef_del_code(extcode, true); + mval.class_value = dval.class_value; + mval.family_value = dval.small_family_value; + mval.character_value = dval.small_character_value; + return mval; +} + +@ 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|. + +@c +#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) + +int scan_math_style(pointer p, int mstyle) +{ + get_next_nb_nr(); + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; +} + +int scan_math(pointer p, int mstyle) +{ + /* label restart,reswitch,exit; */ + mathcodeval mval = { 0, 0, 0 }; + assert(p != null); + RESTART: + get_next_nb_nr(); + RESWITCH: + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + case char_given_cmd: + mval = get_math_code(cur_chr); + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + goto RESTART; + } + break; + case char_num_cmd: + scan_char_num(); + cur_chr = cur_val; + cur_cmd = char_given_cmd; + goto RESWITCH; + break; + case math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(umathnum_mathcode); + else + confusion("scan_math"); + break; + case math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + break; + case xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, umath_mathcode); + break; + case delim_num_cmd: + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_delimiter_as_mathchar(umath_mathcode); + else + confusion("scan_math"); + break; + default: + /* The pointer |p| is placed on |save_stack| while a complex subformula + is being scanned. */ + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; + } + type(p) = math_char_node; + math_character(p) = mval.character_value; + if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(p) = cur_fam_par; + else + math_fam(p) = mval.family_value; + return 0; +} + +@ The |set_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. + +@c +void set_math_char(mathcodeval mval) +{ + pointer p; /* the new noad */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + pointer q; + p = new_noad(); + q = new_node(math_char_node, 0); + nucleus(p) = q; + math_character(nucleus(p)) = mval.character_value; + math_fam(nucleus(p)) = mval.family_value; + if (mval.class_value == math_use_current_family_code) { + if (cur_fam_par_in_range) + math_fam(nucleus(p)) = cur_fam_par; + subtype(p) = ord_noad_type; + } else { + switch (mval.class_value) { + /* *INDENT-OFF* */ + case 0: subtype(p) = ord_noad_type; break; + case 1: subtype(p) = op_noad_type_normal; break; + case 2: subtype(p) = bin_noad_type; break; + case 3: subtype(p) = rel_noad_type; break; + case 4: subtype(p) = open_noad_type; break; + case 5: subtype(p) = close_noad_type; break; + case 6: subtype(p) = punct_noad_type; break; + /* *INDENT-ON* */ + } + } + vlink(tail) = p; + tail = p; + } +} + +@ The |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. + +@c +void math_char_in_text(mathcodeval mval) +{ + pointer p; /* the new node */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value); + vlink(tail) = p; + tail = p; + } +} + +@ @c +void math_math_comp(void) +{ + pointer q; + tail_append(new_noad()); + subtype(tail) = (quarterword) cur_chr; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + if (cur_chr == over_noad_type) + (void) scan_math(nucleus(tail), cramped_style(m_style)); + else + (void) scan_math(nucleus(tail), m_style); +} + +@ @c +void math_limit_switch(void) +{ + const char *hlp[] = { + "I'm ignoring this misplaced \\limits or \\nolimits command.", + NULL + }; + if (head != tail) { + if (type(tail) == simple_noad && + (subtype(tail) == op_noad_type_normal || + subtype(tail) == op_noad_type_limits || + subtype(tail) == op_noad_type_no_limits)) { + subtype(tail) = (quarterword) cur_chr; + return; + } + } + tex_error("Limit controls must follow a math operator", hlp); +} + +@ 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. + +@c +static void scan_delimiter(pointer p, int r) +{ + delcodeval dval = { 0, 0, 0, 0, 0 }; + if (r == tex_mathcode) { /* \.{\\radical} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + } else if (r == umath_mathcode) { /* \.{\\Uradical} */ + dval = do_scan_extdef_del_code(umath_mathcode, false); + } else if (r == no_mathcode) { + get_next_nb_nr(); + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + dval = get_del_code(cur_chr); + break; + case delim_num_cmd: + if (cur_chr == 0) /* \.{\\delimiter} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + else if (cur_chr == 1) /* \.{\\Udelimiter} */ + dval = do_scan_extdef_del_code(umath_mathcode, true); + else + confusion("scan_delimiter1"); + break; + default: + dval.small_family_value = -1; + break; + } + } else { + confusion("scan_delimiter2"); + } + if (p == null) + return; + if (dval.small_family_value < 0) { + const char *hlp[] = { + "I was expecting to see something like `(' or `\\{' or", + "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", + "should probably delete the `{' by typing `1' now, so that", + "braces don't get unbalanced. Otherwise just proceed", + "Acceptable delimiters are characters whose \\delcode is", + "nonnegative, or you can use `\\delimiter <delimiter code>'.", + NULL + }; + back_error("Missing delimiter (. inserted)", hlp); + small_fam(p) = 0; + small_char(p) = 0; + large_fam(p) = 0; + large_char(p) = 0; + } else { + small_fam(p) = dval.small_family_value; + small_char(p) = dval.small_character_value; + large_fam(p) = dval.large_family_value; + large_char(p) = dval.large_character_value; + } + return; +} + +@ @c +void math_radical(void) +{ + halfword q; + int chr_code = cur_chr; + halfword options = 0; + tail_append(new_node(radical_noad, chr_code)); + q = new_node(delim_node, 0); + left_delimiter(tail) = q; + while (1) { + if (scan_keyword("width")) { + scan_dimen(false,false,false); + radicalwidth(tail) = cur_val ; + } else if (scan_keyword("left")) { + options = options | noad_option_left ; + } else if (scan_keyword("middle")) { + options = options | noad_option_middle ; + } else if (scan_keyword("right")) { + options = options | noad_option_right ; + } else { + break; + } + } + radicaloptions(tail) = options; + if (chr_code == 0) /* \.{\\radical} */ + scan_delimiter(left_delimiter(tail), tex_mathcode); + else if (chr_code == 1) /* \.{\\Uradical} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 2) /* \.{\\Uroot} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 5) /* \.{\\Udelimiterunder} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 6) /* \.{\\Udelimiterover} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 7) /* \.{\\Uhextensible} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else + confusion("math_radical"); + if (chr_code == 7) { + q = new_node(sub_box_node, 0); /* type will change */ + nucleus(tail) = q; + return; + } else if (chr_code == 2) { + /* the trick with the |vlink(q)| is used by |scan_math| + to decide whether it needs to go on */ + q = new_node(math_char_node, 0); + vlink(q) = tail; + degree(tail) = q; + if (!scan_math(degree(tail), sup_sup_style(m_style))) { + vlink(degree(tail)) = null; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } + } else { + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } +} + +@ @c +void math_ac(void) +{ + halfword q; + mathcodeval t = { 0, 0, 0 }; + mathcodeval b = { 0, 0, 0 }; + mathcodeval o = { 0, 0, 0 }; + if (cur_cmd == accent_cmd) { + const char *hlp[] = { + "I'm changing \\accent to \\mathaccent here; wish me luck.", + "(Accents are not the same in formulas as they are in text.)", + NULL + }; + tex_error("Please use \\mathaccent for accents in math mode", hlp); + } + tail_append(new_node(accent_noad, 0)); + if (cur_chr == 0) { /* \.{\\mathaccent} */ + t = scan_mathchar(tex_mathcode); + } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ + if (scan_keyword("fixed")) { + /* top */ + subtype(tail) = 1; + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("both")) { + /* top bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + if (scan_keyword("fixed")) { + subtype(tail) += 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("bottom")) { + /* bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("top")) { + /* top */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("overlay")) { + /* overlay */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + o = scan_mathchar(umath_mathcode); + } else { + /* top */ + t = scan_mathchar(umath_mathcode); + } + if (scan_keyword("fraction")) { + scan_int(); + accentfraction(tail) = cur_val; + } + } else { + confusion("mathaccent"); + } + if (!(t.character_value == 0 && t.family_value == 0)) { + q = new_node(math_char_node, 0); + top_accent_chr(tail) = q; + math_character(top_accent_chr(tail)) = t.character_value; + if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(top_accent_chr(tail)) = cur_fam_par; + else + math_fam(top_accent_chr(tail)) = t.family_value; + } + if (!(b.character_value == 0 && b.family_value == 0)) { + q = new_node(math_char_node, 0); + bot_accent_chr(tail) = q; + math_character(bot_accent_chr(tail)) = b.character_value; + if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(bot_accent_chr(tail)) = cur_fam_par; + else + math_fam(bot_accent_chr(tail)) = b.family_value; + } + if (!(o.character_value == 0 && o.family_value == 0)) { + q = new_node(math_char_node, 0); + overlay_accent_chr(tail) = q; + math_character(overlay_accent_chr(tail)) = o.character_value; + if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(overlay_accent_chr(tail)) = cur_fam_par; + else + math_fam(overlay_accent_chr(tail)) = o.family_value; + } + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); +} + +@ @c +pointer math_vcenter_group(pointer p) +{ + pointer q, r; + q = new_noad(); + subtype(q) = vcenter_noad_type; + r = new_node(sub_box_node, 0); + nucleus(q) = r; + math_list(nucleus(q)) = p; + return q; +} + +@ The routine that scans the four mlists of a \.{\\mathchoice} is very +much like the routine that builds discretionary nodes. + +@c +void append_choices(void) +{ + tail_append(new_choice()); + incr(save_ptr); + set_saved_record(-1, saved_choices, 0, 0); + push_math(math_choice_group, display_style); + scan_left_brace(); +} + +@ @c +void build_choices(void) +{ + pointer p; /* the current mlist */ + int prev_style; + prev_style = m_style; + unsave_math(); + p = fin_mlist(null); + assert(saved_type(-1) == saved_choices); + switch (saved_value(-1)) { + case 0: + display_mlist(tail) = p; + break; + case 1: + text_mlist(tail) = p; + break; + case 2: + script_mlist(tail) = p; + break; + case 3: + script_script_mlist(tail) = p; + decr(save_ptr); + return; + break; + } /* there are no other cases */ + set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); + push_math(math_choice_group, (prev_style + 2)); + scan_left_brace(); +} + +@ Subscripts and superscripts are attached to the previous nucleus by the +action procedure called |sub_sup|. + +@c +void sub_sup(void) +{ + pointer q; + if (tail == head || (!scripts_allowed(tail))) { + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + } + if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ + if (supscr(tail) != null) { + const char *hlp[] = { + "I treat `x^1^2' essentially like `x^1{}^2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double superscript", hlp); + } + q = new_node(math_char_node, 0); + supscr(tail) = q; + (void) scan_math(supscr(tail), sup_style(m_style)); + } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { + if (subscr(tail) != null) { + const char *hlp[] = { + "I treat `x_1_2' essentially like `x_1{}_2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double subscript", hlp); + } + q = new_node(math_char_node, 0); + subscr(tail) = q; + (void) scan_math(subscr(tail), sub_style(m_style)); + } +} + +@ An operation like `\.{\\over}' causes the current mlist to go into a +state of suspended animation: |incompleat_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 incompleat fraction noad, and that noad will become the +whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' +delimiters. + +@c +void math_fraction(void) +{ + halfword c; /* the type of generalized fraction we are scanning */ + pointer q; + halfword options = 0; + halfword temp_value; + c = cur_chr; + if (incompleat_noad_par != null) { + const char *hlp[] = { + "I'm ignoring this fraction specification, since I don't", + "know whether a construction like `x \\over y \\over z'", + "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", + NULL + }; + if (c >= delimited_code) { + scan_delimiter(null, no_mathcode); + scan_delimiter(null, no_mathcode); + } + if ((c % delimited_code) == above_code) + scan_normal_dimen(); + tex_error("Ambiguous; you need another { and }", hlp); + } else { + incompleat_noad_par = new_node(fraction_noad, 0); + temp_value = new_node(sub_mlist_node, 0); + numerator(incompleat_noad_par) = temp_value; + math_list(numerator(incompleat_noad_par)) = vlink(head); + vlink(head) = null; + tail = head; + m_style = cramped_style(m_style); + + if ((c % delimited_code) == skewed_code) { + q = new_node(delim_node, 0); + middle_delimiter(incompleat_noad_par) = q; + scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode); + } + if (c >= delimited_code) { + q = new_node(delim_node, 0); + left_delimiter(incompleat_noad_par) = q; + q = new_node(delim_node, 0); + right_delimiter(incompleat_noad_par) = q; + scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode); + scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode); + } + switch (c % delimited_code) { + case above_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + scan_normal_dimen(); + thickness(incompleat_noad_par) = cur_val; + break; + case over_code: + thickness(incompleat_noad_par) = default_code; + break; + case atop_code: + thickness(incompleat_noad_par) = 0; + break; + case skewed_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + thickness(incompleat_noad_par) = 0; + break; + } + } +} + +@ At the end of a math formula or subformula, the |fin_mlist| routine is +called upon to return a pointer to the newly completed mlist, and to +pop the nest back to the enclosing semantic level. The parameter to +|fin_mlist|, if not null, points to a |fence_noad| that ends the +current mlist; this |fence_noad| has not yet been appended. + +@c +pointer fin_mlist(pointer p) +{ + pointer q; /* the mlist to return */ + if (incompleat_noad_par != null) { + if (denominator(incompleat_noad_par) != null) { + type(denominator(incompleat_noad_par)) = sub_mlist_node; + } else { + q = new_node(sub_mlist_node, 0); + denominator(incompleat_noad_par) = q; + } + math_list(denominator(incompleat_noad_par)) = vlink(head); + if (p == null) { + q = incompleat_noad_par; + } else { + q = math_list(numerator(incompleat_noad_par)); + if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) + || (delim_par == null)) + confusion("right"); /* this can't happen */ + math_list(numerator(incompleat_noad_par)) = vlink(delim_par); + vlink(delim_par) = incompleat_noad_par; + vlink(incompleat_noad_par) = p; + } + } else { + vlink(tail) = p; + q = vlink(head); + } + pop_nest(); + return q; +} + +@ 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/superscripts, or when they +surround an accent that is the nucleus of an Ord atom. + +@c +void close_math_group(pointer p) +{ + int old_style = m_style; + unsave_math(); + + decr(save_ptr); + assert(saved_type(0) == saved_math); + type(saved_value(0)) = sub_mlist_node; + p = fin_mlist(null); + math_list(saved_value(0)) = p; + if (p != null) { + if (vlink(p) == null) { + if (type(p) == simple_noad && subtype(p) == ord_noad_type) { + if (subscr(p) == null && supscr(p) == null) { + type(saved_value(0)) = type(nucleus(p)); + if (type(nucleus(p)) == math_char_node) { + math_fam(saved_value(0)) = math_fam(nucleus(p)); + math_character(saved_value(0)) = + math_character(nucleus(p)); + } else { + math_list(saved_value(0)) = math_list(nucleus(p)); + math_list(nucleus(p)) = null; + } + delete_attribute_ref(node_attr(saved_value(0))); + node_attr(saved_value(0)) = node_attr(nucleus(p)); + node_attr(nucleus(p)) = null; + flush_node(p); + } + } else if (type(p) == accent_noad) { + if (saved_value(0) == nucleus(tail)) { + if (type(tail) == simple_noad + && subtype(tail) == ord_noad_type) { + pointer q = head; + while (vlink(q) != tail) + q = vlink(q); + vlink(q) = p; + nucleus(tail) = null; + subscr(tail) = null; + supscr(tail) = null; + delete_attribute_ref(node_attr(p)); + node_attr(p) = node_attr(tail); + node_attr(tail) = null; + flush_node(tail); + tail = p; + } + } + } + } + } + if (vlink(saved_value(0)) > 0) { + pointer q; + q = new_node(math_char_node, 0); + nucleus(vlink(saved_value(0))) = q; + vlink(saved_value(0)) = null; + saved_value(0) = q; + (void) scan_math(saved_value(0), old_style); + /* restart */ + } +} + +@ 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 ore several \.{\\middle} +delimiters to appear between \.{\\left} and \.{\\right}. + +@c +void math_left_right(void) +{ + halfword t; /* |left_noad_side| .. |right_noad_side| */ + pointer p; /* new noad */ + pointer q; /* resulting mlist */ + pointer r; /* temporary */ + halfword ht = 0; + halfword dp = 0; + halfword options = 0; + halfword type = -1 ; + t = cur_chr; + + if (t > 10) { + /* we have \Uleft \Uright \Umiddle */ + t = t - 10; + while (1) { + if (scan_keyword("height")) { + scan_dimen(false,false,false); + ht = cur_val ; + } else if (scan_keyword("depth")) { + scan_dimen(false,false,false); + dp = cur_val ; + } else if (scan_keyword("axis")) { + options = options | noad_option_axis ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("class")) { + scan_int(); + type = cur_val ; + } else { + break; + } + } + } + + if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { + if (cur_group == math_shift_group) { + scan_delimiter(null, no_mathcode); + if (t == middle_noad_side) { + const char *hlp[] = { + "I'm ignoring a \\middle that had no matching \\left.", + NULL + }; + tex_error("Extra \\middle", hlp); + } else { + const char *hlp[] = { + "I'm ignoring a \\right that had no matching \\left.", + NULL + }; + tex_error("Extra \\right", hlp); + } + } else { + off_save(); + } + } else { + p = new_noad(); + type(p) = fence_noad; + subtype(p) = (quarterword) t; + r = new_node(delim_node, 0); + delimiter(p) = r; + + delimiterheight(p) = ht; + delimiterdepth(p) = dp; + delimiteroptions(p) = options; + delimiterclass(p) = type; + delimiteritalic(p) = 0; + + scan_delimiter(delimiter(p), no_mathcode); + + if (t == no_noad_side) { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = p; + return ; + } + + if (t == left_noad_side) { + q = p; + } else { + q = fin_mlist(p); + unsave_math(); + } + if (t != right_noad_side) { + push_math(math_left_group, m_style); + vlink(head) = q; + tail = p; + delim_par = p; + } else { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = q; + } + } +} + +@ \TeX\ gets to the following part of the program when +the first `\.\$' ending a display has been scanned. + +@c +static void check_second_math_shift(void) +{ + get_x_token(); + if (cur_cmd != math_shift_cmd) { + const char *hlp[] = { + "The `$' that I just saw supposedly matches a previous `$$'.", + "So I shall assume that you typed `$$' both times.", + NULL + }; + back_error("Display math should end with $$", hlp); + } +} + +static void check_display_math_end(void) +{ + if (cur_chr != cramped_display_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Display math should end with \\Ustopdisplaymath", hlp); + } +} + +static void check_inline_math_end(void) +{ + if (cur_chr != cramped_text_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Inline math should end with \\Ustopmath", hlp); + } +} + +@ @c +static void resume_after_display(void) +{ + if (cur_group != math_shift_group) + confusion("display"); + unsave_math(); + prev_graf_par = prev_graf_par + 3; + push_nest(); + mode = hmode; + space_factor_par = 1000; + /* this needs to be intercepted in the display math start ! */ + tail_append(make_local_par_node(penalty_par_code)); + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + if (nest_ptr == 1) { + normal_page_filter(after_display); + build_page(); + } +} + +@ The fussiest 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). + + |p| points to the mlist for the formula. + |a| is either |null| or it points to a box containing the equation number. + |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). + +@c +#define inject_display_skip_before(g) \ + switch (display_skip_mode_par) { \ + case 0 : /* normal tex */ \ + tail_append(new_param_glue(g)); \ + break;\ + case 1 : /* always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : /* non-zero */ \ + if (g != 0 && ! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: /* ignore */ \ + break; \ + } + +#define inject_display_skip_after(g) \ + switch (display_skip_mode_par) { \ + case 0 : /* normal tex */ \ + if (g != 0 && glue_is_positive(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 1 : /* always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : /* non-zero */ \ + if (g != 0 && ! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: /* ignore */ \ + break; \ + } + +static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) +{ + pointer eq_box; /* box containing the equation */ + scaled eq_w; /* width of the equation */ + scaled line_w; /* width of the line */ + scaled eqno_w; /* width of equation number */ + scaled eqno_w2; /* width of equation number plus space to separate from equation */ + scaled line_s; /* move the line right this much */ + scaled d; /* displacement of equation in the line */ + small_number g1, g2; /* glue parameter codes for before and after */ + pointer r,s; /* kern nodes used to position the display */ + pointer t; /* tail of adjustment list */ + pointer pre_t; /* tail of pre-adjustment list */ + boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ + scaled eqno_width; + swap_dir = (pre_display_direction_par < 0 ? true : false ); + if (eqno_box != null && swap_dir) + l = !l; + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + eq_box = hpack(p, 0, additional, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + p = list_ptr(eq_box); + t = adjust_tail; + adjust_tail = null; + pre_t = pre_adjust_tail; + pre_adjust_tail = null; + eq_w = width(eq_box); + line_w = display_width_par; + line_s = display_indent_par; + if (eqno_box == null) { + eqno_w = 0; + eqno_width = 0; + eqno_w2 = 0; + } else { + eqno_w = width(eqno_box); + eqno_width = eqno_w; + eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); + subtype(eqno_box) = equation_number_list; /* new */ + /* build_attribute_list(eqno_box); */ /* probably already set */ + } + if (eq_w + eqno_w2 > line_w) { + /* The user can force the equation number to go on a separate line + by causing its width to be zero. */ + if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) + || (total_shrink[sfi] != 0) + || (total_shrink[fil] != 0) + || (total_shrink[fill] != 0) + || (total_shrink[filll] != 0))) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w - eqno_w2, exactly, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + } else { + eqno_w = 0; + if (eq_w > line_w) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w, exactly, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + } + } + eq_w = width(eq_box); + } + /* We try first to center the display without regard to the existence of + the equation number. If that would make it too close (where ``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. + */ + d = half(line_w - eq_w); + if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ + d = half(line_w - eq_w - eqno_w); + if (p != null) + if (!is_char_node(p)) + if (type(p) == glue_node) + d = 0; + } + + tail_append(new_penalty(pre_display_penalty_par,after_display_penalty)); + if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */ + g1 = above_display_skip_code; + g2 = below_display_skip_code; + } else { + g1 = above_display_short_skip_code; + g2 = below_display_short_skip_code; + } + + /* 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 (eqno_box && l && (eqno_w == 0)) { + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = 0; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + } else { + inject_display_skip_before(g1); + } + + if (eqno_w != 0) { + r = new_kern(line_w - eq_w - eqno_w - d); + if (l) { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 1\n"); +#endif + s = new_kern(width(r) + eqno_w); + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } else { + /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 2\n"); +#endif + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + } + } else { + if (math_direction_par==dir_TLT) { + /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 3\n"); +#endif + s = new_kern(width(r) + eqno_w); + } else { + /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 4\n"); +#endif + s = new_kern(width(r)); + } + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } + eq_box = eqno_box; + } else { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 5\n"); +#endif + } else { + /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 6\n"); +#endif + } + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + } else { + if (math_direction_par==dir_TLT) { + /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 7\n"); +#endif + s = new_kern(d); + } else { + /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 8\n"); +#endif + s = new_kern(width(r) + eqno_w); + } + try_couple_nodes(s,eq_box); + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + eq_box = s; + } + } + eq_box = hpack(eq_box, 0, additional, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + shift_amount(eq_box) = line_s; + } else { + shift_amount(eq_box) = line_s + d; + } +/* check for prev: */ + append_to_vlist(eq_box,lua_key_index(equation)); + + if ((eqno_box != null) && (eqno_w == 0) && !l) { + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = line_s + line_w - eqno_width ; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + g2 = 0; + } + if (t != adjust_head) { /* migrating material comes after equation number */ + vlink(tail) = vlink(adjust_head); + /* needs testing */ + alink(adjust_tail) = alink(tail); + tail = t; + } + if (pre_t != pre_adjust_head) { + vlink(tail) = vlink(pre_adjust_head); + /* needs testing */ + alink(pre_adjust_tail) = alink(tail); + tail = pre_t; + } + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(g2); + resume_after_display(); +} + +@ @c +void after_math(void) +{ + int m; /* |mmode| or |-mmode| */ + pointer p; /* the formula */ + pointer a = null; /* box containing equation number */ + boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ + m = mode; + p = fin_mlist(null); /* this pops the nest */ + if (cur_cmd == math_shift_cs_cmd && + (cur_chr == text_style || cur_chr == display_style)) { + you_cant(); + } + if (mode == -m) { /* end of equation number */ + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + run_mlist_to_hlist(p, false, text_style); + a = hpack(vlink(temp_head), 0, additional, -1); + build_attribute_list(a); + unsave_math(); + decr(save_ptr); /* now |cur_group=math_shift_group| */ + assert(saved_type(0) == saved_eqno); + if (saved_value(0) == 1) + l = true; + m = mode; + p = fin_mlist(null); + + } + if (m < 0) { + /* 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. + */ + if (cur_cmd == math_shift_cs_cmd) { + check_inline_math_end(); + } + tail_append(new_math(math_surround_par, before)); + /* begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /* obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 1 : + /* always left */ + case 3 : + /* always both */ + case 6 : + /* only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 2 : + /* only right */ + surround(tail) = 0; + break ; + case 4 : + /* ignore, obey marthsurround */ + break ; + case 5: + /* all spacing disabled */ + surround(tail) = 0; + break ; + } + /* end mathskip code */ + if (dir_math_save) { + tail_append(new_dir(math_direction_par)); + } + run_mlist_to_hlist(p, (mode > 0), text_style); + vlink(tail) = vlink(temp_head); + while (vlink(tail) != null) { + tail = vlink(tail); + } + if (dir_math_save) { + tail_append(new_dir(math_direction_par - dir_swap)); + } + dir_math_save = false; + tail_append(new_math(math_surround_par, after)); + /* begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /* obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 2 : + /* always right */ + case 3 : + /* always both */ + case 6 : + /* only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 1 : + /* only left */ + surround(tail) = 0; + break ; + case 4 : + /* ignore, obey marthsurround */ + break ; + case 5: + /* all spacing disabled */ + surround(tail) = 0; + break ; + } + /* end mathskip code */ + space_factor_par = 1000; + unsave_math(); + } else { + if (a == null) { + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + } + run_mlist_to_hlist(p, false, display_style); + finish_displayed_math(l, a, vlink(temp_head)); + } +} + +@ 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. + +@c +void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) +{ + do_assignments(); + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + pop_nest(); + tail_append(new_penalty(pre_display_penalty_par,before_display_penalty)); + inject_display_skip_before(above_display_skip_code); + vlink(tail) = p; + if (p != null) + tail = q; + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(below_display_skip_code); + cur_list.prev_depth_field = saved_prevdepth; + resume_after_display(); +} + +@ Interface to \.{\\Umath} and \.{\\mathstyle} + +@c +void setup_math_style(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math_style(nucleus(tail), num_style(m_style)); +} + +@ @c +void print_math_style(void) +{ + if (abs(mode) == mmode) + print_int(m_style); + else + print_int(-1); +} |