1% texmath.w
2%
3% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25@ @c
26#define mode          cur_list.mode_field
27#define head          cur_list.head_field
28#define tail          cur_list.tail_field
29#define prev_graf     cur_list.pg_field
30#define eTeX_aux      cur_list.eTeX_aux_field
31#define delim_ptr     eTeX_aux
32#define space_factor  cur_list.space_factor_field
33#define incompleat_noad cur_list.incompleat_noad_field
34
35#define cur_fam int_par(cur_fam_code)
36#define text_direction int_par(text_direction_code)
37
38#define var_code 7
39
40@ TODO: not sure if this is the right order
41@c
42#define back_error(A,B) do {                    \
43    OK_to_interrupt=false;                      \
44    back_input();                               \
45    OK_to_interrupt=true;                       \
46    tex_error(A,B);                             \
47  } while (0)
48
49@ @c
50int scan_math(pointer, int);
51pointer fin_mlist(pointer);
52
53#define pre_display_size dimen_par(pre_display_size_code)
54#define hsize          dimen_par(hsize_code)
55#define display_width  dimen_par(display_width_code)
56#define display_indent dimen_par(display_indent_code)
57#define math_surround  dimen_par(math_surround_code)
58#define hang_indent    dimen_par(hang_indent_code)
59#define hang_after     int_par(hang_after_code)
60#define every_math     equiv(every_math_loc)
61#define every_display  equiv(every_display_loc)
62#define par_shape_ptr  equiv(par_shape_loc)
63
64
65@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
66{\sl mlist}, which is essentially a tree structure representing that
67formula.  An mlist is a linear sequence of items, but we can regard it as
68a tree structure because mlists can appear within mlists. For example, many
69of the entries can be subscripted or superscripted, and such ``scripts''
70are mlists in their own right.
71
72An entire formula is parsed into such a tree before any of the actual
73typesetting is done, because the current style of type is usually not
74known until the formula has been fully scanned. For example, when the
75formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
76that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
77
78During the scanning process, each element of the mlist being built is
79classified as a relation, a binary operator, an open parenthesis, etc.,
80or as a construct like `\.{\\sqrt}' that must be built up. This classification
81appears in the mlist data structure.
82
83After a formula has been fully scanned, the mlist is converted to an hlist
84so that it can be incorporated into the surrounding text. This conversion is
85controlled by a recursive procedure that decides all of the appropriate
86styles by a ``top-down'' process starting at the outermost level and working
87in towards the subformulas. The formula is ultimately pasted together using
88combinations of horizontal and vertical boxes, with glue and penalty nodes
89inserted as necessary.
90
91An mlist is represented internally as a linked list consisting chiefly
92of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
93similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
94allowed to appear in mlists together with the noads; \TeX\ tells the difference
95by means of the |type| field, since a noad's |type| is always greater than
96that of a node. An mlist does not contain character nodes, hlist nodes, vlist
97nodes, math nodes or unset nodes; in particular, each mlist item appears in the
98variable-size part of |mem|, so the |type| field is always present.
99
100Each noad is five or more words long. The first word contains the
101|type| and |subtype| and |link| fields that are already so familiar to
102us; the second contains the attribute list pointer, and the third,
103fourth an fifth words are called the noad's |nucleus|, |subscr|, and
104|supscr| fields. (This use of a combined attribute list is temporary.
105Eventually, each of fields need their own list)
106
107Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
108parsed into an mlist containing a single element called an |ord_noad|.
109The |nucleus| of this noad is a representation of `\.x', the |subscr| is
110empty, and the |supscr| is a representation of `\.2'.
111
112The |nucleus|, |subscr|, and |supscr| fields are further broken into
113subfields. If |p| points to a noad, and if |q| is one of its principal
114fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the
115corresponding attribute of noad |p| is not present). Otherwise, there are
116several possibilities for the subfields, depending on the |type| of |q|.
117
118\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of
119the sixteen font families, and |character(q)| is the number of a character
120within a font of that family, as in a character node.
121
122\yskip\hang|type(q)=math_text_char_node| is similar, but the character is
123unsubscripted and unsuperscripted and it is followed immediately by another
124character from the same font. (This |type| setting appears only
125briefly during the processing; it is used to suppress unwanted italic
126corrections.)
127
128\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box
129node (either an |hlist_node| or a |vlist_node|) that should be used as the
130value of the field.  The |shift_amount| in the subsidiary box node is the
131amount by which that box will be shifted downward.
132
133\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to
134an mlist; the mlist must be converted to an hlist in order to obtain
135the value of this field.
136
137\yskip\noindent In the latter case, we might have |math_list(q)=null|. This
138is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}'
139and `\.{\$P\$}' produce different results (the former will not have the
140``italic correction'' added to the width of |P|, but the ``script skip''
141will be added).
142
143@c
144static void unsave_math(void)
145{
146    unsave();
147    decr(save_ptr);
148    flush_node_list(text_dir_ptr);
149    assert(saved_type(0) == saved_textdir);
150    text_dir_ptr = saved_value(0);
151}
152
153
154@ Sometimes it is necessary to destroy an mlist. The following
155subroutine empties the current list, assuming that |abs(mode)=mmode|.
156
157@c
158void flush_math(void)
159{
160    flush_node_list(vlink(head));
161    flush_node_list(incompleat_noad);
162    vlink(head) = null;
163    tail = head;
164    incompleat_noad = null;
165}
166
167@ Before we can do anything in math mode, we need fonts.
168
169@c
170#define MATHFONTSTACK  8
171#define MATHFONTDEFAULT 0       /* == nullfont */
172
173static sa_tree math_fam_head = NULL;
174
175@ @c
176int fam_fnt(int fam_id, int size_id)
177{
178    int n = fam_id + (256 * size_id);
179    return (int) get_sa_item(math_fam_head, n);
180}
181
182void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
183{
184    int n = fam_id + (256 * size_id);
185    set_sa_item(math_fam_head, n, (sa_tree_item) f, lvl);
186    fixup_math_parameters(fam_id, size_id, f, lvl);
187    if (int_par(tracing_assigns_code) > 0) {
188        begin_diagnostic();
189        tprint("{assigning");
190        print_char(' ');
191        print_cmd_chr(def_family_cmd, size_id);
192        print_int(fam_id);
193        print_char('=');
194        print_font_identifier(fam_fnt(fam_id, size_id));
195        print_char('}');
196        end_diagnostic(false);
197    }
198}
199
200@ @c
201static void unsave_math_fam_data(int gl)
202{
203    sa_stack_item st;
204    if (math_fam_head->stack == NULL)
205        return;
206    while (math_fam_head->stack_ptr > 0 &&
207           abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
208           >= (int) gl) {
209        st = math_fam_head->stack[math_fam_head->stack_ptr];
210        if (st.level > 0) {
211            rawset_sa_item(math_fam_head, st.code, st.value);
212            /* now do a trace message, if requested */
213            if (int_par(tracing_restores_code) > 0) {
214                int size_id = st.code / 256;
215                int fam_id = st.code % 256;
216                begin_diagnostic();
217                tprint("{restoring");
218                print_char(' ');
219                print_cmd_chr(def_family_cmd, size_id);
220                print_int(fam_id);
221                print_char('=');
222                print_font_identifier(fam_fnt(fam_id, size_id));
223                print_char('}');
224                end_diagnostic(false);
225            }
226        }
227        (math_fam_head->stack_ptr)--;
228    }
229}
230
231
232
233@ and parameters
234
235@c
236#define MATHPARAMSTACK  8
237#define MATHPARAMDEFAULT undefined_math_parameter
238
239static sa_tree math_param_head = NULL;
240
241@ @c
242void def_math_param(int param_id, int style_id, scaled value, int lvl)
243{
244    int n = param_id + (256 * style_id);
245    set_sa_item(math_param_head, n, (sa_tree_item) value, lvl);
246    if (int_par(tracing_assigns_code) > 0) {
247        begin_diagnostic();
248        tprint("{assigning");
249        print_char(' ');
250        print_cmd_chr(set_math_param_cmd, param_id);
251        print_cmd_chr(math_style_cmd, style_id);
252        print_char('=');
253        print_int(value);
254        print_char('}');
255        end_diagnostic(false);
256    }
257}
258
259scaled get_math_param(int param_id, int style_id)
260{
261    int n = param_id + (256 * style_id);
262    return (scaled) get_sa_item(math_param_head, n);
263}
264
265
266@ @c
267static void unsave_math_param_data(int gl)
268{
269    sa_stack_item st;
270    if (math_param_head->stack == NULL)
271        return;
272    while (math_param_head->stack_ptr > 0 &&
273           abs(math_param_head->stack[math_param_head->stack_ptr].level)
274           >= (int) gl) {
275        st = math_param_head->stack[math_param_head->stack_ptr];
276        if (st.level > 0) {
277            rawset_sa_item(math_param_head, st.code, st.value);
278            /* now do a trace message, if requested */
279            if (int_par(tracing_restores_code) > 0) {
280                int param_id = st.code % 256;
281                int style_id = st.code / 256;
282                begin_diagnostic();
283                tprint("{restoring");
284                print_char(' ');
285                print_cmd_chr(set_math_param_cmd, param_id);
286                print_cmd_chr(math_style_cmd, style_id);
287                print_char('=');
288                print_int(get_math_param(param_id, style_id));
289                print_char('}');
290                end_diagnostic(false);
291            }
292        }
293        (math_param_head->stack_ptr)--;
294    }
295}
296
297
298@ saving and unsaving of both
299
300@c
301void unsave_math_data(int gl)
302{
303    unsave_math_fam_data(gl);
304    unsave_math_param_data(gl);
305}
306
307@ Dumping and undumping
308@c
309void dump_math_data(void)
310{
311    if (math_fam_head == NULL)
312        math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT);
313    dump_sa_tree(math_fam_head);
314    if (math_param_head == NULL)
315        math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT);
316    dump_sa_tree(math_param_head);
317}
318
319void undump_math_data(void)
320{
321    math_fam_head = undump_sa_tree();
322    math_param_head = undump_sa_tree();
323}
324
325@ @c
326void initialize_math(void)
327{
328    if (math_fam_head == NULL)
329        math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT);
330    if (math_param_head == NULL) {
331        math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT);
332        initialize_math_spacing();
333    }
334    return;
335}
336
337
338
339@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
340Clo, Pun, or Inn, for purposes of spacing and line breaking. An
341|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
342|punct_noad|, or |inner_noad| is used to represent portions of the various
343types. For example, an `\.=' sign in a formula leads to the creation of a
344|rel_noad| whose |nucleus| field is a representation of an equals sign
345(usually |fam=0|, |character=075|).  A formula preceded by \.{\\mathrel}
346also results in a |rel_noad|.  When a |rel_noad| is followed by an
347|op_noad|, say, and possibly separated by one or more ordinary nodes (not
348noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
349just after the formula that corresponds to the |rel_noad|, unless there
350already was a penalty immediately following; and a ``thick space'' will be
351inserted just before the formula that corresponds to the |op_noad|.
352
353A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
354has a |subtype=normal|. The only exception is that an |op_noad| might
355have |subtype=limits| or |no_limits|, if the normal positioning of
356limits has been overridden for this operator.
357
358A |radical_noad| also has a |left_delimiter| field, which usually
359represents a square root sign.
360
361A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
362
363Delimiter fields have four subfields
364called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
365represent variable-size delimiters by giving the ``small'' and ``large''
366starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
367@:TeXbook}{\sl The \TeX book@>
368
369A |fraction_noad| is actually quite different from all other noads.
370It has |thickness|, |denominator|, and |numerator| fields instead of
371|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value
372that tells how thick to make a fraction rule; however, the special
373value |default_code| is used to stand for the
374|default_rule_thickness| of the current size. The |numerator| and
375|denominator| point to mlists that define a fraction; we always have
376$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The
377|left_delimiter| and |right_delimiter| fields specify delimiters that will
378be placed at the left and right of the fraction. In this way, a
379|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
380\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
381 \.{\\abovewithdelims}.
382
383
384
385@ The |new_noad| function creates an |ord_noad| that is completely null
386
387@c
388pointer new_noad(void)
389{
390    pointer p;
391    p = new_node(simple_noad, ord_noad_type);
392    /* all noad fields are zero after this */
393    return p;
394}
395
396@ @c
397pointer new_sub_box(pointer curbox)
398{
399    pointer p, q;
400    p = new_noad();
401    q = new_node(sub_box_node, 0);
402    nucleus(p) = q;
403    math_list(nucleus(p)) = curbox;
404    return p;
405}
406
407
408@ A few more kinds of noads will complete the set: An |under_noad| has its
409nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
410an accent over its nucleus; the accent character appears as
411|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad|
412centers its nucleus vertically with respect to the axis of the formula;
413in such noads we always have |type(nucleus(p))=sub_box|.
414
415And finally, we have the |fence_noad| type, to implement
416\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}.
417The |nucleus| of such noads is
418replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
419a |fence_noad| such that |delimiter(p)| holds the family and character
420codes for all left parentheses. A |fence_noad| of subtype |left_noad_side|
421never appears in an mlist except as the first element, and a |fence_noad|
422with subtype |right_noad_side| never appears in an mlist
423except as the last element; furthermore, we either have both a |left_noad_side|
424and a |right_noad_side|, or neither one is present.
425
426
427
428@ Math formulas can also contain instructions like \.{\\textstyle} that
429override \TeX's normal style rules. A |style_node| is inserted into the
430data structure to record such instructions; it is three words long, so it
431is considered a node instead of a noad. The |subtype| is either |display_style|
432or |text_style| or |script_style| or |script_script_style|. The
433second and third words of a |style_node| are not used, but they are
434present because a |choice_node| is converted to a |style_node|.
435
436\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
437|display_style|, \dots, |script_script_style|, and adds~1 to get the
438``cramped'' versions of these styles. This gives a numerical order that
439is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
440i.e., a smaller style has a larger numerical value.
441@:TeXbook}{\sl The \TeX book@>
442
443@c
444const char *math_style_names[] = {
445    "display", "crampeddisplay",
446    "text", "crampedtext",
447    "script", "crampedscript",
448    "scriptscript", "crampedscriptscript",
449    NULL
450};
451
452const char *math_param_names[] = {
453    "quad", "axis", "operatorsize",
454    "overbarkern", "overbarrule", "overbarvgap",
455    "underbarkern", "underbarrule", "underbarvgap",
456    "radicalkern", "radicalrule", "radicalvgap",
457    "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
458    "stackvgap", "stacknumup", "stackdenomdown",
459    "fractionrule", "fractionnumvgap", "fractionnumup",
460    "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
461    "limitabovevgap", "limitabovebgap", "limitabovekern",
462    "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
463    "underdelimitervgap", "underdelimiterbgap",
464    "overdelimitervgap", "overdelimiterbgap",
465    "subshiftdrop", "supshiftdrop", "subshiftdown",
466    "subsupshiftdown", "subtopmax", "supshiftup",
467    "supbottommin", "supsubbottommax", "subsupvgap",
468    "spaceafterscript", "connectoroverlapmin",
469    "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
470    "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
471    "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
472    "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
473    "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
474    "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
475    "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
476    "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
477    "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
478    "openopenspacing", "openclosespacing", "openpunctspacing",
479    "openinnerspacing",
480    "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
481    "closeopenspacing", "closeclosespacing", "closepunctspacing",
482    "closeinnerspacing",
483    "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
484    "punctopenspacing", "punctclosespacing", "punctpunctspacing",
485    "punctinnerspacing",
486    "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
487    "inneropenspacing", "innerclosespacing", "innerpunctspacing",
488    "innerinnerspacing",
489    NULL
490};
491
492@ @c
493pointer new_style(small_number s)
494{                               /* create a style node */
495    m_style = s;
496    return new_node(style_node, s);
497}
498
499@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
500has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
501and |script_script_mlist| pointing to the mlists for each style.
502
503@c
504static pointer new_choice(void)
505{                               /* create a choice node */
506    return new_node(choice_node, 0);    /* the |subtype| is not used */
507}
508
509
510@ Let's consider now the previously unwritten part of |show_node_list|
511that displays the things that can only be present in mlists; this
512program illustrates how to access the data structures just defined.
513
514In the context of the following program, |p| points to a node or noad that
515should be displayed, and the current string contains the ``recursion history''
516that leads to this point. The recursion history consists of a dot for each
517outer level in which |p| is subsidiary to some node, or in which |p| is
518subsidiary to the |nucleus| field of some noad; the dot is replaced by
519`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
520or |supscr| or |denominator| or |numerator| fields of noads. For example,
521the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
522|x| in the (ridiculous) formula
523`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
524
525@c
526void display_normal_noad(pointer p);    /* forward */
527void display_fence_noad(pointer p);     /* forward */
528void display_fraction_noad(pointer p);  /* forward */
529
530void show_math_node(pointer p)
531{
532    switch (type(p)) {
533    case style_node:
534        print_cmd_chr(math_style_cmd, subtype(p));
535        break;
536    case choice_node:
537        tprint_esc("mathchoice");
538        append_char('D');
539        show_node_list(display_mlist(p));
540        flush_char();
541        append_char('T');
542        show_node_list(text_mlist(p));
543        flush_char();
544        append_char('S');
545        show_node_list(script_mlist(p));
546        flush_char();
547        append_char('s');
548        show_node_list(script_script_mlist(p));
549        flush_char();
550        break;
551    case simple_noad:
552    case radical_noad:
553    case accent_noad:
554        display_normal_noad(p);
555        break;
556    case fence_noad:
557        display_fence_noad(p);
558        break;
559    case fraction_noad:
560        display_fraction_noad(p);
561        break;
562    default:
563        tprint("Unknown node type!");
564        break;
565    }
566}
567
568
569@ Here are some simple routines used in the display of noads.
570
571@c
572static void print_fam_and_char(pointer p)
573{                               /* prints family and character */
574    tprint_esc("fam");
575    print_int(math_fam(p));
576    print_char(' ');
577    print(math_character(p));
578}
579
580@ @c
581static void print_delimiter(pointer p)
582{
583    int a;
584    if (small_fam(p) < 0) {
585        print_int(-1);          /* this should never happen */
586    } else if (small_fam(p) < 16 && large_fam(p) < 16 &&
587               small_char(p) < 256 && large_char(p) < 256) {
588        /* traditional tex style */
589        a = small_fam(p) * 256 + small_char(p);
590        a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
591        print_hex(a);
592    } else if ((large_fam(p) == 0 && large_char(p) == 0) ||
593               small_char(p) > 65535 || large_char(p) > 65535) {
594        /* modern xetex/luatex style */
595        print_hex(small_fam(p));
596        print_hex(small_char(p));
597    }
598}
599
600
601@ The next subroutine will descend to another level of recursion when a
602subsidiary mlist needs to be displayed. The parameter |c| indicates what
603character is to become part of the recursion history. An empty mlist is
604distinguished from a missing field, because these are not equivalent
605(as explained above).
606@^recursion@>
607
608@c
609static void print_subsidiary_data(pointer p, ASCII_code c)
610{                               /* display a noad field */
611    if ((int) cur_length >= depth_threshold) {
612        if (p != null)
613            tprint(" []");
614    } else {
615        append_char(c);         /* include |c| in the recursion history */
616        if (p != null) {
617            switch (type(p)) {
618            case math_char_node:
619                print_ln();
620                print_current_string();
621                print_fam_and_char(p);
622                break;
623            case sub_box_node:
624                show_node_list(math_list(p));
625                break;
626            case sub_mlist_node:
627                if (math_list(p) == null) {
628                    print_ln();
629                    print_current_string();
630                    tprint("{}");
631                } else {
632                    show_node_list(math_list(p));
633                }
634                break;
635            }
636        }
637        flush_char();           /* remove |c| from the recursion history */
638    }
639}
640
641@ @c
642void display_normal_noad(pointer p)
643{
644    switch (type(p)) {
645    case simple_noad:
646        switch (subtype(p)) {
647        case ord_noad_type:
648            tprint_esc("mathord");
649            break;
650        case op_noad_type_normal:
651        case op_noad_type_limits:
652        case op_noad_type_no_limits:
653            tprint_esc("mathop");
654            if (subtype(p) == op_noad_type_limits)
655                tprint_esc("limits");
656            else if (subtype(p) == op_noad_type_no_limits)
657                tprint_esc("nolimits");
658            break;
659        case bin_noad_type:
660            tprint_esc("mathbin");
661            break;
662        case rel_noad_type:
663            tprint_esc("mathrel");
664            break;
665        case open_noad_type:
666            tprint_esc("mathopen");
667            break;
668        case close_noad_type:
669            tprint_esc("mathclose");
670            break;
671        case punct_noad_type:
672            tprint_esc("mathpunct");
673            break;
674        case inner_noad_type:
675            tprint_esc("mathinner");
676            break;
677        case over_noad_type:
678            tprint_esc("overline");
679            break;
680        case under_noad_type:
681            tprint_esc("underline");
682            break;
683        case vcenter_noad_type:
684            tprint_esc("vcenter");
685            break;
686        default:
687            tprint("<unknown noad type!>");
688            break;
689        }
690        break;
691    case radical_noad:
692        if (subtype(p) == 7)
693            tprint_esc("Udelimiterover");
694        else if (subtype(p) == 6)
695            tprint_esc("Udelimiterunder");
696        else if (subtype(p) == 5)
697            tprint_esc("Uoverdelimiter");
698        else if (subtype(p) == 4)
699            tprint_esc("Uunderdelimiter");
700        else if (subtype(p) == 3)
701            tprint_esc("Uroot");
702        else
703            tprint_esc("radical");
704        print_delimiter(left_delimiter(p));
705        if (degree(p) != null) {
706            print_subsidiary_data(degree(p), '/');
707        }
708        break;
709    case accent_noad:
710       if (accent_chr(p) != null) {
711           if (bot_accent_chr(p) != null) {
712               tprint_esc("Umathaccent both");
713           } else {
714               tprint_esc("Umathaccent");
715           }
716       } else {
717           tprint_esc("Umathaccent bottom");
718       }
719       switch (subtype(p)) {
720       case 0:
721        if (accent_chr(p) != null) {
722            if (bot_accent_chr(p) != null) {
723                print_fam_and_char(accent_chr(p));
724                print_fam_and_char(bot_accent_chr(p));
725            } else {
726                print_fam_and_char(accent_chr(p));
727            }
728        } else {
729            print_fam_and_char(bot_accent_chr(p));
730        }
731        break;
732       case 1:
733        if (accent_chr(p) != null) {
734            tprint(" fixed ");
735	    print_fam_and_char(accent_chr(p));
736            if (bot_accent_chr(p) != null) {
737                print_fam_and_char(bot_accent_chr(p));
738            }
739        } else {
740            confusion("display_accent_noad");
741        }
742        break;
743       case 2:
744        if (bot_accent_chr(p) != null) {
745            if (accent_chr(p) != null) {
746	       print_fam_and_char(accent_chr(p));
747            }
748	    tprint(" fixed ");
749            print_fam_and_char(bot_accent_chr(p));
750        } else{
751            confusion("display_accent_noad");
752        }
753        break;
754       case 3:
755        if (accent_chr(p) != null && bot_accent_chr(p) != null) {
756            tprint(" fixed ");
757            print_fam_and_char(accent_chr(p));
758	    tprint(" fixed ");
759            print_fam_and_char(bot_accent_chr(p));
760        } else {
761            confusion("display_accent_noad");
762        }
763        break;
764       }
765       break;
766    }
767    print_subsidiary_data(nucleus(p), '.');
768    print_subsidiary_data(supscr(p), '^');
769    print_subsidiary_data(subscr(p), '_');
770}
771
772@ @c
773void display_fence_noad(pointer p)
774{
775    if (subtype(p) == right_noad_side)
776        tprint_esc("right");
777    else if (subtype(p) == left_noad_side)
778        tprint_esc("left");
779    else
780        tprint_esc("middle");
781    print_delimiter(delimiter(p));
782}
783
784@ @c
785void display_fraction_noad(pointer p)
786{
787    tprint_esc("fraction, thickness ");
788    if (thickness(p) == default_code)
789        tprint("= default");
790    else
791        print_scaled(thickness(p));
792    if ((left_delimiter(p) != null) &&
793        ((small_fam(left_delimiter(p)) != 0) ||
794         (small_char(left_delimiter(p)) != 0) ||
795         (large_fam(left_delimiter(p)) != 0) ||
796         (large_char(left_delimiter(p)) != 0))) {
797        tprint(", left-delimiter ");
798        print_delimiter(left_delimiter(p));
799    }
800    if ((right_delimiter(p) != null) &&
801        ((small_fam(right_delimiter(p)) != 0) ||
802         (small_char(right_delimiter(p)) != 0) ||
803         (large_fam(right_delimiter(p)) != 0) ||
804         (large_char(right_delimiter(p)) != 0))) {
805        tprint(", right-delimiter ");
806        print_delimiter(right_delimiter(p));
807    }
808    print_subsidiary_data(numerator(p), '\\');
809    print_subsidiary_data(denominator(p), '/');
810}
811
812
813@ The routines that \TeX\ uses to create mlists are similar to those we have
814just seen for the generation of hlists and vlists. But it is necessary to
815make ``noads'' as well as nodes, so the reader should review the
816discussion of math mode data structures before trying to make sense out of
817the following program.
818
819Here is a little routine that needs to be done whenever a subformula
820is about to be processed. The parameter is a code like |math_group|.
821
822@c
823static void new_save_level_math(group_code c)
824{
825    set_saved_record(0, saved_textdir, 0, text_dir_ptr);
826    text_dir_ptr = new_dir(math_direction);
827    incr(save_ptr);
828    new_save_level(c);
829    eq_word_define(int_base + body_direction_code, math_direction);
830    eq_word_define(int_base + par_direction_code, math_direction);
831    eq_word_define(int_base + text_direction_code, math_direction);
832}
833
834@ @c
835static void push_math(group_code c, int mstyle)
836{
837    if (math_direction != text_direction)
838        dir_math_save = true;
839    push_nest();
840    mode = -mmode;
841    incompleat_noad = null;
842    m_style = mstyle;
843    new_save_level_math(c);
844}
845
846@ @c
847static void enter_ordinary_math(void)
848{
849    push_math(math_shift_group, text_style);
850    eq_word_define(int_base + cur_fam_code, -1);
851    if (every_math != null)
852        begin_token_list(every_math, every_math_text);
853}
854
855@ @c
856void enter_display_math(void);
857
858@ We get into math mode from horizontal mode when a `\.\$' (i.e., a
859|math_shift| character) is scanned. We must check to see whether this
860`\.\$' is immediately followed by another, in case display math mode is
861called for.
862
863@c
864void init_math(void)
865{
866    if (cur_cmd == math_shift_cmd) {
867        get_token();            /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
868        if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
869            enter_display_math();
870        } else {
871            back_input();
872            enter_ordinary_math();
873        }
874    } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) {
875        enter_display_math();
876    } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
877        enter_ordinary_math();
878    } else {
879        you_cant();
880    }
881}
882
883
884@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
885`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
886the value of |cur_chr| is placed onto |save_stack| for safe keeping.
887
888
889@ When \TeX\ is in display math mode, |cur_group=math_shift_group|,
890so it is not necessary for the |start_eq_no| procedure to test for
891this condition.
892
893@c
894void start_eq_no(void)
895{
896    set_saved_record(0, saved_eqno, 0, cur_chr);
897    incr(save_ptr);
898    enter_ordinary_math();
899}
900
901@ Subformulas of math formulas cause a new level of math mode to be entered,
902on the semantic nest as well as the save stack. These subformulas arise in
903several ways: (1)~A left brace by itself indicates the beginning of a
904subformula that will be put into a box, thereby freezing its glue and
905preventing line breaks. (2)~A subscript or superscript is treated as a
906subformula if it is not a single character; the same applies to
907the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
908initiates a subformula that will be terminated by a matching \.{\\right}.
909The group codes placed on |save_stack| in these three cases are
910|math_group|, |math_group|, and |math_left_group|, respectively.
911
912Here is the code that handles case (1); the other cases are not quite as
913trivial, so we shall consider them later.
914
915@c
916void math_left_brace(void)
917{
918    pointer q;
919    tail_append(new_noad());
920    q = new_node(math_char_node, 0);
921    nucleus(tail) = q;
922    back_input();
923    (void) scan_math(nucleus(tail), m_style);
924}
925
926@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are
927opposite, then this function will return true. Discovering that fact
928is somewhat odd because it needs traversal of the |save_stack|.
929The occurance of displayed equations is weird enough that this is
930probably still better than having yet another field in the |input_stack|
931structures.
932
933None of this makes much sense if the inline direction of either one of
934\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current
935math machinery is ill suited anyway so I do not bother to test that.
936
937@c
938static boolean math_and_text_reversed_p(void)
939{
940    int i = save_ptr - 1;
941    while (save_type(i) != level_boundary)
942        i--;
943    while (i < save_ptr) {
944        if (save_type(i) == restore_old_value &&
945            save_value(i) == int_base + par_direction_code) {
946            if (textdir_opposite(math_direction, save_value(i - 1)))
947                return true;
948        }
949        i++;
950    }
951    return false;
952}
953
954
955
956
957
958@ When we enter display math mode, we need to call |line_break| to
959process the partial paragraph that has just been interrupted by the
960display. Then we can set the proper values of |display_width| and
961|display_indent| and |pre_display_size|.
962
963@c
964void enter_display_math(void)
965{
966    scaled w;                   /* new or partial |pre_display_size| */
967    scaled l;                   /* new |display_width| */
968    scaled s;                   /* new |display_indent| */
969    pointer p;
970    int n;                      /* scope of paragraph shape specification */
971    if (head == tail ||         /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
972        (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
973         type(tail) == whatsit_node &&
974         subtype(tail) == local_par_node && vlink(tail) == null)) {
975        if (vlink(head) == tail) {
976            /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
977               there is another display immediately following, we have to get rid
978               of that node */
979            flush_node(tail);
980        }
981        pop_nest();
982        w = -max_dimen;
983    } else {
984        line_break(true, math_shift_group);
985        w = actual_box_width(just_box, (2 * quad(get_cur_font())));
986    }
987    /* now we are in vertical mode, working on the list that will contain the display */
988    /* A displayed equation is considered to be three lines long, so we
989       calculate the length and offset of line number |prev_graf+2|. */
990    if (par_shape_ptr == null) {
991        if ((hang_indent != 0) &&
992            (((hang_after >= 0) && (prev_graf + 2 > hang_after)) ||
993             (prev_graf + 1 < -hang_after))) {
994            l = hsize - abs(hang_indent);
995            if (hang_indent > 0)
996                s = hang_indent;
997            else
998                s = 0;
999        } else {
1000            l = hsize;
1001            s = 0;
1002        }
1003    } else {
1004        n = vinfo(par_shape_ptr + 1);
1005        if (prev_graf + 2 >= n)
1006            p = par_shape_ptr + 2 * n + 1;
1007        else
1008            p = par_shape_ptr + 2 * (prev_graf + 2) + 1;
1009        s = varmem[(p - 1)].cint;
1010        l = varmem[p].cint;
1011    }
1012
1013    push_math(math_shift_group, display_style);
1014    mode = mmode;
1015    eq_word_define(int_base + cur_fam_code, -1);
1016    eq_word_define(dimen_base + pre_display_size_code, w);
1017    eq_word_define(dimen_base + display_width_code, l);
1018    eq_word_define(dimen_base + display_indent_code, s);
1019    eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0));
1020    if (every_display != null)
1021        begin_token_list(every_display, every_display_text);
1022    if (nest_ptr == 1) {
1023        if (!output_active)
1024	    lua_node_filter_s(buildpage_filter_callback,lua_key_index(before_display));
1025        build_page();
1026    }
1027}
1028
1029@ The next routine parses all variations of a delimiter code. The |extcode|
1030 tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the
1031 |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.).
1032 (the class is passed on for conversion to \.{\\mathchar}).
1033
1034@c
1035#define fam_in_range ((cur_fam>=0)&&(cur_fam<256))
1036
1037static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
1038{
1039    const char *hlp[] = {
1040        "I'm going to use 0 instead of that illegal code value.",
1041        NULL
1042    };
1043    delcodeval d;
1044    int mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
1045    mcls = 0;
1046    if (extcode == tex_mathcode) {      /* \.{\\delcode}, this is the easiest */
1047        scan_int();
1048        /*  "MFCCFCC or "FCCFCC */
1049        if (doclass) {
1050            mcls = (cur_val / 0x1000000);
1051            cur_val = (cur_val & 0xFFFFFF);
1052        }
1053        if (cur_val > 0xFFFFFF) {
1054            tex_error("Invalid delimiter code", hlp);
1055            cur_val = 0;
1056        }
1057        msfam = (cur_val / 0x100000);
1058        mschr = (cur_val % 0x100000) / 0x1000;
1059        mlfam = (cur_val & 0xFFF) / 0x100;
1060        mlchr = (cur_val % 0x100);
1061    } else if (extcode == xetex_mathcode) {     /* \.{\\Udelcode} */
1062        /* <0-7>,<0-0xFF>,<0-0x10FFFF>  or <0-0xFF>,<0-0x10FFFF> */
1063        if (doclass) {
1064            scan_int();
1065            mcls = cur_val;
1066        }
1067        scan_int();
1068        msfam = cur_val;
1069        scan_char_num();
1070        mschr = cur_val;
1071        if (msfam < 0 || msfam > 255) {
1072            tex_error("Invalid delimiter code", hlp);
1073            msfam = 0;
1074            mschr = 0;
1075        }
1076        mlfam = 0;
1077        mlchr = 0;
1078    } else if (extcode == xetexnum_mathcode) {  /* \.{\\Udelcodenum} */
1079        /* "FF<21bits> */
1080        /* the largest numeric value is $2^29-1$, but
1081           the top of bit 21 can't be used as it contains invalid USV's
1082         */
1083        if (doclass) {          /* such a primitive doesn't exist */
1084            confusion("xetexnum_mathcode");
1085        }
1086        scan_int();
1087        msfam = (cur_val / 0x200000);
1088        mschr = cur_val & 0x1FFFFF;
1089        if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
1090            tex_error("Invalid delimiter code", hlp);
1091            msfam = 0;
1092            mschr = 0;
1093        }
1094        mlfam = 0;
1095        mlchr = 0;
1096    } else {
1097        /* something's gone wrong */
1098        confusion("unknown_extcode");
1099    }
1100    d.origin_value = extcode;
1101    d.class_value = mcls;
1102    d.small_family_value = msfam;
1103    d.small_character_value = mschr;
1104    d.large_family_value = mlfam;
1105    d.large_character_value = mlchr;
1106    return d;
1107}
1108
1109@ @c
1110void scan_extdef_del_code(int level, int extcode)
1111{
1112    delcodeval d;
1113    int p;
1114    scan_char_num();
1115    p = cur_val;
1116    scan_optional_equals();
1117    d = do_scan_extdef_del_code(extcode, false);
1118    set_del_code(p, extcode, d.small_family_value, d.small_character_value,
1119                 d.large_family_value, d.large_character_value,
1120                 (quarterword) (level));
1121}
1122
1123@ @c
1124mathcodeval scan_mathchar(int extcode)
1125{
1126    char errstr[255] = { 0 };
1127    const char *hlp[] = {
1128        "I'm going to use 0 instead of that illegal code value.",
1129        NULL
1130    };
1131    mathcodeval d;
1132    int mcls = 0, mfam = 0, mchr = 0;
1133    if (extcode == tex_mathcode) {      /* \.{\\mathcode} */
1134        /* "TFCC */
1135        scan_int();
1136        if (cur_val > 0x8000) {
1137            tex_error("Invalid math code", hlp);
1138            cur_val = 0;
1139        }
1140        if (cur_val < 0) {
1141            snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val);
1142            tex_error(errstr, hlp);
1143            cur_val = 0;
1144        }
1145        mcls = (cur_val / 0x1000);
1146        mfam = ((cur_val % 0x1000) / 0x100);
1147        mchr = (cur_val % 0x100);
1148    } else if (extcode == xetex_mathcode) {
1149        /* <0-0x7> <0-0xFF> <0-0x10FFFF> */
1150        scan_int();
1151        mcls = cur_val;
1152        scan_int();
1153        mfam = cur_val;
1154        scan_char_num();
1155        mchr = cur_val;
1156        if (mcls < 0 || mcls > 7 || mfam > 255) {
1157            tex_error("Invalid math code", hlp);
1158            mchr = 0;
1159            mfam = 0;
1160            mcls = 0;
1161        }
1162    } else if (extcode == xetexnum_mathcode) {
1163        /* "FFT<21bits> */
1164        /* the largest numeric value is $2^32-1$, but
1165           the top of bit 21 can't be used as it contains invalid USV's
1166         */
1167        /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */
1168        scan_int();
1169        mfam = (cur_val / 0x200000) & 0x7FF;
1170        mcls = mfam % 0x08;
1171        mfam = mfam / 0x08;
1172        mchr = cur_val & 0x1FFFFF;
1173        if (mchr > 0x10FFFF) {
1174            tex_error("Invalid math code", hlp);
1175            mcls = 0;
1176            mfam = 0;
1177            mchr = 0;
1178        }
1179    } else {
1180        /* something's gone wrong */
1181        confusion("unknown_extcode");
1182    }
1183    d.class_value = mcls;
1184    d.family_value = mfam;
1185    d.origin_value = extcode;
1186    d.character_value = mchr;
1187    return d;
1188}
1189
1190@ @c
1191void scan_extdef_math_code(int level, int extcode)
1192{
1193    mathcodeval d;
1194    int p;
1195    scan_char_num();
1196    p = cur_val;
1197    scan_optional_equals();
1198    d = scan_mathchar(extcode);
1199    set_math_code(p, extcode, d.class_value,
1200                  d.family_value, d.character_value, (quarterword) (level));
1201}
1202
1203
1204@ this reads in a delcode when actually a mathcode is needed
1205@c
1206mathcodeval scan_delimiter_as_mathchar(int extcode)
1207{
1208    delcodeval dval;
1209    mathcodeval mval;
1210    dval = do_scan_extdef_del_code(extcode, true);
1211    mval.origin_value = 0;
1212    mval.class_value = dval.class_value;
1213    mval.family_value = dval.small_family_value;
1214    mval.character_value = dval.small_character_value;
1215    return mval;
1216}
1217
1218@ this has to match the inverse routine in the pascal code
1219 where the \.{\\Umathchardef} is executed
1220
1221@c
1222mathcodeval mathchar_from_integer(int value, int extcode)
1223{
1224    mathcodeval mval;
1225    mval.origin_value = extcode;
1226    if (extcode == tex_mathcode) {
1227        mval.class_value = (value / 0x1000);
1228        mval.family_value = ((value % 0x1000) / 0x100);
1229        mval.character_value = (value % 0x100);
1230    } else {                    /* some xetexended xetex thing */
1231        int mfam = (value / 0x200000) & 0x7FF;
1232        mval.class_value = mfam % 0x08;
1233        mval.family_value = mfam / 0x08;
1234        mval.character_value = value & 0x1FFFFF;
1235    }
1236    return mval;
1237}
1238
1239@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad
1240are broken down into subfields called |type| and either |math_list| or
1241|(math_fam,math_character)|. The job of |scan_math| is to figure out
1242what to place in one of these principal fields; it looks at the
1243subformula that comes next in the input, and places an encoding of
1244that subformula into a given word of |mem|.
1245
1246@c
1247#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
1248
1249
1250int scan_math(pointer p, int mstyle)
1251{
1252    /* label restart,reswitch,exit; */
1253    mathcodeval mval = { 0, 0, 0, 0 };
1254    assert(p != null);
1255  RESTART:
1256    get_next_nb_nr();
1257  RESWITCH:
1258    switch (cur_cmd) {
1259    case letter_cmd:
1260    case other_char_cmd:
1261    case char_given_cmd:
1262        mval = get_math_code(cur_chr);
1263        if (mval.class_value == 8) {
1264            /* An active character that is an |outer_call| is allowed here */
1265            cur_cs = active_to_cs(cur_chr, true);
1266            cur_cmd = eq_type(cur_cs);
1267            cur_chr = equiv(cur_cs);
1268            x_token();
1269            back_input();
1270            goto RESTART;
1271        }
1272        break;
1273    case char_num_cmd:
1274        scan_char_num();
1275        cur_chr = cur_val;
1276        cur_cmd = char_given_cmd;
1277        goto RESWITCH;
1278        break;
1279    case math_char_num_cmd:
1280        if (cur_chr == 0)
1281            mval = scan_mathchar(tex_mathcode);
1282        else if (cur_chr == 1)
1283            mval = scan_mathchar(xetex_mathcode);
1284        else if (cur_chr == 2)
1285            mval = scan_mathchar(xetexnum_mathcode);
1286        else
1287            confusion("scan_math");
1288        break;
1289    case math_given_cmd:
1290        mval = mathchar_from_integer(cur_chr, tex_mathcode);
1291        break;
1292    case xmath_given_cmd:
1293        mval = mathchar_from_integer(cur_chr, xetex_mathcode);
1294        break;
1295    case delim_num_cmd:
1296        if (cur_chr == 0)
1297            mval = scan_delimiter_as_mathchar(tex_mathcode);
1298        else if (cur_chr == 1)
1299            mval = scan_delimiter_as_mathchar(xetex_mathcode);
1300        else
1301            confusion("scan_math");
1302        break;
1303    default:
1304        /* The pointer |p| is placed on |save_stack| while a complex subformula
1305           is being scanned. */
1306        back_input();
1307        scan_left_brace();
1308        set_saved_record(0, saved_math, 0, p);
1309        incr(save_ptr);
1310        push_math(math_group, mstyle);
1311        return 1;
1312    }
1313    type(p) = math_char_node;
1314    math_character(p) = mval.character_value;
1315    if ((mval.class_value == var_code) && fam_in_range)
1316        math_fam(p) = cur_fam;
1317    else
1318        math_fam(p) = mval.family_value;
1319    return 0;
1320}
1321
1322
1323
1324@ The |set_math_char| procedure creates a new noad appropriate to a given
1325math code, and appends it to the current mlist. However, if the math code
1326is sufficiently large, the |cur_chr| is treated as an active character and
1327nothing is appended.
1328
1329@c
1330void set_math_char(mathcodeval mval)
1331{
1332    pointer p;                  /* the new noad */
1333    if (mval.class_value == 8) {
1334        /* An active character that is an |outer_call| is allowed here */
1335        cur_cs = active_to_cs(cur_chr, true);
1336        cur_cmd = eq_type(cur_cs);
1337        cur_chr = equiv(cur_cs);
1338        x_token();
1339        back_input();
1340    } else {
1341        pointer q;
1342        p = new_noad();
1343        q = new_node(math_char_node, 0);
1344        nucleus(p) = q;
1345        math_character(nucleus(p)) = mval.character_value;
1346        math_fam(nucleus(p)) = mval.family_value;
1347        if (mval.class_value == var_code) {
1348            if (fam_in_range)
1349                math_fam(nucleus(p)) = cur_fam;
1350            subtype(p) = ord_noad_type;
1351        } else {
1352            switch (mval.class_value) {
1353          /* *INDENT-OFF* */
1354          case 0: subtype(p) = ord_noad_type; break;
1355          case 1: subtype(p) = op_noad_type_normal; break;
1356          case 2: subtype(p) = bin_noad_type; break;
1357          case 3: subtype(p) = rel_noad_type; break;
1358          case 4: subtype(p) = open_noad_type; break;
1359          case 5: subtype(p) = close_noad_type; break;
1360          case 6: subtype(p) = punct_noad_type; break;
1361          /* *INDENT-ON* */
1362            }
1363        }
1364        vlink(tail) = p;
1365        tail = p;
1366    }
1367}
1368
1369
1370
1371@ The |math_char_in_text| procedure creates a new node representing a math char
1372in text code, and appends it to the current list. However, if the math code
1373is sufficiently large, the |cur_chr| is treated as an active character and
1374nothing is appended.
1375
1376@c
1377void math_char_in_text(mathcodeval mval)
1378{
1379    pointer p;                  /* the new node */
1380    if (mval.class_value == 8) {
1381        /* An active character that is an |outer_call| is allowed here */
1382        cur_cs = active_to_cs(cur_chr, true);
1383        cur_cmd = eq_type(cur_cs);
1384        cur_chr = equiv(cur_cs);
1385        x_token();
1386        back_input();
1387    } else {
1388        p = new_char(fam_fnt(mval.family_value, text_size),
1389                     mval.character_value);
1390        vlink(tail) = p;
1391        tail = p;
1392    }
1393}
1394
1395
1396@ @c
1397void math_math_comp(void)
1398{
1399    pointer q;
1400    tail_append(new_noad());
1401    subtype(tail) = (quarterword) cur_chr;
1402    q = new_node(math_char_node, 0);
1403    nucleus(tail) = q;
1404    if (cur_chr == over_noad_type)
1405        (void) scan_math(nucleus(tail), cramped_style(m_style));
1406    else
1407        (void) scan_math(nucleus(tail), m_style);
1408}
1409
1410
1411@ @c
1412void math_limit_switch(void)
1413{
1414    const char *hlp[] = {
1415        "I'm ignoring this misplaced \\limits or \\nolimits command.",
1416        NULL
1417    };
1418    if (head != tail) {
1419         if (type(tail) == simple_noad &&
1420             (subtype(tail) == op_noad_type_normal ||
1421              subtype(tail) == op_noad_type_limits ||
1422              subtype(tail) == op_noad_type_no_limits)) {
1423            subtype(tail) = (quarterword) cur_chr;
1424            return;
1425        }
1426    }
1427    tex_error("Limit controls must follow a math operator", hlp);
1428}
1429
1430
1431@ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
1432The first parameter of this procedure is the |mem| address where the
1433delimiter is to be placed; the second tells if this delimiter follows
1434\.{\\radical} or not.
1435
1436@c
1437static void scan_delimiter(pointer p, int r)
1438{
1439    delcodeval dval = { 0, 0, 0, 0, 0, 0 };
1440    if (r == tex_mathcode) {    /* \.{\\radical} */
1441        dval = do_scan_extdef_del_code(tex_mathcode, true);
1442    } else if (r == xetex_mathcode) {   /* \.{\\Uradical} */
1443        dval = do_scan_extdef_del_code(xetex_mathcode, false);
1444    } else if (r == no_mathcode) {
1445        get_next_nb_nr();
1446        switch (cur_cmd) {
1447        case letter_cmd:
1448        case other_char_cmd:
1449            dval = get_del_code(cur_chr);
1450            break;
1451        case delim_num_cmd:
1452            if (cur_chr == 0)   /* \.{\\delimiter} */
1453                dval = do_scan_extdef_del_code(tex_mathcode, true);
1454            else if (cur_chr == 1)      /* \.{\\Udelimiter} */
1455                dval = do_scan_extdef_del_code(xetex_mathcode, true);
1456            else
1457                confusion("scan_delimiter1");
1458            break;
1459        default:
1460            dval.small_family_value = -1;
1461            break;
1462        }
1463    } else {
1464        confusion("scan_delimiter2");
1465    }
1466    if (p == null)
1467        return;
1468    if (dval.small_family_value < 0) {
1469        const char *hlp[] = {
1470            "I was expecting to see something like `(' or `\\{' or",
1471            "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
1472            "should probably delete the `{' by typing `1' now, so that",
1473            "braces don't get unbalanced. Otherwise just proceed",
1474            "Acceptable delimiters are characters whose \\delcode is",
1475            "nonnegative, or you can use `\\delimiter <delimiter code>'.",
1476            NULL
1477        };
1478        back_error("Missing delimiter (. inserted)", hlp);
1479        small_fam(p) = 0;
1480        small_char(p) = 0;
1481        large_fam(p) = 0;
1482        large_char(p) = 0;
1483    } else {
1484        small_fam(p) = dval.small_family_value;
1485        small_char(p) = dval.small_character_value;
1486        large_fam(p) = dval.large_family_value;
1487        large_char(p) = dval.large_character_value;
1488    }
1489    return;
1490}
1491
1492
1493@ @c
1494void math_radical(void)
1495{
1496    halfword q;
1497    int chr_code = cur_chr;
1498    tail_append(new_node(radical_noad, chr_code));
1499    q = new_node(delim_node, 0);
1500    left_delimiter(tail) = q;
1501    if (chr_code == 0)          /* \.{\\radical} */
1502        scan_delimiter(left_delimiter(tail), tex_mathcode);
1503    else if (chr_code == 1)     /* \.{\\Uradical} */
1504        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1505    else if (chr_code == 2)     /* \.{\\Uroot} */
1506        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1507    else if (chr_code == 3)     /* \.{\\Uunderdelimiter} */
1508        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1509    else if (chr_code == 4)     /* \.{\\Uoverdelimiter} */
1510        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1511    else if (chr_code == 5)     /* \.{\\Udelimiterunder} */
1512        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1513    else if (chr_code == 6)     /* \.{\\Udelimiterover} */
1514        scan_delimiter(left_delimiter(tail), xetex_mathcode);
1515    else
1516        confusion("math_radical");
1517    if (chr_code == 2) {
1518        /* the trick with the |vlink(q)| is used by |scan_math|
1519           to decide whether it needs to go on */
1520        q = new_node(math_char_node, 0);
1521        vlink(q) = tail;
1522        degree(tail) = q;
1523        if (!scan_math(degree(tail), sup_sup_style(m_style))) {
1524            vlink(degree(tail)) = null;
1525            q = new_node(math_char_node, 0);
1526            nucleus(tail) = q;
1527            (void) scan_math(nucleus(tail), cramped_style(m_style));
1528        }
1529    } else {
1530        q = new_node(math_char_node, 0);
1531        nucleus(tail) = q;
1532        (void) scan_math(nucleus(tail), cramped_style(m_style));
1533    }
1534}
1535
1536@ @c
1537void math_ac(void)
1538{
1539    halfword q;
1540    mathcodeval t = { 0, 0, 0, 0 }, b = {
1541    0, 0, 0, 0};
1542    if (cur_cmd == accent_cmd) {
1543        const char *hlp[] = {
1544            "I'm changing \\accent to \\mathaccent here; wish me luck.",
1545            "(Accents are not the same in formulas as they are in text.)",
1546            NULL
1547        };
1548        tex_error("Please use \\mathaccent for accents in math mode", hlp);
1549    }
1550    tail_append(new_node(accent_noad, 0));
1551    if (cur_chr == 0) {         /* \.{\\mathaccent} */
1552        t = scan_mathchar(tex_mathcode);
1553    } else if (cur_chr == 1) {  /* \.{\\Umathaccent} */
1554	if (scan_keyword("fixed")) {
1555           subtype(tail) = 1;
1556	   t = scan_mathchar(xetex_mathcode);
1557	} else if (scan_keyword("both")) {
1558  	   if (scan_keyword("fixed")) {
1559             subtype(tail) = 1;
1560           }
1561	   t = scan_mathchar(xetex_mathcode);
1562  	   if (scan_keyword("fixed")) {
1563             subtype(tail) += 2;
1564           }
1565	   b = scan_mathchar(xetex_mathcode);
1566	} else if (scan_keyword("bottom")) {
1567  	   if (scan_keyword("fixed")) {
1568             subtype(tail) = 2;
1569           }
1570	   b = scan_mathchar(xetex_mathcode);
1571	} else {
1572	   t = scan_mathchar(xetex_mathcode);
1573	}
1574    } else {
1575        confusion("math_ac");
1576    }
1577    if (!(t.character_value == 0 && t.family_value == 0)) {
1578        q = new_node(math_char_node, 0);
1579        accent_chr(tail) = q;
1580        math_character(accent_chr(tail)) = t.character_value;
1581        if ((t.class_value == var_code) && fam_in_range)
1582            math_fam(accent_chr(tail)) = cur_fam;
1583        else
1584            math_fam(accent_chr(tail)) = t.family_value;
1585    }
1586    if (!(b.character_value == 0 && b.family_value == 0)) {
1587        q = new_node(math_char_node, 0);
1588        bot_accent_chr(tail) = q;
1589        math_character(bot_accent_chr(tail)) = b.character_value;
1590        if ((b.class_value == var_code) && fam_in_range)
1591            math_fam(bot_accent_chr(tail)) = cur_fam;
1592        else
1593            math_fam(bot_accent_chr(tail)) = b.family_value;
1594    }
1595    q = new_node(math_char_node, 0);
1596    nucleus(tail) = q;
1597    (void) scan_math(nucleus(tail), cramped_style(m_style));
1598}
1599
1600@ @c
1601pointer math_vcenter_group(pointer p)
1602{
1603    pointer q, r;
1604    q = new_noad();
1605    subtype(q) = vcenter_noad_type;
1606    r = new_node(sub_box_node, 0);
1607    nucleus(q) = r;
1608    math_list(nucleus(q)) = p;
1609    return q;
1610}
1611
1612
1613@ The routine that scans the four mlists of a \.{\\mathchoice} is very
1614much like the routine that builds discretionary nodes.
1615
1616@c
1617void append_choices(void)
1618{
1619    tail_append(new_choice());
1620    incr(save_ptr);
1621    set_saved_record(-1, saved_choices, 0, 0);
1622    push_math(math_choice_group, display_style);
1623    scan_left_brace();
1624}
1625
1626@ @c
1627void build_choices(void)
1628{
1629    pointer p;                  /* the current mlist */
1630    int prev_style;
1631    prev_style = m_style;
1632    unsave_math();
1633    p = fin_mlist(null);
1634    assert(saved_type(-1) == saved_choices);
1635    switch (saved_value(-1)) {
1636    case 0:
1637        display_mlist(tail) = p;
1638        break;
1639    case 1:
1640        text_mlist(tail) = p;
1641        break;
1642    case 2:
1643        script_mlist(tail) = p;
1644        break;
1645    case 3:
1646        script_script_mlist(tail) = p;
1647        decr(save_ptr);
1648        return;
1649        break;
1650    }                           /* there are no other cases */
1651    set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
1652    push_math(math_choice_group, (prev_style + 2));
1653    scan_left_brace();
1654}
1655
1656
1657@ Subscripts and superscripts are attached to the previous nucleus by the
1658action procedure called |sub_sup|.
1659
1660@c
1661void sub_sup(void)
1662{
1663    pointer q;
1664    if (tail == head || (!scripts_allowed(tail))) {
1665        tail_append(new_noad());
1666        q = new_node(sub_mlist_node, 0);
1667        nucleus(tail) = q;
1668    }
1669    if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) {   /* |super_sub_script| */
1670        if (supscr(tail) != null) {
1671            const char *hlp[] = {
1672                "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
1673            };
1674            tail_append(new_noad());
1675            q = new_node(sub_mlist_node, 0);
1676            nucleus(tail) = q;
1677            tex_error("Double superscript", hlp);
1678        }
1679        q = new_node(math_char_node, 0);
1680        supscr(tail) = q;
1681        (void) scan_math(supscr(tail), sup_style(m_style));
1682    } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
1683        if (subscr(tail) != null) {
1684            const char *hlp[] = {
1685                "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
1686            };
1687            tail_append(new_noad());
1688            q = new_node(sub_mlist_node, 0);
1689            nucleus(tail) = q;
1690            tex_error("Double subscript", hlp);
1691        }
1692        q = new_node(math_char_node, 0);
1693        subscr(tail) = q;
1694        (void) scan_math(subscr(tail), sub_style(m_style));
1695    }
1696}
1697
1698
1699@ An operation like `\.{\\over}' causes the current mlist to go into a
1700state of suspended animation: |incompleat_noad| points to a |fraction_noad|
1701that contains the mlist-so-far as its numerator, while the denominator
1702is yet to come. Finally when the mlist is finished, the denominator will
1703go into the incompleat fraction noad, and that noad will become the
1704whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
1705delimiters.
1706
1707@c
1708void math_fraction(void)
1709{
1710    halfword c;                 /* the type of generalized fraction we are scanning */
1711    pointer q;
1712    c = cur_chr;
1713    if (incompleat_noad != null) {
1714        const char *hlp[] = {
1715            "I'm ignoring this fraction specification, since I don't",
1716            "know whether a construction like `x \\over y \\over z'",
1717            "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
1718            NULL
1719        };
1720        if (c >= delimited_code) {
1721            scan_delimiter(null, no_mathcode);
1722            scan_delimiter(null, no_mathcode);
1723        }
1724        if ((c % delimited_code) == above_code)
1725            scan_normal_dimen();
1726        tex_error("Ambiguous; you need another { and }", hlp);
1727    } else {
1728        incompleat_noad = new_node(fraction_noad, 0);
1729        numerator(incompleat_noad) = new_node(sub_mlist_node, 0);
1730        math_list(numerator(incompleat_noad)) = vlink(head);
1731        vlink(head) = null;
1732        tail = head;
1733        m_style = cramped_style(m_style);
1734
1735        if (c >= delimited_code) {
1736            q = new_node(delim_node, 0);
1737            left_delimiter(incompleat_noad) = q;
1738            q = new_node(delim_node, 0);
1739            right_delimiter(incompleat_noad) = q;
1740            scan_delimiter(left_delimiter(incompleat_noad), no_mathcode);
1741            scan_delimiter(right_delimiter(incompleat_noad), no_mathcode);
1742        }
1743        switch (c % delimited_code) {
1744        case above_code:
1745            scan_normal_dimen();
1746            thickness(incompleat_noad) = cur_val;
1747            break;
1748        case over_code:
1749            thickness(incompleat_noad) = default_code;
1750            break;
1751        case atop_code:
1752            thickness(incompleat_noad) = 0;
1753            break;
1754        }                       /* there are no other cases */
1755    }
1756}
1757
1758
1759
1760@ At the end of a math formula or subformula, the |fin_mlist| routine is
1761called upon to return a pointer to the newly completed mlist, and to
1762pop the nest back to the enclosing semantic level. The parameter to
1763|fin_mlist|, if not null, points to a |fence_noad| that ends the
1764current mlist; this |fence_noad| has not yet been appended.
1765
1766@c
1767pointer fin_mlist(pointer p)
1768{
1769    pointer q;                  /* the mlist to return */
1770    if (incompleat_noad != null) {
1771        if (denominator(incompleat_noad) != null) {
1772            type(denominator(incompleat_noad)) = sub_mlist_node;
1773        } else {
1774            q = new_node(sub_mlist_node, 0);
1775            denominator(incompleat_noad) = q;
1776        }
1777        math_list(denominator(incompleat_noad)) = vlink(head);
1778        if (p == null) {
1779            q = incompleat_noad;
1780        } else {
1781            q = math_list(numerator(incompleat_noad));
1782            if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
1783                || (delim_ptr == null))
1784                confusion("right");     /* this can't happen */
1785            math_list(numerator(incompleat_noad)) = vlink(delim_ptr);
1786            vlink(delim_ptr) = incompleat_noad;
1787            vlink(incompleat_noad) = p;
1788        }
1789    } else {
1790        vlink(tail) = p;
1791        q = vlink(head);
1792    }
1793    pop_nest();
1794    return q;
1795}
1796
1797
1798@ Now at last we're ready to see what happens when a right brace occurs
1799in a math formula. Two special cases are simplified here: Braces are effectively
1800removed when they surround a single Ord without sub/superscripts, or when they
1801surround an accent that is the nucleus of an Ord atom.
1802
1803@c
1804void close_math_group(pointer p)
1805{
1806    int old_style = m_style;
1807    unsave_math();
1808
1809    decr(save_ptr);
1810    assert(saved_type(0) == saved_math);
1811    type(saved_value(0)) = sub_mlist_node;
1812    p = fin_mlist(null);
1813    math_list(saved_value(0)) = p;
1814    if (p != null) {
1815        if (vlink(p) == null) {
1816            if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
1817                if (subscr(p) == null && supscr(p) == null) {
1818                    type(saved_value(0)) = type(nucleus(p));
1819                    if (type(nucleus(p)) == math_char_node) {
1820                        math_fam(saved_value(0)) = math_fam(nucleus(p));
1821                        math_character(saved_value(0)) =
1822                            math_character(nucleus(p));
1823                    } else {
1824                        math_list(saved_value(0)) = math_list(nucleus(p));
1825                        math_list(nucleus(p)) = null;
1826                    }
1827                    delete_attribute_ref(node_attr(saved_value(0)));
1828                    node_attr(saved_value(0)) = node_attr(nucleus(p));
1829                    node_attr(nucleus(p)) = null;
1830                    flush_node(p);
1831                }
1832            } else if (type(p) == accent_noad) {
1833                if (saved_value(0) == nucleus(tail)) {
1834                    if (type(tail) == simple_noad
1835                        && subtype(tail) == ord_noad_type) {
1836                        pointer q = head;
1837                        while (vlink(q) != tail)
1838                            q = vlink(q);
1839                        vlink(q) = p;
1840                        nucleus(tail) = null;
1841                        subscr(tail) = null;
1842                        supscr(tail) = null;
1843                        delete_attribute_ref(node_attr(p));
1844                        node_attr(p) = node_attr(tail);
1845                        node_attr(tail) = null;
1846                        flush_node(tail);
1847                        tail = p;
1848                    }
1849                }
1850            }
1851        }
1852    }
1853    if (vlink(saved_value(0)) > 0) {
1854        pointer q;
1855        q = new_node(math_char_node, 0);
1856        nucleus(vlink(saved_value(0))) = q;
1857        vlink(saved_value(0)) = null;
1858        saved_value(0) = q;
1859        (void) scan_math(saved_value(0), old_style);
1860        /* restart */
1861    }
1862}
1863
1864
1865@ We have dealt with all constructions of math mode except `\.{\\left}' and
1866`\.{\\right}', so the picture is completed by the following sections of
1867the program. The |middle| feature of eTeX allows one ore several \.{\\middle}
1868delimiters to appear between \.{\\left} and \.{\\right}.
1869
1870@c
1871void math_left_right(void)
1872{
1873    halfword t;                 /* |left_noad_side| .. |right_noad_side| */
1874    pointer p;                  /* new noad */
1875    pointer q;                  /* resulting mlist */
1876    pointer r;                  /* temporary */
1877    t = cur_chr;
1878    if ((t != left_noad_side) && (cur_group != math_left_group)) {
1879        if (cur_group == math_shift_group) {
1880            scan_delimiter(null, no_mathcode);
1881            if (t == middle_noad_side) {
1882                const char *hlp[] = {
1883                    "I'm ignoring a \\middle that had no matching \\left.",
1884                    NULL
1885                };
1886                tex_error("Extra \\middle", hlp);
1887            } else {
1888                const char *hlp[] = {
1889                    "I'm ignoring a \\right that had no matching \\left.",
1890                    NULL
1891                };
1892                tex_error("Extra \\right", hlp);
1893            }
1894        } else {
1895            off_save();
1896        }
1897    } else {
1898        p = new_noad();
1899        type(p) = fence_noad;
1900        subtype(p) = (quarterword) t;
1901        r = new_node(delim_node, 0);
1902        delimiter(p) = r;
1903        scan_delimiter(delimiter(p), no_mathcode);
1904        if (t == left_noad_side) {
1905            q = p;
1906        } else {
1907            q = fin_mlist(p);
1908            unsave_math();
1909        }
1910        if (t != right_noad_side) {
1911            push_math(math_left_group, m_style);
1912            vlink(head) = q;
1913            tail = p;
1914            delim_ptr = p;
1915        } else {
1916            tail_append(new_noad());
1917            subtype(tail) = inner_noad_type;
1918            r = new_node(sub_mlist_node, 0);
1919            nucleus(tail) = r;
1920            math_list(nucleus(tail)) = q;
1921        }
1922    }
1923}
1924
1925
1926@ \TeX\ gets to the following part of the program when
1927the first `\.\$' ending a display has been scanned.
1928
1929@c
1930static void check_second_math_shift(void)
1931{
1932    get_x_token();
1933    if (cur_cmd != math_shift_cmd) {
1934        const char *hlp[] = {
1935            "The `$' that I just saw supposedly matches a previous `$$'.",
1936            "So I shall assume that you typed `$$' both times.",
1937            NULL
1938        };
1939        back_error("Display math should end with $$", hlp);
1940    }
1941}
1942
1943static void check_display_math_end(void)
1944{
1945    if (cur_chr != cramped_display_style) {
1946        const char *hlp[] = {
1947            "I shall assume that you typed that.",
1948            NULL
1949        };
1950        tex_error("Display math should end with \\Ustopdisplaymath", hlp);
1951    }
1952}
1953
1954static void check_inline_math_end(void)
1955{
1956    if (cur_chr != cramped_text_style) {
1957        const char *hlp[] = {
1958            "I shall assume that you typed that.",
1959            NULL
1960        };
1961        tex_error("Inline math should end with \\Ustopmath", hlp);
1962    }
1963}
1964
1965@ @c
1966static void resume_after_display(void)
1967{
1968    if (cur_group != math_shift_group)
1969        confusion("display");
1970    unsave_math();
1971    prev_graf = prev_graf + 3;
1972    push_nest();
1973    mode = hmode;
1974    space_factor = 1000;
1975    tail_append(make_local_par_node()); /* this needs to be intercepted in
1976                                           the display math start ! */
1977    get_x_token();
1978    if (cur_cmd != spacer_cmd)
1979        back_input();
1980    if (nest_ptr == 1) {
1981        lua_node_filter_s(buildpage_filter_callback,lua_key_index(after_display));
1982        build_page();
1983    }
1984}
1985
1986
1987@  The fussiest part of math mode processing occurs when a displayed formula is
1988being centered and placed with an optional equation number.
1989
1990
1991At this time we are in vertical mode (or internal vertical mode).
1992
1993  |p| points to the mlist for the formula.
1994  |a| is either |null| or it points to a box containing the equation number.
1995  |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
1996
1997
1998@c
1999static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
2000{
2001    pointer eq_box;             /* box containing the equation */
2002    scaled eq_w;                /* width of the equation */
2003    scaled line_w;              /* width of the line */
2004    scaled eqno_w;              /* width of equation number */
2005    scaled eqno_w2;             /* width of equation number plus space to separate from equation */
2006    scaled line_s;              /* move the line right this much */
2007    scaled d;                   /* displacement of equation in the line */
2008    small_number g1, g2;        /* glue parameter codes for before and after */
2009    pointer r,s;                /* kern nodes used to position the display */
2010    pointer t;                  /* tail of adjustment list */
2011    pointer pre_t;              /* tail of pre-adjustment list */
2012    boolean swap_dir;           /* true if the math and surrounding text dirs are opposed */
2013    swap_dir = (int_par(pre_display_direction_code) < 0 ? true : false );
2014    if (eqno_box != null && swap_dir)
2015        l = !l;
2016
2017    adjust_tail = adjust_head;
2018    pre_adjust_tail = pre_adjust_head;
2019    eq_box = hpack(p, 0, additional, -1);
2020    p = list_ptr(eq_box);
2021    t = adjust_tail;
2022    adjust_tail = null;
2023    pre_t = pre_adjust_tail;
2024    pre_adjust_tail = null;
2025    eq_w = width(eq_box);
2026    line_w = display_width;
2027    line_s = display_indent;
2028    if (eqno_box == null) {
2029        eqno_w = 0;
2030        eqno_w2 = 0;
2031    } else {
2032        eqno_w = width(eqno_box);
2033        eqno_w2 = eqno_w + get_math_quad(text_size);
2034    }
2035    if (eq_w + eqno_w2 > line_w) {
2036        /* The user can force the equation number to go on a separate line
2037           by causing its width to be zero. */
2038        if ((eqno_w != 0)
2039            && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
2040                || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0)
2041                || (total_shrink[fill] != 0)
2042                || (total_shrink[filll] != 0))) {
2043            list_ptr(eq_box) = null;
2044            flush_node(eq_box);
2045            eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
2046        } else {
2047            eqno_w = 0;
2048            if (eq_w > line_w) {
2049                list_ptr(eq_box) = null;
2050                flush_node(eq_box);
2051                eq_box = hpack(p, line_w, exactly, -1);
2052            }
2053        }
2054        eq_w = width(eq_box);
2055    }
2056    /* We try first to center the display without regard to the existence of
2057       the equation number. If that would make it too close (where ``too close''
2058       means that the space between display and equation number is less than the
2059       width of the equation number), we either center it in the remaining space
2060       or move it as far from the equation number as possible. The latter alternative
2061       is taken only if the display begins with glue, since we assume that the
2062       user put glue there to control the spacing precisely.
2063     */
2064    d = half(line_w - eq_w);
2065    if ((eqno_w > 0) && (d < 2 * eqno_w)) {     /* too close */
2066        d = half(line_w - eq_w - eqno_w);
2067        if (p != null)
2068            if (!is_char_node(p))
2069                if (type(p) == glue_node)
2070                    d = 0;
2071    }
2072
2073    tail_append(new_penalty(int_par(pre_display_penalty_code)));
2074    if ((d + line_s <= pre_display_size) || l) {        /* not enough clearance */
2075        g1 = above_display_skip_code;
2076        g2 = below_display_skip_code;
2077    } else {
2078        g1 = above_display_short_skip_code;
2079        g2 = below_display_short_skip_code;
2080    }
2081
2082    /* If the equation number is set on a line by itself, either before or
2083       after the formula, we append an infinite penalty so that no page break will
2084       separate the display from its number; and we use the same size and
2085       displacement for all three potential lines of the display, even though
2086       `\.{\\parshape}' may specify them differently.
2087     */
2088    if (eqno_box && l && (eqno_w == 0)) {   /* \.{\\leqno} on a forced single line due to |width=0| */
2089        /* it follows that |type(a)=hlist_node| */
2090        shift_amount(eqno_box) = line_s;
2091        append_to_vlist(eqno_box);
2092        tail_append(new_penalty(inf_penalty));
2093    } else {
2094        tail_append(new_param_glue(g1));
2095    }
2096
2097    if (eqno_w != 0) {
2098        r = new_kern(line_w - eq_w - eqno_w - d);
2099        s = new_kern(width(r) + eqno_w);
2100        if (l) {
2101            if (swap_dir) {
2102                if (math_direction==dir_TLT) {
2103                    /* TRT + TLT + \eqno,    (swap_dir=true,  math_direction=TLT, l=true)  */
2104                    vlink(eqno_box) = r;
2105                    vlink(r) = eq_box;
2106                    vlink(eq_box) = s;
2107                    eq_box = eqno_box;
2108                } else {
2109                    /* TLT + TRT + \eqno,    (swap_dir=true,  math_direction=TRT, l=true) */
2110                    vlink(eqno_box) = r;
2111                    vlink(r) = eq_box;
2112                    vlink(eq_box) = s;
2113                    eq_box = eqno_box;
2114                }
2115            } else {
2116                if (math_direction==dir_TLT) {
2117                    /* TLT + TLT + \leqno,   (swap_dir=false, math_direction=TLT, l=true) */ /* OK */
2118                    vlink(eqno_box) = r;
2119                    vlink(r) = eq_box;
2120                    vlink(eq_box) = s;
2121                    eq_box = eqno_box;
2122                } else {
2123                    /* TRT + TRT + \leqno,    (swap_dir=false, math_direction=TRT, l=true) */
2124                    vlink(eqno_box) = r;
2125                    vlink(r) = eq_box;
2126                    vlink(eq_box) = s;
2127                    eq_box = eqno_box;
2128                }
2129            }
2130        } else {
2131            if (swap_dir) {
2132                if (math_direction==dir_TLT) {
2133                    /* TRT + TLT + \leqno,   (swap_dir=true,  math_direction=TLT, l=false) */
2134   	            vlink(eq_box) = r;
2135                    vlink(r) = eqno_box;
2136                } else {
2137                    /* TLT + TRT + \leqno,   (swap_dir=true,  math_direction=TRT, l=false) */
2138   	            vlink(eq_box) = r;
2139                    vlink(r) = eqno_box;
2140                }
2141            } else {
2142                if (math_direction==dir_TLT) {
2143                    /*  TLT + TLT + \eqno,    (swap_dir=false, math_direction=TLT, l=false) */ /* OK */
2144                    s = new_kern(d);
2145                    vlink(s) = eq_box;
2146   	            vlink(eq_box) = r;
2147                    vlink(r) = eqno_box;
2148                    eq_box = s;
2149                } else {
2150                    /* TRT + TRT + \eqno,   (swap_dir=false, math_direction=TRT, l=false) */
2151                    vlink(s) = eq_box;
2152   	            vlink(eq_box) = r;
2153                    vlink(r) = eqno_box;
2154                    eq_box = s;
2155                }
2156            }
2157        }
2158        eq_box = hpack(eq_box, 0, additional, -1);
2159        shift_amount(eq_box) = line_s;
2160    } else {
2161        shift_amount(eq_box) = line_s + d;
2162    }
2163    append_to_vlist(eq_box);
2164
2165    if ((eqno_box != null) && (eqno_w == 0) && !l) {
2166        tail_append(new_penalty(inf_penalty));
2167        shift_amount(eqno_box) = line_s;
2168        append_to_vlist(eqno_box);
2169        g2 = 0;
2170    }
2171    if (t != adjust_head) {     /* migrating material comes after equation number */
2172        vlink(tail) = vlink(adjust_head);
2173        tail = t;
2174    }
2175    if (pre_t != pre_adjust_head) {
2176        vlink(tail) = vlink(pre_adjust_head);
2177        tail = pre_t;
2178    }
2179    tail_append(new_penalty(int_par(post_display_penalty_code)));
2180    if (g2 > 0)
2181        tail_append(new_param_glue(g2));
2182
2183    resume_after_display();
2184}
2185
2186@ @c
2187void after_math(void)
2188{
2189    int m;                      /* |mmode| or |-mmode| */
2190    pointer p;                  /* the formula */
2191    pointer a = null;           /* box containing equation number */
2192    boolean l = false;          /* `\.{\\leqno}' instead of `\.{\\eqno}' */
2193    m = mode;
2194    p = fin_mlist(null);        /* this pops the nest */
2195    if (cur_cmd == math_shift_cs_cmd &&
2196        (cur_chr == text_style || cur_chr == display_style)) {
2197        you_cant();
2198    }
2199    if (mode == -m) {           /* end of equation number */
2200        if (cur_cmd == math_shift_cmd) {
2201            check_second_math_shift();
2202        } else {
2203            check_display_math_end();
2204        }
2205        run_mlist_to_hlist(p, text_style, false);
2206        a = hpack(vlink(temp_head), 0, additional, -1);
2207        unsave_math();
2208        decr(save_ptr);         /* now |cur_group=math_shift_group| */
2209        assert(saved_type(0) == saved_eqno);
2210        if (saved_value(0) == 1)
2211            l = true;
2212        m = mode;
2213        p = fin_mlist(null);
2214    }
2215    if (m < 0) {
2216        /* The |unsave| is done after everything else here; hence an appearance of
2217           `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
2218           particular \.\$'s. This is consistent with the conventions of
2219           `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
2220           space above that display.
2221         */
2222        if (cur_cmd == math_shift_cs_cmd) {
2223            check_inline_math_end();
2224        }
2225        tail_append(new_math(math_surround, before));
2226        if (dir_math_save) {
2227            tail_append(new_dir(math_direction));
2228        }
2229        run_mlist_to_hlist(p, text_style, (mode > 0));
2230        vlink(tail) = vlink(temp_head);
2231        while (vlink(tail) != null)
2232            tail = vlink(tail);
2233        if (dir_math_save) {
2234            tail_append(new_dir(math_direction - 64));
2235        }
2236        dir_math_save = false;
2237        tail_append(new_math(math_surround, after));
2238        space_factor = 1000;
2239        unsave_math();
2240    } else {
2241        if (a == null) {
2242            if (cur_cmd == math_shift_cmd) {
2243                check_second_math_shift();
2244            } else {
2245                check_display_math_end();
2246            }
2247        }
2248        run_mlist_to_hlist(p, display_style, false);
2249        finish_displayed_math(l, a, vlink(temp_head));
2250    }
2251}
2252
2253
2254@ When \.{\\halign} appears in a display, the alignment routines operate
2255essentially as they do in vertical mode. Then the following program is
2256activated, with |p| and |q| pointing to the beginning and end of the
2257resulting list, and with |aux_save| holding the |prev_depth| value.
2258
2259@c
2260void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
2261{
2262    do_assignments();
2263    if (cur_cmd == math_shift_cmd) {
2264        check_second_math_shift();
2265    } else {
2266        check_display_math_end();
2267    }
2268    pop_nest();
2269    tail_append(new_penalty(int_par(pre_display_penalty_code)));
2270    tail_append(new_param_glue(above_display_skip_code));
2271    vlink(tail) = p;
2272    if (p != null)
2273        tail = q;
2274    tail_append(new_penalty(int_par(post_display_penalty_code)));
2275    tail_append(new_param_glue(below_display_skip_code));
2276    cur_list.prev_depth_field = saved_prevdepth;
2277    resume_after_display();
2278}
2279
2280@ Interface to \.{\\Umath} and \.{\\mathstyle}
2281
2282@c
2283void setup_math_style(void)
2284{
2285    pointer q;
2286    tail_append(new_noad());
2287    q = new_node(math_char_node, 0);
2288    nucleus(tail) = q;
2289    (void) scan_math(nucleus(tail), num_style(m_style));
2290}
2291
2292
2293@ @c
2294void print_math_style(void)
2295{
2296    if (abs(mode) == mmode)
2297        print_int(m_style);
2298    else
2299        print_int(-1);
2300}
2301