fix getsup (HH)
[luatex.git] / source / texk / web2c / luatexdir / tex / texmath.w
blob2f7fe90689bbaf3fa06b1e1cc8e746f7b792f31a
1 % texmath.w
3 % Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
5 % This file is part of LuaTeX.
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.
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.
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/>.
20 @ @c
21 #include "ptexlib.h"
23 @ @c
24 #define mode mode_par
25 #define tail tail_par
26 #define head head_par
27 #define dir_save dirs_par
31 \mathdisplayskipmode
33 tex normally always inserts before and only after when larger than zero
35 0 = normal tex
36 1 = always
37 2 = non-zero
38 3 = ignore
42 @ TODO: not sure if this is the right order
44 #define back_error(A,B) do { \
45 OK_to_interrupt=false; \
46 back_input(); \
47 OK_to_interrupt=true; \
48 tex_error(A,B); \
49 } while (0)
51 @ @c
52 int scan_math(pointer, int);
53 int scan_math_style(pointer, int);
54 pointer fin_mlist(pointer);
56 @ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
57 {\sl mlist}, which is essentially a tree structure representing that
58 formula. An mlist is a linear sequence of items, but we can regard it as
59 a tree structure because mlists can appear within mlists. For example, many
60 of the entries can be subscripted or superscripted, and such ``scripts''
61 are mlists in their own right.
63 An entire formula is parsed into such a tree before any of the actual
64 typesetting is done, because the current style of type is usually not
65 known until the formula has been fully scanned. For example, when the
66 formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
67 that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
69 During the scanning process, each element of the mlist being built is
70 classified as a relation, a binary operator, an open parenthesis, etc.,
71 or as a construct like `\.{\\sqrt}' that must be built up. This classification
72 appears in the mlist data structure.
74 After a formula has been fully scanned, the mlist is converted to an hlist
75 so that it can be incorporated into the surrounding text. This conversion is
76 controlled by a recursive procedure that decides all of the appropriate
77 styles by a ``top-down'' process starting at the outermost level and working
78 in towards the subformulas. The formula is ultimately pasted together using
79 combinations of horizontal and vertical boxes, with glue and penalty nodes
80 inserted as necessary.
82 An mlist is represented internally as a linked list consisting chiefly
83 of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
84 similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
85 allowed to appear in mlists together with the noads; \TeX\ tells the difference
86 by means of the |type| field, since a noad's |type| is always greater than
87 that of a node. An mlist does not contain character nodes, hlist nodes, vlist
88 nodes, math nodes or unset nodes; in particular, each mlist item appears in the
89 variable-size part of |mem|, so the |type| field is always present.
91 Each noad is five or more words long. The first word contains the
92 |type| and |subtype| and |link| fields that are already so familiar to
93 us; the second contains the attribute list pointer, and the third,
94 fourth an fifth words are called the noad's |nucleus|, |subscr|, and
95 |supscr| fields. (This use of a combined attribute list is temporary.
96 Eventually, each of fields need their own list)
98 Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
99 parsed into an mlist containing a single element called an |ord_noad|.
100 The |nucleus| of this noad is a representation of `\.x', the |subscr| is
101 empty, and the |supscr| is a representation of `\.2'.
103 The |nucleus|, |subscr|, and |supscr| fields are further broken into
104 subfields. If |p| points to a noad, and if |q| is one of its principal
105 fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the
106 corresponding attribute of noad |p| is not present). Otherwise, there are
107 several possibilities for the subfields, depending on the |type| of |q|.
109 \yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of
110 the sixteen font families, and |character(q)| is the number of a character
111 within a font of that family, as in a character node.
113 \yskip\hang|type(q)=math_text_char_node| is similar, but the character is
114 unsubscripted and unsuperscripted and it is followed immediately by another
115 character from the same font. (This |type| setting appears only
116 briefly during the processing; it is used to suppress unwanted italic
117 corrections.)
119 \yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box
120 node (either an |hlist_node| or a |vlist_node|) that should be used as the
121 value of the field. The |shift_amount| in the subsidiary box node is the
122 amount by which that box will be shifted downward.
124 \yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to
125 an mlist; the mlist must be converted to an hlist in order to obtain
126 the value of this field.
128 \yskip\noindent In the latter case, we might have |math_list(q)=null|. This
129 is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}'
130 and `\.{\$P\$}' produce different results (the former will not have the
131 ``italic correction'' added to the width of |P|, but the ``script skip''
132 will be added).
135 static void unsave_math(void)
137 unsave();
138 decr(save_ptr);
139 flush_node_list(text_dir_ptr);
140 assert(saved_type(0) == saved_textdir);
141 text_dir_ptr = saved_value(0);
144 @ Sometimes it is necessary to destroy an mlist. The following
145 subroutine empties the current list, assuming that |abs(mode)=mmode|.
148 void flush_math(void)
150 flush_node_list(vlink(head));
151 flush_node_list(incompleat_noad_par);
152 vlink(head) = null;
153 tail = head;
154 incompleat_noad_par = null;
157 @ Before we can do anything in math mode, we need fonts.
160 #define MATHFONTSTACK 8
161 #define MATHFONTDEFAULT 0 /* == nullfont */
163 static sa_tree math_fam_head = NULL;
165 @ @c
166 int fam_fnt(int fam_id, int size_id)
168 int n = fam_id + (256 * size_id);
169 return (int) get_sa_item(math_fam_head, n).int_value;
172 void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
174 int n = fam_id + (256 * size_id);
175 sa_tree_item sa_value = { 0 };
176 sa_value.int_value = f;
177 set_sa_item(math_fam_head, n, sa_value, lvl);
178 fixup_math_parameters(fam_id, size_id, f, lvl);
179 if (tracing_assigns_par > 1) {
180 begin_diagnostic();
181 tprint("{assigning");
182 print_char(' ');
183 print_cmd_chr(def_family_cmd, size_id);
184 print_int(fam_id);
185 print_char('=');
186 print_font_identifier(fam_fnt(fam_id, size_id));
187 print_char('}');
188 end_diagnostic(false);
192 @ @c
193 static void unsave_math_fam_data(int gl)
195 sa_stack_item st;
196 if (math_fam_head->stack == NULL)
197 return;
198 while (math_fam_head->stack_ptr > 0 &&
199 abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
200 >= (int) gl) {
201 st = math_fam_head->stack[math_fam_head->stack_ptr];
202 if (st.level > 0) {
203 rawset_sa_item(math_fam_head, st.code, st.value);
204 /* now do a trace message, if requested */
205 if (tracing_restores_par > 1) {
206 int size_id = st.code / 256;
207 int fam_id = st.code % 256;
208 begin_diagnostic();
209 tprint("{restoring");
210 print_char(' ');
211 print_cmd_chr(def_family_cmd, size_id);
212 print_int(fam_id);
213 print_char('=');
214 print_font_identifier(fam_fnt(fam_id, size_id));
215 print_char('}');
216 end_diagnostic(false);
219 (math_fam_head->stack_ptr)--;
223 @ and parameters
226 #define MATHPARAMSTACK 8
227 #define MATHPARAMDEFAULT undefined_math_parameter
229 static sa_tree math_param_head = NULL;
231 @ @c
232 void def_math_param(int param_id, int style_id, scaled value, int lvl)
234 int n = param_id + (256 * style_id);
235 sa_tree_item sa_value = { 0 };
236 sa_value.int_value = (int) value;
237 set_sa_item(math_param_head, n, sa_value, lvl);
238 if (tracing_assigns_par > 1) {
239 begin_diagnostic();
240 tprint("{assigning");
241 print_char(' ');
242 print_cmd_chr(set_math_param_cmd, param_id);
243 print_cmd_chr(math_style_cmd, style_id);
244 print_char('=');
245 print_int(value);
246 print_char('}');
247 end_diagnostic(false);
251 scaled get_math_param(int param_id, int style_id)
253 int n = param_id + (256 * style_id);
254 return (scaled) get_sa_item(math_param_head, n).int_value;
257 @ @c
258 static void unsave_math_param_data(int gl)
260 sa_stack_item st;
261 if (math_param_head->stack == NULL)
262 return;
263 while (math_param_head->stack_ptr > 0 &&
264 abs(math_param_head->stack[math_param_head->stack_ptr].level)
265 >= (int) gl) {
266 st = math_param_head->stack[math_param_head->stack_ptr];
267 if (st.level > 0) {
268 rawset_sa_item(math_param_head, st.code, st.value);
269 /* now do a trace message, if requested */
270 if (tracing_restores_par > 1) {
271 int param_id = st.code % 256;
272 int style_id = st.code / 256;
273 begin_diagnostic();
274 tprint("{restoring");
275 print_char(' ');
276 print_cmd_chr(set_math_param_cmd, param_id);
277 print_cmd_chr(math_style_cmd, style_id);
278 print_char('=');
279 print_int(get_math_param(param_id, style_id));
280 print_char('}');
281 end_diagnostic(false);
284 (math_param_head->stack_ptr)--;
288 @ saving and unsaving of both
291 void unsave_math_data(int gl)
293 unsave_math_fam_data(gl);
294 unsave_math_param_data(gl);
297 @ Dumping and undumping
299 void dump_math_data(void)
301 sa_tree_item sa_value = { 0 };
302 if (math_fam_head == NULL) {
303 sa_value.int_value = MATHFONTDEFAULT;
304 math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
306 dump_sa_tree(math_fam_head, "mathfonts");
307 if (math_param_head == NULL) {
308 sa_value.int_value = MATHPARAMDEFAULT;
309 math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
311 dump_sa_tree(math_param_head, "mathparameters");
314 void undump_math_data(void)
316 math_fam_head = undump_sa_tree("mathfonts");
317 math_param_head = undump_sa_tree("mathparameters");
320 @ @c
321 void initialize_math(void)
323 sa_tree_item sa_value = { 0 };
324 if (math_fam_head == NULL) {
325 sa_value.int_value = MATHFONTDEFAULT;
326 math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
328 if (math_param_head == NULL) {
329 sa_value.int_value = MATHPARAMDEFAULT;
330 math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
331 initialize_math_spacing();
333 return;
336 @ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
337 Clo, Pun, or Inn, for purposes of spacing and line breaking. An
338 |ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
339 |punct_noad|, or |inner_noad| is used to represent portions of the various
340 types. For example, an `\.=' sign in a formula leads to the creation of a
341 |rel_noad| whose |nucleus| field is a representation of an equals sign
342 (usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel}
343 also results in a |rel_noad|. When a |rel_noad| is followed by an
344 |op_noad|, say, and possibly separated by one or more ordinary nodes (not
345 noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
346 just after the formula that corresponds to the |rel_noad|, unless there
347 already was a penalty immediately following; and a ``thick space'' will be
348 inserted just before the formula that corresponds to the |op_noad|.
350 A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
351 has a |subtype=normal|. The only exception is that an |op_noad| might
352 have |subtype=limits| or |no_limits|, if the normal positioning of
353 limits has been overridden for this operator.
355 A |radical_noad| also has a |left_delimiter| field, which usually
356 represents a square root sign.
358 A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
360 Delimiter fields have four subfields
361 called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
362 represent variable-size delimiters by giving the ``small'' and ``large''
363 starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
364 @:TeXbook}{\sl The \TeX book@>
366 A |fraction_noad| is actually quite different from all other noads.
367 It has |thickness|, |denominator|, and |numerator| fields instead of
368 |nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value
369 that tells how thick to make a fraction rule; however, the special
370 value |default_code| is used to stand for the
371 |default_rule_thickness| of the current size. The |numerator| and
372 |denominator| point to mlists that define a fraction; we always have
373 $$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The
374 |left_delimiter| and |right_delimiter| fields specify delimiters that will
375 be placed at the left and right of the fraction. In this way, a
376 |fraction_noad| is able to represent all of \TeX's operators \.{\\over},
377 \.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
378 \.{\\abovewithdelims}.
380 @ The |new_noad| function creates an |ord_noad| that is completely null
383 pointer new_noad(void)
385 pointer p;
386 p = new_node(simple_noad, ord_noad_type);
387 /* all noad fields are zero after this */
388 return p;
391 @ @c
392 pointer new_sub_box(pointer curbox)
394 pointer p, q;
395 p = new_noad();
396 q = new_node(sub_box_node, 0);
397 nucleus(p) = q;
398 math_list(nucleus(p)) = curbox;
399 return p;
402 @ A few more kinds of noads will complete the set: An |under_noad| has its
403 nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
404 an accent over its nucleus; the accent character appears as
405 |math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad|
406 centers its nucleus vertically with respect to the axis of the formula;
407 in such noads we always have |type(nucleus(p))=sub_box|.
409 And finally, we have the |fence_noad| type, to implement
410 \TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}.
411 The |nucleus| of such noads is
412 replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
413 a |fence_noad| such that |delimiter(p)| holds the family and character
414 codes for all left parentheses. A |fence_noad| of subtype |left_noad_side|
415 never appears in an mlist except as the first element, and a |fence_noad|
416 with subtype |right_noad_side| never appears in an mlist
417 except as the last element; furthermore, we either have both a |left_noad_side|
418 and a |right_noad_side|, or neither one is present.
420 @ Math formulas can also contain instructions like \.{\\textstyle} that
421 override \TeX's normal style rules. A |style_node| is inserted into the
422 data structure to record such instructions; it is three words long, so it
423 is considered a node instead of a noad. The |subtype| is either |display_style|
424 or |text_style| or |script_style| or |script_script_style|. The
425 second and third words of a |style_node| are not used, but they are
426 present because a |choice_node| is converted to a |style_node|.
428 \TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
429 |display_style|, \dots, |script_script_style|, and adds~1 to get the
430 ``cramped'' versions of these styles. This gives a numerical order that
431 is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
432 i.e., a smaller style has a larger numerical value.
433 @:TeXbook}{\sl The \TeX book@>
436 const char *math_style_names[] = {
437 "display", "crampeddisplay",
438 "text", "crampedtext",
439 "script", "crampedscript",
440 "scriptscript", "crampedscriptscript",
441 NULL
444 const char *math_param_names[] = {
445 "quad", "axis", "operatorsize",
446 "overbarkern", "overbarrule", "overbarvgap",
447 "underbarkern", "underbarrule", "underbarvgap",
448 "radicalkern", "radicalrule", "radicalvgap",
449 "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
450 "stackvgap", "stacknumup", "stackdenomdown",
451 "fractionrule", "fractionnumvgap", "fractionnumup",
452 "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
453 "limitabovevgap", "limitabovebgap", "limitabovekern",
454 "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
455 "nolimitsubfactor", "nolimitsupfactor", /* bonus */
456 "underdelimitervgap", "underdelimiterbgap",
457 "overdelimitervgap", "overdelimiterbgap",
458 "subshiftdrop", "supshiftdrop", "subshiftdown",
459 "subsupshiftdown", "subtopmax", "supshiftup",
460 "supbottommin", "supsubbottommax", "subsupvgap",
461 "spaceafterscript", "connectoroverlapmin",
462 "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
463 "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
464 "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
465 "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
466 "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
467 "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
468 "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
469 "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
470 "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
471 "openopenspacing", "openclosespacing", "openpunctspacing",
472 "openinnerspacing",
473 "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
474 "closeopenspacing", "closeclosespacing", "closepunctspacing",
475 "closeinnerspacing",
476 "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
477 "punctopenspacing", "punctclosespacing", "punctpunctspacing",
478 "punctinnerspacing",
479 "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
480 "inneropenspacing", "innerclosespacing", "innerpunctspacing",
481 "innerinnerspacing",
482 NULL
485 @ @c
486 pointer new_style(small_number s)
487 { /* create a style node */
488 m_style = s;
489 return new_node(style_node, s);
492 @ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
493 has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
494 and |script_script_mlist| pointing to the mlists for each style.
497 static pointer new_choice(void)
498 { /* create a choice node */
499 return new_node(choice_node, 0); /* the |subtype| is not used */
502 @ Let's consider now the previously unwritten part of |show_node_list|
503 that displays the things that can only be present in mlists; this
504 program illustrates how to access the data structures just defined.
506 In the context of the following program, |p| points to a node or noad that
507 should be displayed, and the current string contains the ``recursion history''
508 that leads to this point. The recursion history consists of a dot for each
509 outer level in which |p| is subsidiary to some node, or in which |p| is
510 subsidiary to the |nucleus| field of some noad; the dot is replaced by
511 `\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
512 or |supscr| or |denominator| or |numerator| fields of noads. For example,
513 the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
514 |x| in the (ridiculous) formula
515 `\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
518 void display_normal_noad(pointer p); /* forward */
519 void display_fence_noad(pointer p); /* forward */
520 void display_fraction_noad(pointer p); /* forward */
522 void show_math_node(pointer p)
524 switch (type(p)) {
525 case style_node:
526 print_cmd_chr(math_style_cmd, subtype(p));
527 break;
528 case choice_node:
529 tprint_esc("mathchoice");
530 append_char('D');
531 show_node_list(display_mlist(p));
532 flush_char();
533 append_char('T');
534 show_node_list(text_mlist(p));
535 flush_char();
536 append_char('S');
537 show_node_list(script_mlist(p));
538 flush_char();
539 append_char('s');
540 show_node_list(script_script_mlist(p));
541 flush_char();
542 break;
543 case simple_noad:
544 case radical_noad:
545 case accent_noad:
546 display_normal_noad(p);
547 break;
548 case fence_noad:
549 display_fence_noad(p);
550 break;
551 case fraction_noad:
552 display_fraction_noad(p);
553 break;
554 default:
555 tprint("Unknown node type!");
556 break;
560 @ Here are some simple routines used in the display of noads.
563 static void print_fam_and_char(pointer p)
564 { /* prints family and character */
565 tprint_esc("fam");
566 print_int(math_fam(p));
567 print_char(' ');
568 print(math_character(p));
571 @ @c
572 static void print_delimiter(pointer p)
574 int a;
575 if (delimiteroptionset(p)) {
576 tprint(" [ ");
577 if (delimiteraxis(p))
578 tprint("axis ");
579 if (delimiternoaxis(p))
580 tprint("noaxis ");
581 if (delimiterexact(p))
582 tprint("exact ");
583 tprint("]");
585 if (delimiterheight(p)) {
586 tprint("height=");
587 print_scaled(delimiterheight(p));
588 tprint(" ");
590 if (delimiterdepth(p)) {
591 tprint("depth=");
592 print_scaled(delimiterdepth(p));
593 tprint(" ");
595 if (delimiterclass(p)) {
596 tprint("class=");
597 print_int(delimiterclass(p));
598 tprint(" ");
600 if (small_fam(p) < 0) {
601 print_int(-1); /* this should never happen */
602 } else if (small_fam(p) < 16 && large_fam(p) < 16 &&
603 small_char(p) < 256 && large_char(p) < 256) {
604 /* traditional tex style */
605 a = small_fam(p) * 256 + small_char(p);
606 a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
607 print_hex(a);
608 } else if ((large_fam(p) == 0 && large_char(p) == 0) ||
609 small_char(p) > 65535 || large_char(p) > 65535) {
610 /* modern xetex/luatex style */
611 print_hex(small_fam(p));
612 print_hex(small_char(p));
616 @ The next subroutine will descend to another level of recursion when a
617 subsidiary mlist needs to be displayed. The parameter |c| indicates what
618 character is to become part of the recursion history. An empty mlist is
619 distinguished from a missing field, because these are not equivalent
620 (as explained above).
621 @^recursion@>
624 static void print_subsidiary_data(pointer p, ASCII_code c)
625 { /* display a noad field */
626 if ((int) cur_length >= depth_threshold) {
627 if (p != null)
628 tprint(" []");
629 } else {
630 append_char(c); /* include |c| in the recursion history */
631 if (p != null) {
632 switch (type(p)) {
633 case math_char_node:
634 print_ln();
635 print_current_string();
636 print_fam_and_char(p);
637 break;
638 case sub_box_node:
639 show_node_list(math_list(p));
640 break;
641 case sub_mlist_node:
642 if (math_list(p) == null) {
643 print_ln();
644 print_current_string();
645 tprint("{}");
646 } else {
647 show_node_list(math_list(p));
649 break;
652 flush_char(); /* remove |c| from the recursion history */
656 @ @c
657 void display_normal_noad(pointer p)
659 switch (type(p)) {
660 case simple_noad:
661 switch (subtype(p)) {
662 case ord_noad_type:
663 tprint_esc("mathord");
664 break;
665 case op_noad_type_normal:
666 case op_noad_type_limits:
667 case op_noad_type_no_limits:
668 tprint_esc("mathop");
669 if (subtype(p) == op_noad_type_limits)
670 tprint_esc("limits");
671 else if (subtype(p) == op_noad_type_no_limits)
672 tprint_esc("nolimits");
673 break;
674 case bin_noad_type:
675 tprint_esc("mathbin");
676 break;
677 case rel_noad_type:
678 tprint_esc("mathrel");
679 break;
680 case open_noad_type:
681 tprint_esc("mathopen");
682 break;
683 case close_noad_type:
684 tprint_esc("mathclose");
685 break;
686 case punct_noad_type:
687 tprint_esc("mathpunct");
688 break;
689 case inner_noad_type:
690 tprint_esc("mathinner");
691 break;
692 case over_noad_type:
693 tprint_esc("overline");
694 break;
695 case under_noad_type:
696 tprint_esc("underline");
697 break;
698 case vcenter_noad_type:
699 tprint_esc("vcenter");
700 break;
701 default:
702 tprint("<unknown noad type!>");
703 break;
705 break;
706 case radical_noad:
707 if (subtype(p) == 6)
708 tprint_esc("Udelimiterover");
709 else if (subtype(p) == 5)
710 tprint_esc("Udelimiterunder");
711 else if (subtype(p) == 4)
712 tprint_esc("Uoverdelimiter");
713 else if (subtype(p) == 3)
714 tprint_esc("Uunderdelimiter");
715 else if (subtype(p) == 2)
716 tprint_esc("Uroot");
717 else
718 tprint_esc("radical");
719 print_delimiter(left_delimiter(p));
720 if (degree(p) != null) {
721 print_subsidiary_data(degree(p), '/');
723 if (radicalwidth(p)) {
724 tprint("width=");
725 print_scaled(radicalwidth(p));
726 tprint(" ");
728 if (radicaloptionset(p)) {
729 tprint(" [ ");
730 if (radicalexact(p))
731 tprint("exact ");
732 if (radicalleft(p))
733 tprint("left ");
734 if (radicalmiddle(p))
735 tprint("middle ");
736 if (radicalright(p))
737 tprint("right ");
738 tprint("]");
740 break;
741 case accent_noad:
742 if (top_accent_chr(p) != null) {
743 if (bot_accent_chr(p) != null) {
744 tprint_esc("Umathaccent both");
745 } else {
746 tprint_esc("Umathaccent");
748 } else if (bot_accent_chr(p) != null) {
749 tprint_esc("Umathaccent bottom");
750 } else {
751 tprint_esc("Umathaccent overlay");
753 if (accentfraction(p)) {
754 tprint(" fraction=");
755 print_int(accentfraction(p));
756 tprint(" ");
758 switch (subtype(p)) {
759 case 0:
760 if (top_accent_chr(p) != null) {
761 if (bot_accent_chr(p) != null) {
762 print_fam_and_char(top_accent_chr(p));
763 print_fam_and_char(bot_accent_chr(p));
764 } else {
765 print_fam_and_char(top_accent_chr(p));
767 } else if (bot_accent_chr(p) != null) {
768 print_fam_and_char(bot_accent_chr(p));
769 } else {
770 print_fam_and_char(overlay_accent_chr(p));
772 break;
773 case 1:
774 if (top_accent_chr(p) != null) {
775 tprint(" fixed ");
776 print_fam_and_char(top_accent_chr(p));
777 if (bot_accent_chr(p) != null) {
778 print_fam_and_char(bot_accent_chr(p));
780 } else {
781 confusion("display_accent_noad");
783 break;
784 case 2:
785 if (bot_accent_chr(p) != null) {
786 if (top_accent_chr(p) != null) {
787 print_fam_and_char(top_accent_chr(p));
789 tprint(" fixed ");
790 print_fam_and_char(bot_accent_chr(p));
791 } else{
792 confusion("display_accent_noad");
794 break;
795 case 3:
796 if (top_accent_chr(p) != null && bot_accent_chr(p) != null) {
797 tprint(" fixed ");
798 print_fam_and_char(top_accent_chr(p));
799 tprint(" fixed ");
800 print_fam_and_char(bot_accent_chr(p));
801 } else {
802 confusion("display_accent_noad");
804 break;
806 break;
808 print_subsidiary_data(nucleus(p), '.');
809 print_subsidiary_data(supscr(p), '^');
810 print_subsidiary_data(subscr(p), '_');
813 @ @c
814 void display_fence_noad(pointer p)
816 if (subtype(p) == right_noad_side)
817 tprint_esc("right");
818 else if (subtype(p) == left_noad_side)
819 tprint_esc("left");
820 else
821 tprint_esc("middle");
822 print_delimiter(delimiter(p));
825 @ @c
826 void display_fraction_noad(pointer p)
828 tprint_esc("fraction, thickness ");
829 if (thickness(p) == default_code)
830 tprint("= default");
831 else
832 print_scaled(thickness(p));
833 if ((left_delimiter(p) != null) &&
834 ((small_fam(left_delimiter(p)) != 0) ||
835 (small_char(left_delimiter(p)) != 0) ||
836 (large_fam(left_delimiter(p)) != 0) ||
837 (large_char(left_delimiter(p)) != 0))) {
838 tprint(", left-delimiter ");
839 print_delimiter(left_delimiter(p));
841 if ((right_delimiter(p) != null) &&
842 ((small_fam(right_delimiter(p)) != 0) ||
843 (small_char(right_delimiter(p)) != 0) ||
844 (large_fam(right_delimiter(p)) != 0) ||
845 (large_char(right_delimiter(p)) != 0))) {
846 tprint(", right-delimiter ");
847 print_delimiter(right_delimiter(p));
849 print_subsidiary_data(numerator(p), '\\');
850 print_subsidiary_data(denominator(p), '/');
853 @ The routines that \TeX\ uses to create mlists are similar to those we have
854 just seen for the generation of hlists and vlists. But it is necessary to
855 make ``noads'' as well as nodes, so the reader should review the
856 discussion of math mode data structures before trying to make sense out of
857 the following program.
859 Here is a little routine that needs to be done whenever a subformula
860 is about to be processed. The parameter is a code like |math_group|.
863 static void new_save_level_math(group_code c)
865 set_saved_record(0, saved_textdir, 0, text_dir_ptr);
866 text_dir_ptr = new_dir(math_direction_par);
867 incr(save_ptr);
868 new_save_level(c);
869 eq_word_define(int_base + body_direction_code, math_direction_par);
870 eq_word_define(int_base + par_direction_code, math_direction_par);
871 eq_word_define(int_base + text_direction_code, math_direction_par);
874 @ @c
875 static void push_math(group_code c, int mstyle)
877 if (math_direction_par != text_direction_par)
878 dir_math_save = true;
879 push_nest();
880 mode = -mmode;
881 incompleat_noad_par = null;
882 m_style = mstyle;
883 new_save_level_math(c);
886 @ @c
887 static void enter_ordinary_math(void)
889 push_math(math_shift_group, text_style);
890 eq_word_define(int_base + cur_fam_code, -1);
891 if (every_math_par != null)
892 begin_token_list(every_math_par, every_math_text);
895 @ @c
896 void enter_display_math(void);
898 @ We get into math mode from horizontal mode when a `\.\$' (i.e., a
899 |math_shift| character) is scanned. We must check to see whether this
900 `\.\$' is immediately followed by another, in case display math mode is
901 called for.
904 void init_math(void)
906 if (cur_cmd == math_shift_cmd) {
907 get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
908 if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
909 enter_display_math();
910 } else {
911 back_input();
912 enter_ordinary_math();
914 } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) {
915 enter_display_math();
916 } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
917 enter_ordinary_math();
918 } else {
919 you_cant();
923 @ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
924 `\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
925 the value of |cur_chr| is placed onto |save_stack| for safe keeping.
927 @ When \TeX\ is in display math mode, |cur_group=math_shift_group|,
928 so it is not necessary for the |start_eq_no| procedure to test for
929 this condition.
932 void start_eq_no(void)
934 set_saved_record(0, saved_eqno, 0, cur_chr);
935 incr(save_ptr);
936 enter_ordinary_math();
939 @ Subformulas of math formulas cause a new level of math mode to be entered,
940 on the semantic nest as well as the save stack. These subformulas arise in
941 several ways: (1)~A left brace by itself indicates the beginning of a
942 subformula that will be put into a box, thereby freezing its glue and
943 preventing line breaks. (2)~A subscript or superscript is treated as a
944 subformula if it is not a single character; the same applies to
945 the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
946 initiates a subformula that will be terminated by a matching \.{\\right}.
947 The group codes placed on |save_stack| in these three cases are
948 |math_group|, |math_group|, and |math_left_group|, respectively.
950 Here is the code that handles case (1); the other cases are not quite as
951 trivial, so we shall consider them later.
954 void math_left_brace(void)
956 pointer q;
957 tail_append(new_noad());
958 q = new_node(math_char_node, 0);
959 nucleus(tail) = q;
960 back_input();
961 (void) scan_math(nucleus(tail), m_style);
964 @ If the inline directions of \.{\\pardir} and \.{\\mathdir} are
965 opposite, then this function will return true. Discovering that fact
966 is somewhat odd because it needs traversal of the |save_stack|.
967 The occurance of displayed equations is weird enough that this is
968 probably still better than having yet another field in the |input_stack|
969 structures.
971 None of this makes much sense if the inline direction of either one of
972 \.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current
973 math machinery is ill suited anyway so I do not bother to test that.
976 static boolean math_and_text_reversed_p(void)
978 int i = save_ptr - 1;
979 while (save_type(i) != level_boundary)
980 i--;
981 while (i < save_ptr) {
982 if (save_type(i) == restore_old_value &&
983 save_value(i) == int_base + par_direction_code) {
984 if (textdir_opposite(math_direction_par, save_value(i - 1)))
985 return true;
987 i++;
989 return false;
992 @ When we enter display math mode, we need to call |line_break| to
993 process the partial paragraph that has just been interrupted by the
994 display. Then we can set the proper values of |display_width| and
995 |display_indent| and |pre_display_size|.
998 void enter_display_math(void)
1000 scaled w; /* new or partial |pre_display_size| */
1001 scaled l; /* new |display_width| */
1002 scaled s; /* new |display_indent| */
1003 pointer p;
1004 int n; /* scope of paragraph shape specification */
1005 if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
1006 (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
1007 type(tail) == local_par_node && vlink(tail) == null)) {
1008 if (vlink(head) == tail) {
1009 /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
1010 there is another display immediately following, we have to get rid
1011 of that node */
1012 flush_node(tail);
1014 pop_nest();
1015 w = -max_dimen;
1016 } else {
1017 line_break(true, math_shift_group);
1018 w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par);
1020 /* now we are in vertical mode, working on the list that will contain the display */
1021 /* A displayed equation is considered to be three lines long, so we
1022 calculate the length and offset of line number |prev_graf+2|. */
1023 if (par_shape_par_ptr == null) {
1024 if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) {
1025 halfword used_hang_indent = swap_hang_indent(hang_indent_par);
1026 l = hsize_par - abs(used_hang_indent);
1027 if (used_hang_indent > 0)
1028 s = used_hang_indent;
1029 else
1030 s = 0;
1031 } else {
1032 l = hsize_par;
1033 s = 0;
1035 } else {
1036 n = vinfo(par_shape_par_ptr + 1);
1037 if (prev_graf_par + 2 >= n)
1038 p = par_shape_par_ptr + 2 * n + 1;
1039 else
1040 p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1;
1041 s = varmem[(p - 1)].cint;
1042 l = varmem[p].cint;
1043 s = swap_parshape_indent(s,l);
1046 push_math(math_shift_group, display_style);
1047 mode = mmode;
1048 eq_word_define(int_base + cur_fam_code, -1);
1049 eq_word_define(dimen_base + pre_display_size_code, w);
1050 eq_word_define(dimen_base + display_width_code, l);
1051 eq_word_define(dimen_base + display_indent_code, s);
1052 eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0));
1053 if (every_display_par != null)
1054 begin_token_list(every_display_par, every_display_text);
1055 if (nest_ptr == 1) {
1056 checked_page_filter(before_display);
1057 build_page();
1061 @ The next routine parses all variations of a delimiter code. The |extcode|
1062 tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the
1063 |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.).
1064 (the class is passed on for conversion to \.{\\mathchar}).
1067 static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
1069 const char *hlp[] = {
1070 "I'm going to use 0 instead of that illegal code value.",
1071 NULL
1073 delcodeval d;
1074 int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
1075 if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */
1076 scan_int();
1077 /* "MFCCFCC or "FCCFCC */
1078 if (doclass) {
1079 mcls = (cur_val / 0x1000000);
1080 cur_val = (cur_val & 0xFFFFFF);
1082 if (cur_val > 0xFFFFFF) {
1083 tex_error("Invalid delimiter code", hlp);
1084 cur_val = 0;
1086 msfam = (cur_val / 0x100000);
1087 mschr = (cur_val % 0x100000) / 0x1000;
1088 mlfam = (cur_val & 0xFFF) / 0x100;
1089 mlchr = (cur_val % 0x100);
1090 } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */
1091 /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */
1092 if (doclass) {
1093 scan_int();
1094 mcls = cur_val;
1096 scan_int();
1097 msfam = cur_val;
1098 scan_char_num();
1099 mschr = cur_val;
1100 if (msfam < 0 || msfam > 255) {
1101 tex_error("Invalid delimiter code", hlp);
1102 msfam = 0;
1103 mschr = 0;
1105 mlfam = 0;
1106 mlchr = 0;
1107 } else if (extcode == umathnum_mathcode) { /* \.{\\Udelcodenum} */
1108 /* "FF<21bits> */
1109 /* the largest numeric value is $2^29-1$, but
1110 the top of bit 21 can't be used as it contains invalid USV's
1112 if (doclass) { /* such a primitive doesn't exist */
1113 confusion("umathnum_mathcode");
1115 scan_int();
1116 msfam = (cur_val / 0x200000);
1117 mschr = cur_val & 0x1FFFFF;
1118 if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
1119 tex_error("Invalid delimiter code", hlp);
1120 msfam = 0;
1121 mschr = 0;
1123 mlfam = 0;
1124 mlchr = 0;
1125 } else {
1126 /* something's gone wrong */
1127 confusion("unknown_extcode");
1129 d.class_value = mcls;
1130 d.small_family_value = msfam;
1131 d.small_character_value = mschr;
1132 d.large_family_value = mlfam;
1133 d.large_character_value = mlchr;
1134 return d;
1137 @ @c
1138 void scan_extdef_del_code(int level, int extcode)
1140 delcodeval d;
1141 int p;
1142 scan_char_num();
1143 p = cur_val;
1144 scan_optional_equals();
1145 d = do_scan_extdef_del_code(extcode, false);
1146 set_del_code(p, d.small_family_value, d.small_character_value,
1147 d.large_family_value, d.large_character_value,
1148 (quarterword) (level));
1151 @ @c
1152 mathcodeval scan_mathchar(int extcode)
1154 char errstr[255] = { 0 };
1155 const char *hlp[] = {
1156 "I'm going to use 0 instead of that illegal code value.",
1157 NULL
1159 mathcodeval d;
1160 int mcls = 0, mfam = 0, mchr = 0;
1161 if (extcode == tex_mathcode) { /* \.{\\mathcode} */
1162 /* "TFCC */
1163 scan_int();
1164 if (cur_val > 0x8000) {
1166 tex_error("Invalid math code", hlp);
1167 cur_val = 0;
1169 /* needed for latex: fallback to umathnum_mathcode */
1170 mfam = (cur_val / 0x200000) & 0x7FF;
1171 mcls = mfam % 0x08;
1172 mfam = mfam / 0x08;
1173 mchr = cur_val & 0x1FFFFF;
1174 if (mchr > 0x10FFFF) {
1175 tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp);
1176 mcls = 0;
1177 mfam = 0;
1178 mchr = 0;
1180 } else {
1181 if (cur_val < 0) {
1182 snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val);
1183 tex_error(errstr, hlp);
1184 cur_val = 0;
1186 mcls = (cur_val / 0x1000);
1187 mfam = ((cur_val % 0x1000) / 0x100);
1188 mchr = (cur_val % 0x100);
1190 } else if (extcode == umath_mathcode) {
1191 /* <0-0x7> <0-0xFF> <0-0x10FFFF> */
1192 scan_int();
1193 mcls = cur_val;
1194 scan_int();
1195 mfam = cur_val;
1196 scan_char_num();
1197 mchr = cur_val;
1198 if (mcls < 0 || mcls > 7 || mfam > 255) {
1199 tex_error("Invalid math code", hlp);
1200 mchr = 0;
1201 mfam = 0;
1202 mcls = 0;
1204 } else if (extcode == umathnum_mathcode) {
1205 /* "FFT<21bits> */
1206 /* the largest numeric value is $2^32-1$, but
1207 the top of bit 21 can't be used as it contains invalid USV's
1209 /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */
1210 scan_int();
1211 mfam = (cur_val / 0x200000) & 0x7FF;
1212 mcls = mfam % 0x08;
1213 mfam = mfam / 0x08;
1214 mchr = cur_val & 0x1FFFFF;
1215 if (mchr > 0x10FFFF) {
1216 tex_error("Invalid math code", hlp);
1217 mcls = 0;
1218 mfam = 0;
1219 mchr = 0;
1221 } else {
1222 /* something's gone wrong */
1223 confusion("unknown_extcode");
1225 d.class_value = mcls;
1226 d.family_value = mfam;
1227 d.character_value = mchr;
1228 return d;
1231 @ @c
1232 void scan_extdef_math_code(int level, int extcode)
1234 mathcodeval d;
1235 int p;
1236 scan_char_num();
1237 p = cur_val;
1238 scan_optional_equals();
1239 d = scan_mathchar(extcode);
1240 set_math_code(p, d.class_value,
1241 d.family_value, d.character_value, (quarterword) (level));
1244 @ this reads in a delcode when actually a mathcode is needed
1246 mathcodeval scan_delimiter_as_mathchar(int extcode)
1248 delcodeval dval;
1249 mathcodeval mval;
1250 dval = do_scan_extdef_del_code(extcode, true);
1251 mval.class_value = dval.class_value;
1252 mval.family_value = dval.small_family_value;
1253 mval.character_value = dval.small_character_value;
1254 return mval;
1257 @ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad
1258 are broken down into subfields called |type| and either |math_list| or
1259 |(math_fam,math_character)|. The job of |scan_math| is to figure out
1260 what to place in one of these principal fields; it looks at the
1261 subformula that comes next in the input, and places an encoding of
1262 that subformula into a given word of |mem|.
1265 #define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
1267 int scan_math_style(pointer p, int mstyle)
1269 get_next_nb_nr();
1270 back_input();
1271 scan_left_brace();
1272 set_saved_record(0, saved_math, 0, p);
1273 incr(save_ptr);
1274 push_math(math_group, mstyle);
1275 return 1;
1278 int scan_math(pointer p, int mstyle)
1280 /* label restart,reswitch,exit; */
1281 mathcodeval mval = { 0, 0, 0 };
1282 assert(p != null);
1283 RESTART:
1284 get_next_nb_nr();
1285 RESWITCH:
1286 switch (cur_cmd) {
1287 case letter_cmd:
1288 case other_char_cmd:
1289 case char_given_cmd:
1290 mval = get_math_code(cur_chr);
1291 if (mval.class_value == 8) {
1292 /* An active character that is an |outer_call| is allowed here */
1293 cur_cs = active_to_cs(cur_chr, true);
1294 cur_cmd = eq_type(cur_cs);
1295 cur_chr = equiv(cur_cs);
1296 x_token();
1297 back_input();
1298 goto RESTART;
1300 break;
1301 case char_num_cmd:
1302 scan_char_num();
1303 cur_chr = cur_val;
1304 cur_cmd = char_given_cmd;
1305 goto RESWITCH;
1306 break;
1307 case math_char_num_cmd:
1308 if (cur_chr == 0)
1309 mval = scan_mathchar(tex_mathcode);
1310 else if (cur_chr == 1)
1311 mval = scan_mathchar(umath_mathcode);
1312 else if (cur_chr == 2)
1313 mval = scan_mathchar(umathnum_mathcode);
1314 else
1315 confusion("scan_math");
1316 break;
1317 case math_given_cmd:
1318 mval = mathchar_from_integer(cur_chr, tex_mathcode);
1319 break;
1320 case xmath_given_cmd:
1321 mval = mathchar_from_integer(cur_chr, umath_mathcode);
1322 break;
1323 case delim_num_cmd:
1324 if (cur_chr == 0)
1325 mval = scan_delimiter_as_mathchar(tex_mathcode);
1326 else if (cur_chr == 1)
1327 mval = scan_delimiter_as_mathchar(umath_mathcode);
1328 else
1329 confusion("scan_math");
1330 break;
1331 default:
1332 /* The pointer |p| is placed on |save_stack| while a complex subformula
1333 is being scanned. */
1334 back_input();
1335 scan_left_brace();
1336 set_saved_record(0, saved_math, 0, p);
1337 incr(save_ptr);
1338 push_math(math_group, mstyle);
1339 return 1;
1341 type(p) = math_char_node;
1342 math_character(p) = mval.character_value;
1343 if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range)
1344 math_fam(p) = cur_fam_par;
1345 else
1346 math_fam(p) = mval.family_value;
1347 return 0;
1350 @ The |set_math_char| procedure creates a new noad appropriate to a given
1351 math code, and appends it to the current mlist. However, if the math code
1352 is sufficiently large, the |cur_chr| is treated as an active character and
1353 nothing is appended.
1356 void set_math_char(mathcodeval mval)
1358 pointer p; /* the new noad */
1359 if (mval.class_value == 8) {
1360 /* An active character that is an |outer_call| is allowed here */
1361 cur_cs = active_to_cs(cur_chr, true);
1362 cur_cmd = eq_type(cur_cs);
1363 cur_chr = equiv(cur_cs);
1364 x_token();
1365 back_input();
1366 } else {
1367 pointer q;
1368 p = new_noad();
1369 q = new_node(math_char_node, 0);
1370 nucleus(p) = q;
1371 math_character(nucleus(p)) = mval.character_value;
1372 math_fam(nucleus(p)) = mval.family_value;
1373 if (mval.class_value == math_use_current_family_code) {
1374 if (cur_fam_par_in_range)
1375 math_fam(nucleus(p)) = cur_fam_par;
1376 subtype(p) = ord_noad_type;
1377 } else {
1378 switch (mval.class_value) {
1379 /* *INDENT-OFF* */
1380 case 0: subtype(p) = ord_noad_type; break;
1381 case 1: subtype(p) = op_noad_type_normal; break;
1382 case 2: subtype(p) = bin_noad_type; break;
1383 case 3: subtype(p) = rel_noad_type; break;
1384 case 4: subtype(p) = open_noad_type; break;
1385 case 5: subtype(p) = close_noad_type; break;
1386 case 6: subtype(p) = punct_noad_type; break;
1387 /* *INDENT-ON* */
1390 vlink(tail) = p;
1391 tail = p;
1395 @ The |math_char_in_text| procedure creates a new node representing a math char
1396 in text code, and appends it to the current list. However, if the math code
1397 is sufficiently large, the |cur_chr| is treated as an active character and
1398 nothing is appended.
1401 void math_char_in_text(mathcodeval mval)
1403 pointer p; /* the new node */
1404 if (mval.class_value == 8) {
1405 /* An active character that is an |outer_call| is allowed here */
1406 cur_cs = active_to_cs(cur_chr, true);
1407 cur_cmd = eq_type(cur_cs);
1408 cur_chr = equiv(cur_cs);
1409 x_token();
1410 back_input();
1411 } else {
1412 p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value);
1413 vlink(tail) = p;
1414 tail = p;
1418 @ @c
1419 void math_math_comp(void)
1421 pointer q;
1422 tail_append(new_noad());
1423 subtype(tail) = (quarterword) cur_chr;
1424 q = new_node(math_char_node, 0);
1425 nucleus(tail) = q;
1426 if (cur_chr == over_noad_type)
1427 (void) scan_math(nucleus(tail), cramped_style(m_style));
1428 else
1429 (void) scan_math(nucleus(tail), m_style);
1432 @ @c
1433 void math_limit_switch(void)
1435 const char *hlp[] = {
1436 "I'm ignoring this misplaced \\limits or \\nolimits command.",
1437 NULL
1439 if (head != tail) {
1440 if (type(tail) == simple_noad &&
1441 (subtype(tail) == op_noad_type_normal ||
1442 subtype(tail) == op_noad_type_limits ||
1443 subtype(tail) == op_noad_type_no_limits)) {
1444 subtype(tail) = (quarterword) cur_chr;
1445 return;
1448 tex_error("Limit controls must follow a math operator", hlp);
1451 @ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
1452 The first parameter of this procedure is the |mem| address where the
1453 delimiter is to be placed; the second tells if this delimiter follows
1454 \.{\\radical} or not.
1457 static void scan_delimiter(pointer p, int r)
1459 delcodeval dval = { 0, 0, 0, 0, 0 };
1460 if (r == tex_mathcode) { /* \.{\\radical} */
1461 dval = do_scan_extdef_del_code(tex_mathcode, true);
1462 } else if (r == umath_mathcode) { /* \.{\\Uradical} */
1463 dval = do_scan_extdef_del_code(umath_mathcode, false);
1464 } else if (r == no_mathcode) {
1465 get_next_nb_nr();
1466 switch (cur_cmd) {
1467 case letter_cmd:
1468 case other_char_cmd:
1469 dval = get_del_code(cur_chr);
1470 break;
1471 case delim_num_cmd:
1472 if (cur_chr == 0) /* \.{\\delimiter} */
1473 dval = do_scan_extdef_del_code(tex_mathcode, true);
1474 else if (cur_chr == 1) /* \.{\\Udelimiter} */
1475 dval = do_scan_extdef_del_code(umath_mathcode, true);
1476 else
1477 confusion("scan_delimiter1");
1478 break;
1479 default:
1480 dval.small_family_value = -1;
1481 break;
1483 } else {
1484 confusion("scan_delimiter2");
1486 if (p == null)
1487 return;
1488 if (dval.small_family_value < 0) {
1489 const char *hlp[] = {
1490 "I was expecting to see something like `(' or `\\{' or",
1491 "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
1492 "should probably delete the `{' by typing `1' now, so that",
1493 "braces don't get unbalanced. Otherwise just proceed",
1494 "Acceptable delimiters are characters whose \\delcode is",
1495 "nonnegative, or you can use `\\delimiter <delimiter code>'.",
1496 NULL
1498 back_error("Missing delimiter (. inserted)", hlp);
1499 small_fam(p) = 0;
1500 small_char(p) = 0;
1501 large_fam(p) = 0;
1502 large_char(p) = 0;
1503 } else {
1504 small_fam(p) = dval.small_family_value;
1505 small_char(p) = dval.small_character_value;
1506 large_fam(p) = dval.large_family_value;
1507 large_char(p) = dval.large_character_value;
1509 return;
1512 @ @c
1513 void math_radical(void)
1515 halfword q;
1516 int chr_code = cur_chr;
1517 halfword options = 0;
1518 tail_append(new_node(radical_noad, chr_code));
1519 q = new_node(delim_node, 0);
1520 left_delimiter(tail) = q;
1521 while (1) {
1522 if (scan_keyword("width")) {
1523 scan_dimen(false,false,false);
1524 radicalwidth(tail) = cur_val ;
1525 } else if (scan_keyword("left")) {
1526 options = options | noad_option_left ;
1527 } else if (scan_keyword("middle")) {
1528 options = options | noad_option_middle ;
1529 } else if (scan_keyword("right")) {
1530 options = options | noad_option_right ;
1531 } else {
1532 break;
1535 radicaloptions(tail) = options;
1536 if (chr_code == 0) /* \.{\\radical} */
1537 scan_delimiter(left_delimiter(tail), tex_mathcode);
1538 else if (chr_code == 1) /* \.{\\Uradical} */
1539 scan_delimiter(left_delimiter(tail), umath_mathcode);
1540 else if (chr_code == 2) /* \.{\\Uroot} */
1541 scan_delimiter(left_delimiter(tail), umath_mathcode);
1542 else if (chr_code == 3) /* \.{\\Uunderdelimiter} */
1543 scan_delimiter(left_delimiter(tail), umath_mathcode);
1544 else if (chr_code == 4) /* \.{\\Uoverdelimiter} */
1545 scan_delimiter(left_delimiter(tail), umath_mathcode);
1546 else if (chr_code == 5) /* \.{\\Udelimiterunder} */
1547 scan_delimiter(left_delimiter(tail), umath_mathcode);
1548 else if (chr_code == 6) /* \.{\\Udelimiterover} */
1549 scan_delimiter(left_delimiter(tail), umath_mathcode);
1550 else if (chr_code == 7) /* \.{\\Uhextensible} */
1551 scan_delimiter(left_delimiter(tail), umath_mathcode);
1552 else
1553 confusion("math_radical");
1554 if (chr_code == 7) {
1555 q = new_node(sub_box_node, 0); /* type will change */
1556 nucleus(tail) = q;
1557 return;
1558 } else if (chr_code == 2) {
1559 /* the trick with the |vlink(q)| is used by |scan_math|
1560 to decide whether it needs to go on */
1561 q = new_node(math_char_node, 0);
1562 vlink(q) = tail;
1563 degree(tail) = q;
1564 if (!scan_math(degree(tail), sup_sup_style(m_style))) {
1565 vlink(degree(tail)) = null;
1566 q = new_node(math_char_node, 0);
1567 nucleus(tail) = q;
1568 (void) scan_math(nucleus(tail), cramped_style(m_style));
1570 } else {
1571 q = new_node(math_char_node, 0);
1572 nucleus(tail) = q;
1573 (void) scan_math(nucleus(tail), cramped_style(m_style));
1577 @ @c
1578 void math_ac(void)
1580 halfword q;
1581 mathcodeval t = { 0, 0, 0 };
1582 mathcodeval b = { 0, 0, 0 };
1583 mathcodeval o = { 0, 0, 0 };
1584 if (cur_cmd == accent_cmd) {
1585 const char *hlp[] = {
1586 "I'm changing \\accent to \\mathaccent here; wish me luck.",
1587 "(Accents are not the same in formulas as they are in text.)",
1588 NULL
1590 tex_error("Please use \\mathaccent for accents in math mode", hlp);
1592 tail_append(new_node(accent_noad, 0));
1593 if (cur_chr == 0) { /* \.{\\mathaccent} */
1594 t = scan_mathchar(tex_mathcode);
1595 } else if (cur_chr == 1) { /* \.{\\Umathaccent} */
1596 if (scan_keyword("fixed")) {
1597 /* top */
1598 subtype(tail) = 1;
1599 t = scan_mathchar(umath_mathcode);
1600 } else if (scan_keyword("both")) {
1601 /* top bottom */
1602 if (scan_keyword("fixed")) {
1603 subtype(tail) = 1;
1605 t = scan_mathchar(umath_mathcode);
1606 if (scan_keyword("fixed")) {
1607 subtype(tail) += 2;
1609 b = scan_mathchar(umath_mathcode);
1610 } else if (scan_keyword("bottom")) {
1611 /* bottom */
1612 if (scan_keyword("fixed")) {
1613 subtype(tail) = 2;
1615 b = scan_mathchar(umath_mathcode);
1616 } else if (scan_keyword("top")) {
1617 /* top */
1618 if (scan_keyword("fixed")) {
1619 subtype(tail) = 1;
1621 t = scan_mathchar(umath_mathcode);
1622 } else if (scan_keyword("overlay")) {
1623 /* overlay */
1624 if (scan_keyword("fixed")) {
1625 subtype(tail) = 1;
1627 o = scan_mathchar(umath_mathcode);
1628 } else {
1629 /* top */
1630 t = scan_mathchar(umath_mathcode);
1632 if (scan_keyword("fraction")) {
1633 scan_int();
1634 accentfraction(tail) = cur_val;
1636 } else {
1637 confusion("mathaccent");
1639 if (!(t.character_value == 0 && t.family_value == 0)) {
1640 q = new_node(math_char_node, 0);
1641 top_accent_chr(tail) = q;
1642 math_character(top_accent_chr(tail)) = t.character_value;
1643 if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range)
1644 math_fam(top_accent_chr(tail)) = cur_fam_par;
1645 else
1646 math_fam(top_accent_chr(tail)) = t.family_value;
1648 if (!(b.character_value == 0 && b.family_value == 0)) {
1649 q = new_node(math_char_node, 0);
1650 bot_accent_chr(tail) = q;
1651 math_character(bot_accent_chr(tail)) = b.character_value;
1652 if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range)
1653 math_fam(bot_accent_chr(tail)) = cur_fam_par;
1654 else
1655 math_fam(bot_accent_chr(tail)) = b.family_value;
1657 if (!(o.character_value == 0 && o.family_value == 0)) {
1658 q = new_node(math_char_node, 0);
1659 overlay_accent_chr(tail) = q;
1660 math_character(overlay_accent_chr(tail)) = o.character_value;
1661 if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range)
1662 math_fam(overlay_accent_chr(tail)) = cur_fam_par;
1663 else
1664 math_fam(overlay_accent_chr(tail)) = o.family_value;
1666 q = new_node(math_char_node, 0);
1667 nucleus(tail) = q;
1668 (void) scan_math(nucleus(tail), cramped_style(m_style));
1671 @ @c
1672 pointer math_vcenter_group(pointer p)
1674 pointer q, r;
1675 q = new_noad();
1676 subtype(q) = vcenter_noad_type;
1677 r = new_node(sub_box_node, 0);
1678 nucleus(q) = r;
1679 math_list(nucleus(q)) = p;
1680 return q;
1683 @ The routine that scans the four mlists of a \.{\\mathchoice} is very
1684 much like the routine that builds discretionary nodes.
1687 void append_choices(void)
1689 tail_append(new_choice());
1690 incr(save_ptr);
1691 set_saved_record(-1, saved_choices, 0, 0);
1692 push_math(math_choice_group, display_style);
1693 scan_left_brace();
1696 @ @c
1697 void build_choices(void)
1699 pointer p; /* the current mlist */
1700 int prev_style;
1701 prev_style = m_style;
1702 unsave_math();
1703 p = fin_mlist(null);
1704 assert(saved_type(-1) == saved_choices);
1705 switch (saved_value(-1)) {
1706 case 0:
1707 display_mlist(tail) = p;
1708 break;
1709 case 1:
1710 text_mlist(tail) = p;
1711 break;
1712 case 2:
1713 script_mlist(tail) = p;
1714 break;
1715 case 3:
1716 script_script_mlist(tail) = p;
1717 decr(save_ptr);
1718 return;
1719 break;
1720 } /* there are no other cases */
1721 set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
1722 push_math(math_choice_group, (prev_style + 2));
1723 scan_left_brace();
1726 @ Subscripts and superscripts are attached to the previous nucleus by the
1727 action procedure called |sub_sup|.
1730 void sub_sup(void)
1732 pointer q;
1733 if (tail == head || (!scripts_allowed(tail))) {
1734 tail_append(new_noad());
1735 q = new_node(sub_mlist_node, 0);
1736 nucleus(tail) = q;
1738 if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */
1739 if (supscr(tail) != null) {
1740 const char *hlp[] = {
1741 "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
1743 tail_append(new_noad());
1744 q = new_node(sub_mlist_node, 0);
1745 nucleus(tail) = q;
1746 tex_error("Double superscript", hlp);
1748 q = new_node(math_char_node, 0);
1749 supscr(tail) = q;
1750 (void) scan_math(supscr(tail), sup_style(m_style));
1751 } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
1752 if (subscr(tail) != null) {
1753 const char *hlp[] = {
1754 "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
1756 tail_append(new_noad());
1757 q = new_node(sub_mlist_node, 0);
1758 nucleus(tail) = q;
1759 tex_error("Double subscript", hlp);
1761 q = new_node(math_char_node, 0);
1762 subscr(tail) = q;
1763 (void) scan_math(subscr(tail), sub_style(m_style));
1767 @ An operation like `\.{\\over}' causes the current mlist to go into a
1768 state of suspended animation: |incompleat_noad| points to a |fraction_noad|
1769 that contains the mlist-so-far as its numerator, while the denominator
1770 is yet to come. Finally when the mlist is finished, the denominator will
1771 go into the incompleat fraction noad, and that noad will become the
1772 whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
1773 delimiters.
1776 void math_fraction(void)
1778 halfword c; /* the type of generalized fraction we are scanning */
1779 pointer q;
1780 halfword options = 0;
1781 halfword temp_value;
1782 c = cur_chr;
1783 if (incompleat_noad_par != null) {
1784 const char *hlp[] = {
1785 "I'm ignoring this fraction specification, since I don't",
1786 "know whether a construction like `x \\over y \\over z'",
1787 "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
1788 NULL
1790 if (c >= delimited_code) {
1791 scan_delimiter(null, no_mathcode);
1792 scan_delimiter(null, no_mathcode);
1794 if ((c % delimited_code) == above_code)
1795 scan_normal_dimen();
1796 tex_error("Ambiguous; you need another { and }", hlp);
1797 } else {
1798 incompleat_noad_par = new_node(fraction_noad, 0);
1799 temp_value = new_node(sub_mlist_node, 0);
1800 numerator(incompleat_noad_par) = temp_value;
1801 math_list(numerator(incompleat_noad_par)) = vlink(head);
1802 vlink(head) = null;
1803 tail = head;
1804 m_style = cramped_style(m_style);
1806 if ((c % delimited_code) == skewed_code) {
1807 q = new_node(delim_node, 0);
1808 middle_delimiter(incompleat_noad_par) = q;
1809 scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode);
1811 if (c >= delimited_code) {
1812 q = new_node(delim_node, 0);
1813 left_delimiter(incompleat_noad_par) = q;
1814 q = new_node(delim_node, 0);
1815 right_delimiter(incompleat_noad_par) = q;
1816 scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode);
1817 scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode);
1819 switch (c % delimited_code) {
1820 case above_code:
1821 while (1) {
1822 if (scan_keyword("exact")) {
1823 options = options | noad_option_exact ;
1824 } else {
1825 break;
1828 fractionoptions(incompleat_noad_par) = options;
1829 scan_normal_dimen();
1830 thickness(incompleat_noad_par) = cur_val;
1831 break;
1832 case over_code:
1833 thickness(incompleat_noad_par) = default_code;
1834 break;
1835 case atop_code:
1836 thickness(incompleat_noad_par) = 0;
1837 break;
1838 case skewed_code:
1839 while (1) {
1840 if (scan_keyword("exact")) {
1841 options = options | noad_option_exact ;
1842 } else if (scan_keyword("noaxis")) {
1843 options = options | noad_option_no_axis ;
1844 } else {
1845 break;
1848 fractionoptions(incompleat_noad_par) = options;
1849 thickness(incompleat_noad_par) = 0;
1850 break;
1855 @ At the end of a math formula or subformula, the |fin_mlist| routine is
1856 called upon to return a pointer to the newly completed mlist, and to
1857 pop the nest back to the enclosing semantic level. The parameter to
1858 |fin_mlist|, if not null, points to a |fence_noad| that ends the
1859 current mlist; this |fence_noad| has not yet been appended.
1862 pointer fin_mlist(pointer p)
1864 pointer q; /* the mlist to return */
1865 if (incompleat_noad_par != null) {
1866 if (denominator(incompleat_noad_par) != null) {
1867 type(denominator(incompleat_noad_par)) = sub_mlist_node;
1868 } else {
1869 q = new_node(sub_mlist_node, 0);
1870 denominator(incompleat_noad_par) = q;
1872 math_list(denominator(incompleat_noad_par)) = vlink(head);
1873 if (p == null) {
1874 q = incompleat_noad_par;
1875 } else {
1876 q = math_list(numerator(incompleat_noad_par));
1877 if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
1878 || (delim_par == null))
1879 confusion("right"); /* this can't happen */
1880 math_list(numerator(incompleat_noad_par)) = vlink(delim_par);
1881 vlink(delim_par) = incompleat_noad_par;
1882 vlink(incompleat_noad_par) = p;
1884 } else {
1885 vlink(tail) = p;
1886 q = vlink(head);
1888 pop_nest();
1889 return q;
1892 @ Now at last we're ready to see what happens when a right brace occurs
1893 in a math formula. Two special cases are simplified here: Braces are effectively
1894 removed when they surround a single Ord without sub/superscripts, or when they
1895 surround an accent that is the nucleus of an Ord atom.
1898 void close_math_group(pointer p)
1900 int old_style = m_style;
1901 unsave_math();
1903 decr(save_ptr);
1904 assert(saved_type(0) == saved_math);
1905 type(saved_value(0)) = sub_mlist_node;
1906 p = fin_mlist(null);
1907 math_list(saved_value(0)) = p;
1908 if (p != null) {
1909 if (vlink(p) == null) {
1910 if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
1911 if (subscr(p) == null && supscr(p) == null) {
1912 type(saved_value(0)) = type(nucleus(p));
1913 if (type(nucleus(p)) == math_char_node) {
1914 math_fam(saved_value(0)) = math_fam(nucleus(p));
1915 math_character(saved_value(0)) =
1916 math_character(nucleus(p));
1917 } else {
1918 math_list(saved_value(0)) = math_list(nucleus(p));
1919 math_list(nucleus(p)) = null;
1921 delete_attribute_ref(node_attr(saved_value(0)));
1922 node_attr(saved_value(0)) = node_attr(nucleus(p));
1923 node_attr(nucleus(p)) = null;
1924 flush_node(p);
1926 } else if (type(p) == accent_noad) {
1927 if (saved_value(0) == nucleus(tail)) {
1928 if (type(tail) == simple_noad
1929 && subtype(tail) == ord_noad_type) {
1930 pointer q = head;
1931 while (vlink(q) != tail)
1932 q = vlink(q);
1933 vlink(q) = p;
1934 nucleus(tail) = null;
1935 subscr(tail) = null;
1936 supscr(tail) = null;
1937 delete_attribute_ref(node_attr(p));
1938 node_attr(p) = node_attr(tail);
1939 node_attr(tail) = null;
1940 flush_node(tail);
1941 tail = p;
1947 if (vlink(saved_value(0)) > 0) {
1948 pointer q;
1949 q = new_node(math_char_node, 0);
1950 nucleus(vlink(saved_value(0))) = q;
1951 vlink(saved_value(0)) = null;
1952 saved_value(0) = q;
1953 (void) scan_math(saved_value(0), old_style);
1954 /* restart */
1958 @ We have dealt with all constructions of math mode except `\.{\\left}' and
1959 `\.{\\right}', so the picture is completed by the following sections of
1960 the program. The |middle| feature of eTeX allows one ore several \.{\\middle}
1961 delimiters to appear between \.{\\left} and \.{\\right}.
1964 void math_left_right(void)
1966 halfword t; /* |left_noad_side| .. |right_noad_side| */
1967 pointer p; /* new noad */
1968 pointer q; /* resulting mlist */
1969 pointer r; /* temporary */
1970 halfword ht = 0;
1971 halfword dp = 0;
1972 halfword options = 0;
1973 halfword type = -1 ;
1974 t = cur_chr;
1976 if (t > 10) {
1977 /* we have \Uleft \Uright \Umiddle */
1978 t = t - 10;
1979 while (1) {
1980 if (scan_keyword("height")) {
1981 scan_dimen(false,false,false);
1982 ht = cur_val ;
1983 } else if (scan_keyword("depth")) {
1984 scan_dimen(false,false,false);
1985 dp = cur_val ;
1986 } else if (scan_keyword("axis")) {
1987 options = options | noad_option_axis ;
1988 } else if (scan_keyword("noaxis")) {
1989 options = options | noad_option_no_axis ;
1990 } else if (scan_keyword("exact")) {
1991 options = options | noad_option_exact ;
1992 } else if (scan_keyword("class")) {
1993 scan_int();
1994 type = cur_val ;
1995 } else {
1996 break;
2001 if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) {
2002 if (cur_group == math_shift_group) {
2003 scan_delimiter(null, no_mathcode);
2004 if (t == middle_noad_side) {
2005 const char *hlp[] = {
2006 "I'm ignoring a \\middle that had no matching \\left.",
2007 NULL
2009 tex_error("Extra \\middle", hlp);
2010 } else {
2011 const char *hlp[] = {
2012 "I'm ignoring a \\right that had no matching \\left.",
2013 NULL
2015 tex_error("Extra \\right", hlp);
2017 } else {
2018 off_save();
2020 } else {
2021 p = new_noad();
2022 type(p) = fence_noad;
2023 subtype(p) = (quarterword) t;
2024 r = new_node(delim_node, 0);
2025 delimiter(p) = r;
2027 delimiterheight(p) = ht;
2028 delimiterdepth(p) = dp;
2029 delimiteroptions(p) = options;
2030 delimiterclass(p) = type;
2031 delimiteritalic(p) = 0;
2033 scan_delimiter(delimiter(p), no_mathcode);
2035 if (t == no_noad_side) {
2036 tail_append(new_noad());
2037 subtype(tail) = inner_noad_type;
2038 r = new_node(sub_mlist_node, 0);
2039 nucleus(tail) = r;
2040 math_list(nucleus(tail)) = p;
2041 return ;
2044 if (t == left_noad_side) {
2045 q = p;
2046 } else {
2047 q = fin_mlist(p);
2048 unsave_math();
2050 if (t != right_noad_side) {
2051 push_math(math_left_group, m_style);
2052 vlink(head) = q;
2053 tail = p;
2054 delim_par = p;
2055 } else {
2056 tail_append(new_noad());
2057 subtype(tail) = inner_noad_type;
2058 r = new_node(sub_mlist_node, 0);
2059 nucleus(tail) = r;
2060 math_list(nucleus(tail)) = q;
2065 @ \TeX\ gets to the following part of the program when
2066 the first `\.\$' ending a display has been scanned.
2069 static void check_second_math_shift(void)
2071 get_x_token();
2072 if (cur_cmd != math_shift_cmd) {
2073 const char *hlp[] = {
2074 "The `$' that I just saw supposedly matches a previous `$$'.",
2075 "So I shall assume that you typed `$$' both times.",
2076 NULL
2078 back_error("Display math should end with $$", hlp);
2082 static void check_display_math_end(void)
2084 if (cur_chr != cramped_display_style) {
2085 const char *hlp[] = {
2086 "I shall assume that you typed that.",
2087 NULL
2089 tex_error("Display math should end with \\Ustopdisplaymath", hlp);
2093 static void check_inline_math_end(void)
2095 if (cur_chr != cramped_text_style) {
2096 const char *hlp[] = {
2097 "I shall assume that you typed that.",
2098 NULL
2100 tex_error("Inline math should end with \\Ustopmath", hlp);
2104 @ @c
2105 static void resume_after_display(void)
2107 if (cur_group != math_shift_group)
2108 confusion("display");
2109 unsave_math();
2110 prev_graf_par = prev_graf_par + 3;
2111 push_nest();
2112 mode = hmode;
2113 space_factor_par = 1000;
2114 /* this needs to be intercepted in the display math start ! */
2115 tail_append(make_local_par_node(penalty_par_code));
2116 get_x_token();
2117 if (cur_cmd != spacer_cmd)
2118 back_input();
2119 if (nest_ptr == 1) {
2120 normal_page_filter(after_display);
2121 build_page();
2125 @ The fussiest part of math mode processing occurs when a displayed formula is
2126 being centered and placed with an optional equation number.
2128 At this time we are in vertical mode (or internal vertical mode).
2130 |p| points to the mlist for the formula.
2131 |a| is either |null| or it points to a box containing the equation number.
2132 |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
2135 #define inject_display_skip_before(g) \
2136 switch (display_skip_mode_par) { \
2137 case 0 : /* normal tex */ \
2138 tail_append(new_param_glue(g)); \
2139 break;\
2140 case 1 : /* always */ \
2141 tail_append(new_param_glue(g)); \
2142 break; \
2143 case 2 : /* non-zero */ \
2144 if (g != 0 && ! glue_is_zero(glue_par(g))) \
2145 tail_append(new_param_glue(g)); \
2146 break; \
2147 case 3: /* ignore */ \
2148 break; \
2151 #define inject_display_skip_after(g) \
2152 switch (display_skip_mode_par) { \
2153 case 0 : /* normal tex */ \
2154 if (g != 0 && glue_is_positive(glue_par(g))) \
2155 tail_append(new_param_glue(g)); \
2156 break; \
2157 case 1 : /* always */ \
2158 tail_append(new_param_glue(g)); \
2159 break; \
2160 case 2 : /* non-zero */ \
2161 if (g != 0 && ! glue_is_zero(glue_par(g))) \
2162 tail_append(new_param_glue(g)); \
2163 break; \
2164 case 3: /* ignore */ \
2165 break; \
2168 static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
2170 pointer eq_box; /* box containing the equation */
2171 scaled eq_w; /* width of the equation */
2172 scaled line_w; /* width of the line */
2173 scaled eqno_w; /* width of equation number */
2174 scaled eqno_w2; /* width of equation number plus space to separate from equation */
2175 scaled line_s; /* move the line right this much */
2176 scaled d; /* displacement of equation in the line */
2177 small_number g1, g2; /* glue parameter codes for before and after */
2178 pointer r,s; /* kern nodes used to position the display */
2179 pointer t; /* tail of adjustment list */
2180 pointer pre_t; /* tail of pre-adjustment list */
2181 boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
2182 scaled eqno_width;
2183 swap_dir = (pre_display_direction_par < 0 ? true : false );
2184 if (eqno_box != null && swap_dir)
2185 l = !l;
2186 adjust_tail = adjust_head;
2187 pre_adjust_tail = pre_adjust_head;
2188 eq_box = hpack(p, 0, additional, -1);
2189 subtype(eq_box) = equation_list; /* new */
2190 build_attribute_list(eq_box);
2191 p = list_ptr(eq_box);
2192 t = adjust_tail;
2193 adjust_tail = null;
2194 pre_t = pre_adjust_tail;
2195 pre_adjust_tail = null;
2196 eq_w = width(eq_box);
2197 line_w = display_width_par;
2198 line_s = display_indent_par;
2199 if (eqno_box == null) {
2200 eqno_w = 0;
2201 eqno_width = 0;
2202 eqno_w2 = 0;
2203 } else {
2204 eqno_w = width(eqno_box);
2205 eqno_width = eqno_w;
2206 eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000);
2207 subtype(eqno_box) = equation_number_list; /* new */
2208 /* build_attribute_list(eqno_box); */ /* probably already set */
2210 if (eq_w + eqno_w2 > line_w) {
2211 /* The user can force the equation number to go on a separate line
2212 by causing its width to be zero. */
2213 if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
2214 || (total_shrink[sfi] != 0)
2215 || (total_shrink[fil] != 0)
2216 || (total_shrink[fill] != 0)
2217 || (total_shrink[filll] != 0))) {
2218 list_ptr(eq_box) = null;
2219 flush_node(eq_box);
2220 eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
2221 subtype(eq_box) = equation_list; /* new */
2222 build_attribute_list(eq_box);
2223 } else {
2224 eqno_w = 0;
2225 if (eq_w > line_w) {
2226 list_ptr(eq_box) = null;
2227 flush_node(eq_box);
2228 eq_box = hpack(p, line_w, exactly, -1);
2229 subtype(eq_box) = equation_list; /* new */
2230 build_attribute_list(eq_box);
2233 eq_w = width(eq_box);
2235 /* We try first to center the display without regard to the existence of
2236 the equation number. If that would make it too close (where ``too close''
2237 means that the space between display and equation number is less than the
2238 width of the equation number), we either center it in the remaining space
2239 or move it as far from the equation number as possible. The latter alternative
2240 is taken only if the display begins with glue, since we assume that the
2241 user put glue there to control the spacing precisely.
2243 d = half(line_w - eq_w);
2244 if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */
2245 d = half(line_w - eq_w - eqno_w);
2246 if (p != null)
2247 if (!is_char_node(p))
2248 if (type(p) == glue_node)
2249 d = 0;
2252 tail_append(new_penalty(pre_display_penalty_par));
2253 if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */
2254 g1 = above_display_skip_code;
2255 g2 = below_display_skip_code;
2256 } else {
2257 g1 = above_display_short_skip_code;
2258 g2 = below_display_short_skip_code;
2261 /* If the equation number is set on a line by itself, either before or
2262 after the formula, we append an infinite penalty so that no page break will
2263 separate the display from its number; and we use the same size and
2264 displacement for all three potential lines of the display, even though
2265 `\.{\\parshape}' may specify them differently.
2267 /* \.{\\leqno} on a forced single line due to |width=0| */
2268 /* it follows that |type(a)=hlist_node| */
2270 if (eqno_box && l && (eqno_w == 0)) {
2271 /* if (math_direction_par==dir_TLT) { */
2272 shift_amount(eqno_box) = 0;
2273 /* } else { */
2274 /* } */
2275 append_to_vlist(eqno_box,lua_key_index(equation_number));
2276 tail_append(new_penalty(inf_penalty));
2277 } else {
2278 inject_display_skip_before(g1);
2281 if (eqno_w != 0) {
2282 r = new_kern(line_w - eq_w - eqno_w - d);
2283 if (l) {
2284 if (swap_dir) {
2285 if (math_direction_par==dir_TLT) {
2286 /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */
2287 #ifdef DEBUG
2288 fprintf(stderr, "\nDEBUG: CASE 1\n");
2289 #endif
2290 s = new_kern(width(r) + eqno_w);
2291 try_couple_nodes(eqno_box,r);
2292 try_couple_nodes(r,eq_box);
2293 try_couple_nodes(eq_box,s);
2294 } else {
2295 /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */
2296 #ifdef DEBUG
2297 fprintf(stderr, "\nDEBUG: CASE 2\n");
2298 #endif
2299 try_couple_nodes(eqno_box,r);
2300 try_couple_nodes(r,eq_box);
2302 } else {
2303 if (math_direction_par==dir_TLT) {
2304 /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */
2305 #ifdef DEBUG
2306 fprintf(stderr, "\nDEBUG: CASE 3\n");
2307 #endif
2308 s = new_kern(width(r) + eqno_w);
2309 } else {
2310 /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */
2311 #ifdef DEBUG
2312 fprintf(stderr, "\nDEBUG: CASE 4\n");
2313 #endif
2314 s = new_kern(width(r));
2316 try_couple_nodes(eqno_box,r);
2317 try_couple_nodes(r,eq_box);
2318 try_couple_nodes(eq_box,s);
2320 eq_box = eqno_box;
2321 } else {
2322 if (swap_dir) {
2323 if (math_direction_par==dir_TLT) {
2324 /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */
2325 #ifdef DEBUG
2326 fprintf(stderr, "\nDEBUG: CASE 5\n");
2327 #endif
2328 } else {
2329 /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */
2330 #ifdef DEBUG
2331 fprintf(stderr, "\nDEBUG: CASE 6\n");
2332 #endif
2334 try_couple_nodes(eq_box,r);
2335 try_couple_nodes(r,eqno_box);
2336 } else {
2337 if (math_direction_par==dir_TLT) {
2338 /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */
2339 #ifdef DEBUG
2340 fprintf(stderr, "\nDEBUG: CASE 7\n");
2341 #endif
2342 s = new_kern(d);
2343 } else {
2344 /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */
2345 #ifdef DEBUG
2346 fprintf(stderr, "\nDEBUG: CASE 8\n");
2347 #endif
2348 s = new_kern(width(r) + eqno_w);
2350 try_couple_nodes(s,eq_box);
2351 try_couple_nodes(eq_box,r);
2352 try_couple_nodes(r,eqno_box);
2353 eq_box = s;
2356 eq_box = hpack(eq_box, 0, additional, -1);
2357 subtype(eq_box) = equation_list; /* new */
2358 build_attribute_list(eq_box);
2359 shift_amount(eq_box) = line_s;
2360 } else {
2361 shift_amount(eq_box) = line_s + d;
2363 /* check for prev: */
2364 append_to_vlist(eq_box,lua_key_index(equation));
2366 if ((eqno_box != null) && (eqno_w == 0) && !l) {
2367 tail_append(new_penalty(inf_penalty));
2368 /* if (math_direction_par==dir_TLT) { */
2369 shift_amount(eqno_box) = line_s + line_w - eqno_width ;
2370 /* } else { */
2371 /* } */
2372 append_to_vlist(eqno_box,lua_key_index(equation_number));
2373 g2 = 0;
2375 if (t != adjust_head) { /* migrating material comes after equation number */
2376 vlink(tail) = vlink(adjust_head);
2377 /* needs testing */
2378 alink(adjust_tail) = alink(tail);
2379 tail = t;
2381 if (pre_t != pre_adjust_head) {
2382 vlink(tail) = vlink(pre_adjust_head);
2383 /* needs testing */
2384 alink(pre_adjust_tail) = alink(tail);
2385 tail = pre_t;
2387 tail_append(new_penalty(post_display_penalty_par));
2388 inject_display_skip_after(g2);
2389 resume_after_display();
2392 @ @c
2393 void after_math(void)
2395 int m; /* |mmode| or |-mmode| */
2396 pointer p; /* the formula */
2397 pointer a = null; /* box containing equation number */
2398 boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
2399 m = mode;
2400 p = fin_mlist(null); /* this pops the nest */
2401 if (cur_cmd == math_shift_cs_cmd &&
2402 (cur_chr == text_style || cur_chr == display_style)) {
2403 you_cant();
2405 if (mode == -m) { /* end of equation number */
2406 if (cur_cmd == math_shift_cmd) {
2407 check_second_math_shift();
2408 } else {
2409 check_display_math_end();
2411 run_mlist_to_hlist(p, false, text_style);
2412 a = hpack(vlink(temp_head), 0, additional, -1);
2413 build_attribute_list(a);
2414 unsave_math();
2415 decr(save_ptr); /* now |cur_group=math_shift_group| */
2416 assert(saved_type(0) == saved_eqno);
2417 if (saved_value(0) == 1)
2418 l = true;
2419 m = mode;
2420 p = fin_mlist(null);
2423 if (m < 0) {
2424 /* The |unsave| is done after everything else here; hence an appearance of
2425 `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
2426 particular \.\$'s. This is consistent with the conventions of
2427 `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
2428 space above that display.
2430 if (cur_cmd == math_shift_cs_cmd) {
2431 check_inline_math_end();
2433 tail_append(new_math(math_surround_par, before));
2434 /* begin mathskip code */
2435 switch (math_skip_mode) {
2436 case 0 :
2437 /* obey mathsurround when zero glue */
2438 if (! glue_is_zero(math_skip_par)) {
2439 copy_glue_values(tail,math_skip_par);
2440 surround(tail) = 0;
2442 break ;
2443 case 1 :
2444 /* always left */
2445 case 3 :
2446 /* always both */
2447 case 6 :
2448 /* only when skip */
2449 copy_glue_values(tail,math_skip_par);
2450 surround(tail) = 0;
2451 break ;
2452 case 2 :
2453 /* only right */
2454 surround(tail) = 0;
2455 break ;
2456 case 4 :
2457 /* ignore, obey marthsurround */
2458 break ;
2459 case 5:
2460 /* all spacing disabled */
2461 surround(tail) = 0;
2462 break ;
2464 /* end mathskip code */
2465 if (dir_math_save) {
2466 tail_append(new_dir(math_direction_par));
2468 run_mlist_to_hlist(p, (mode > 0), text_style);
2469 vlink(tail) = vlink(temp_head);
2470 while (vlink(tail) != null) {
2471 tail = vlink(tail);
2473 if (dir_math_save) {
2474 tail_append(new_dir(math_direction_par - dir_swap));
2476 dir_math_save = false;
2477 tail_append(new_math(math_surround_par, after));
2478 /* begin mathskip code */
2479 switch (math_skip_mode) {
2480 case 0 :
2481 /* obey mathsurround when zero glue */
2482 if (! glue_is_zero(math_skip_par)) {
2483 copy_glue_values(tail,math_skip_par);
2484 surround(tail) = 0;
2486 break ;
2487 case 2 :
2488 /* always right */
2489 case 3 :
2490 /* always both */
2491 case 6 :
2492 /* only when skip */
2493 copy_glue_values(tail,math_skip_par);
2494 surround(tail) = 0;
2495 break ;
2496 case 1 :
2497 /* only left */
2498 surround(tail) = 0;
2499 break ;
2500 case 4 :
2501 /* ignore, obey marthsurround */
2502 break ;
2503 case 5:
2504 /* all spacing disabled */
2505 surround(tail) = 0;
2506 break ;
2508 /* end mathskip code */
2509 space_factor_par = 1000;
2510 unsave_math();
2511 } else {
2512 if (a == null) {
2513 if (cur_cmd == math_shift_cmd) {
2514 check_second_math_shift();
2515 } else {
2516 check_display_math_end();
2519 run_mlist_to_hlist(p, false, display_style);
2520 finish_displayed_math(l, a, vlink(temp_head));
2524 @ When \.{\\halign} appears in a display, the alignment routines operate
2525 essentially as they do in vertical mode. Then the following program is
2526 activated, with |p| and |q| pointing to the beginning and end of the
2527 resulting list, and with |aux_save| holding the |prev_depth| value.
2530 void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
2532 do_assignments();
2533 if (cur_cmd == math_shift_cmd) {
2534 check_second_math_shift();
2535 } else {
2536 check_display_math_end();
2538 pop_nest();
2539 tail_append(new_penalty(pre_display_penalty_par));
2540 inject_display_skip_before(above_display_skip_code);
2541 vlink(tail) = p;
2542 if (p != null)
2543 tail = q;
2544 tail_append(new_penalty(post_display_penalty_par));
2545 inject_display_skip_after(below_display_skip_code);
2546 cur_list.prev_depth_field = saved_prevdepth;
2547 resume_after_display();
2550 @ Interface to \.{\\Umath} and \.{\\mathstyle}
2553 void setup_math_style(void)
2555 pointer q;
2556 tail_append(new_noad());
2557 q = new_node(math_char_node, 0);
2558 nucleus(tail) = q;
2559 (void) scan_math_style(nucleus(tail), num_style(m_style));
2562 @ @c
2563 void print_math_style(void)
2565 if (abs(mode) == mmode)
2566 print_int(m_style);
2567 else
2568 print_int(-1);