sync with experimental
[luatex.git] / source / texk / web2c / luatexdir / tex / texmath.w
blobb378c6626197995e160646bea0add7834725b1ee
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, (2 * quad(get_cur_font())));
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 c = cur_chr;
1782 if (incompleat_noad_par != null) {
1783 const char *hlp[] = {
1784 "I'm ignoring this fraction specification, since I don't",
1785 "know whether a construction like `x \\over y \\over z'",
1786 "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
1787 NULL
1789 if (c >= delimited_code) {
1790 scan_delimiter(null, no_mathcode);
1791 scan_delimiter(null, no_mathcode);
1793 if ((c % delimited_code) == above_code)
1794 scan_normal_dimen();
1795 tex_error("Ambiguous; you need another { and }", hlp);
1796 } else {
1797 incompleat_noad_par = new_node(fraction_noad, 0);
1798 numerator(incompleat_noad_par) = new_node(sub_mlist_node, 0);
1799 math_list(numerator(incompleat_noad_par)) = vlink(head);
1800 vlink(head) = null;
1801 tail = head;
1802 m_style = cramped_style(m_style);
1804 if ((c % delimited_code) == skewed_code) {
1805 q = new_node(delim_node, 0);
1806 middle_delimiter(incompleat_noad_par) = q;
1807 scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode);
1809 if (c >= delimited_code) {
1810 q = new_node(delim_node, 0);
1811 left_delimiter(incompleat_noad_par) = q;
1812 q = new_node(delim_node, 0);
1813 right_delimiter(incompleat_noad_par) = q;
1814 scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode);
1815 scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode);
1817 switch (c % delimited_code) {
1818 case above_code:
1819 while (1) {
1820 if (scan_keyword("exact")) {
1821 options = options | noad_option_exact ;
1822 } else {
1823 break;
1826 fractionoptions(incompleat_noad_par) = options;
1827 scan_normal_dimen();
1828 thickness(incompleat_noad_par) = cur_val;
1829 break;
1830 case over_code:
1831 thickness(incompleat_noad_par) = default_code;
1832 break;
1833 case atop_code:
1834 thickness(incompleat_noad_par) = 0;
1835 break;
1836 case skewed_code:
1837 while (1) {
1838 if (scan_keyword("exact")) {
1839 options = options | noad_option_exact ;
1840 } else if (scan_keyword("noaxis")) {
1841 options = options | noad_option_no_axis ;
1842 } else {
1843 break;
1846 fractionoptions(incompleat_noad_par) = options;
1847 thickness(incompleat_noad_par) = 0;
1848 break;
1853 @ At the end of a math formula or subformula, the |fin_mlist| routine is
1854 called upon to return a pointer to the newly completed mlist, and to
1855 pop the nest back to the enclosing semantic level. The parameter to
1856 |fin_mlist|, if not null, points to a |fence_noad| that ends the
1857 current mlist; this |fence_noad| has not yet been appended.
1860 pointer fin_mlist(pointer p)
1862 pointer q; /* the mlist to return */
1863 if (incompleat_noad_par != null) {
1864 if (denominator(incompleat_noad_par) != null) {
1865 type(denominator(incompleat_noad_par)) = sub_mlist_node;
1866 } else {
1867 q = new_node(sub_mlist_node, 0);
1868 denominator(incompleat_noad_par) = q;
1870 math_list(denominator(incompleat_noad_par)) = vlink(head);
1871 if (p == null) {
1872 q = incompleat_noad_par;
1873 } else {
1874 q = math_list(numerator(incompleat_noad_par));
1875 if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
1876 || (delim_par == null))
1877 confusion("right"); /* this can't happen */
1878 math_list(numerator(incompleat_noad_par)) = vlink(delim_par);
1879 vlink(delim_par) = incompleat_noad_par;
1880 vlink(incompleat_noad_par) = p;
1882 } else {
1883 vlink(tail) = p;
1884 q = vlink(head);
1886 pop_nest();
1887 return q;
1890 @ Now at last we're ready to see what happens when a right brace occurs
1891 in a math formula. Two special cases are simplified here: Braces are effectively
1892 removed when they surround a single Ord without sub/superscripts, or when they
1893 surround an accent that is the nucleus of an Ord atom.
1896 void close_math_group(pointer p)
1898 int old_style = m_style;
1899 unsave_math();
1901 decr(save_ptr);
1902 assert(saved_type(0) == saved_math);
1903 type(saved_value(0)) = sub_mlist_node;
1904 p = fin_mlist(null);
1905 math_list(saved_value(0)) = p;
1906 if (p != null) {
1907 if (vlink(p) == null) {
1908 if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
1909 if (subscr(p) == null && supscr(p) == null) {
1910 type(saved_value(0)) = type(nucleus(p));
1911 if (type(nucleus(p)) == math_char_node) {
1912 math_fam(saved_value(0)) = math_fam(nucleus(p));
1913 math_character(saved_value(0)) =
1914 math_character(nucleus(p));
1915 } else {
1916 math_list(saved_value(0)) = math_list(nucleus(p));
1917 math_list(nucleus(p)) = null;
1919 delete_attribute_ref(node_attr(saved_value(0)));
1920 node_attr(saved_value(0)) = node_attr(nucleus(p));
1921 node_attr(nucleus(p)) = null;
1922 flush_node(p);
1924 } else if (type(p) == accent_noad) {
1925 if (saved_value(0) == nucleus(tail)) {
1926 if (type(tail) == simple_noad
1927 && subtype(tail) == ord_noad_type) {
1928 pointer q = head;
1929 while (vlink(q) != tail)
1930 q = vlink(q);
1931 vlink(q) = p;
1932 nucleus(tail) = null;
1933 subscr(tail) = null;
1934 supscr(tail) = null;
1935 delete_attribute_ref(node_attr(p));
1936 node_attr(p) = node_attr(tail);
1937 node_attr(tail) = null;
1938 flush_node(tail);
1939 tail = p;
1945 if (vlink(saved_value(0)) > 0) {
1946 pointer q;
1947 q = new_node(math_char_node, 0);
1948 nucleus(vlink(saved_value(0))) = q;
1949 vlink(saved_value(0)) = null;
1950 saved_value(0) = q;
1951 (void) scan_math(saved_value(0), old_style);
1952 /* restart */
1956 @ We have dealt with all constructions of math mode except `\.{\\left}' and
1957 `\.{\\right}', so the picture is completed by the following sections of
1958 the program. The |middle| feature of eTeX allows one ore several \.{\\middle}
1959 delimiters to appear between \.{\\left} and \.{\\right}.
1962 void math_left_right(void)
1964 halfword t; /* |left_noad_side| .. |right_noad_side| */
1965 pointer p; /* new noad */
1966 pointer q; /* resulting mlist */
1967 pointer r; /* temporary */
1968 halfword ht = 0;
1969 halfword dp = 0;
1970 halfword options = 0;
1971 halfword type = -1 ;
1972 t = cur_chr;
1974 if (t > 10) {
1975 /* we have \Uleft \Uright \Umiddle */
1976 t = t - 10;
1977 while (1) {
1978 if (scan_keyword("height")) {
1979 scan_dimen(false,false,false);
1980 ht = cur_val ;
1981 } else if (scan_keyword("depth")) {
1982 scan_dimen(false,false,false);
1983 dp = cur_val ;
1984 } else if (scan_keyword("axis")) {
1985 options = options | noad_option_axis ;
1986 } else if (scan_keyword("noaxis")) {
1987 options = options | noad_option_no_axis ;
1988 } else if (scan_keyword("exact")) {
1989 options = options | noad_option_exact ;
1990 } else if (scan_keyword("class")) {
1991 scan_int();
1992 type = cur_val ;
1993 } else {
1994 break;
1999 if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) {
2000 if (cur_group == math_shift_group) {
2001 scan_delimiter(null, no_mathcode);
2002 if (t == middle_noad_side) {
2003 const char *hlp[] = {
2004 "I'm ignoring a \\middle that had no matching \\left.",
2005 NULL
2007 tex_error("Extra \\middle", hlp);
2008 } else {
2009 const char *hlp[] = {
2010 "I'm ignoring a \\right that had no matching \\left.",
2011 NULL
2013 tex_error("Extra \\right", hlp);
2015 } else {
2016 off_save();
2018 } else {
2019 p = new_noad();
2020 type(p) = fence_noad;
2021 subtype(p) = (quarterword) t;
2022 r = new_node(delim_node, 0);
2023 delimiter(p) = r;
2025 delimiterheight(p) = ht;
2026 delimiterdepth(p) = dp;
2027 delimiteroptions(p) = options;
2028 delimiterclass(p) = type;
2029 delimiteritalic(p) = 0;
2031 scan_delimiter(delimiter(p), no_mathcode);
2033 if (t == no_noad_side) {
2034 tail_append(new_noad());
2035 subtype(tail) = inner_noad_type;
2036 r = new_node(sub_mlist_node, 0);
2037 nucleus(tail) = r;
2038 math_list(nucleus(tail)) = p;
2039 return ;
2042 if (t == left_noad_side) {
2043 q = p;
2044 } else {
2045 q = fin_mlist(p);
2046 unsave_math();
2048 if (t != right_noad_side) {
2049 push_math(math_left_group, m_style);
2050 vlink(head) = q;
2051 tail = p;
2052 delim_par = p;
2053 } else {
2054 tail_append(new_noad());
2055 subtype(tail) = inner_noad_type;
2056 r = new_node(sub_mlist_node, 0);
2057 nucleus(tail) = r;
2058 math_list(nucleus(tail)) = q;
2063 @ \TeX\ gets to the following part of the program when
2064 the first `\.\$' ending a display has been scanned.
2067 static void check_second_math_shift(void)
2069 get_x_token();
2070 if (cur_cmd != math_shift_cmd) {
2071 const char *hlp[] = {
2072 "The `$' that I just saw supposedly matches a previous `$$'.",
2073 "So I shall assume that you typed `$$' both times.",
2074 NULL
2076 back_error("Display math should end with $$", hlp);
2080 static void check_display_math_end(void)
2082 if (cur_chr != cramped_display_style) {
2083 const char *hlp[] = {
2084 "I shall assume that you typed that.",
2085 NULL
2087 tex_error("Display math should end with \\Ustopdisplaymath", hlp);
2091 static void check_inline_math_end(void)
2093 if (cur_chr != cramped_text_style) {
2094 const char *hlp[] = {
2095 "I shall assume that you typed that.",
2096 NULL
2098 tex_error("Inline math should end with \\Ustopmath", hlp);
2102 @ @c
2103 static void resume_after_display(void)
2105 if (cur_group != math_shift_group)
2106 confusion("display");
2107 unsave_math();
2108 prev_graf_par = prev_graf_par + 3;
2109 push_nest();
2110 mode = hmode;
2111 space_factor_par = 1000;
2112 /* this needs to be intercepted in the display math start ! */
2113 tail_append(make_local_par_node(penalty_par_code));
2114 get_x_token();
2115 if (cur_cmd != spacer_cmd)
2116 back_input();
2117 if (nest_ptr == 1) {
2118 normal_page_filter(after_display);
2119 build_page();
2123 @ The fussiest part of math mode processing occurs when a displayed formula is
2124 being centered and placed with an optional equation number.
2126 At this time we are in vertical mode (or internal vertical mode).
2128 |p| points to the mlist for the formula.
2129 |a| is either |null| or it points to a box containing the equation number.
2130 |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
2133 #define inject_display_skip_before(g) \
2134 switch (display_skip_mode_par) { \
2135 case 0 : /* normal tex */ \
2136 tail_append(new_param_glue(g)); \
2137 break;\
2138 case 1 : /* always */ \
2139 tail_append(new_param_glue(g)); \
2140 break; \
2141 case 2 : /* non-zero */ \
2142 if (g != 0 && ! glue_is_zero(glue_par(g))) \
2143 tail_append(new_param_glue(g)); \
2144 break; \
2145 case 3: /* ignore */ \
2146 break; \
2149 #define inject_display_skip_after(g) \
2150 switch (display_skip_mode_par) { \
2151 case 0 : /* normal tex */ \
2152 if (g != 0 && glue_is_positive(glue_par(g))) \
2153 tail_append(new_param_glue(g)); \
2154 break; \
2155 case 1 : /* always */ \
2156 tail_append(new_param_glue(g)); \
2157 break; \
2158 case 2 : /* non-zero */ \
2159 if (g != 0 && ! glue_is_zero(glue_par(g))) \
2160 tail_append(new_param_glue(g)); \
2161 break; \
2162 case 3: /* ignore */ \
2163 break; \
2166 static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
2168 pointer eq_box; /* box containing the equation */
2169 scaled eq_w; /* width of the equation */
2170 scaled line_w; /* width of the line */
2171 scaled eqno_w; /* width of equation number */
2172 scaled eqno_w2; /* width of equation number plus space to separate from equation */
2173 scaled line_s; /* move the line right this much */
2174 scaled d; /* displacement of equation in the line */
2175 small_number g1, g2; /* glue parameter codes for before and after */
2176 pointer r,s; /* kern nodes used to position the display */
2177 pointer t; /* tail of adjustment list */
2178 pointer pre_t; /* tail of pre-adjustment list */
2179 boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
2180 scaled eqno_width;
2181 swap_dir = (pre_display_direction_par < 0 ? true : false );
2182 if (eqno_box != null && swap_dir)
2183 l = !l;
2184 adjust_tail = adjust_head;
2185 pre_adjust_tail = pre_adjust_head;
2186 eq_box = hpack(p, 0, additional, -1);
2187 subtype(eq_box) = equation_list; /* new */
2188 build_attribute_list(eq_box);
2189 p = list_ptr(eq_box);
2190 t = adjust_tail;
2191 adjust_tail = null;
2192 pre_t = pre_adjust_tail;
2193 pre_adjust_tail = null;
2194 eq_w = width(eq_box);
2195 line_w = display_width_par;
2196 line_s = display_indent_par;
2197 if (eqno_box == null) {
2198 eqno_w = 0;
2199 eqno_width = 0;
2200 eqno_w2 = 0;
2201 } else {
2202 eqno_w = width(eqno_box);
2203 eqno_width = eqno_w;
2204 eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000);
2205 subtype(eqno_box) = equation_number_list; /* new */
2206 /* build_attribute_list(eqno_box); */ /* probably already set */
2208 if (eq_w + eqno_w2 > line_w) {
2209 /* The user can force the equation number to go on a separate line
2210 by causing its width to be zero. */
2211 if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
2212 || (total_shrink[sfi] != 0)
2213 || (total_shrink[fil] != 0)
2214 || (total_shrink[fill] != 0)
2215 || (total_shrink[filll] != 0))) {
2216 list_ptr(eq_box) = null;
2217 flush_node(eq_box);
2218 eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
2219 subtype(eq_box) = equation_list; /* new */
2220 build_attribute_list(eq_box);
2221 } else {
2222 eqno_w = 0;
2223 if (eq_w > line_w) {
2224 list_ptr(eq_box) = null;
2225 flush_node(eq_box);
2226 eq_box = hpack(p, line_w, exactly, -1);
2227 subtype(eq_box) = equation_list; /* new */
2228 build_attribute_list(eq_box);
2231 eq_w = width(eq_box);
2233 /* We try first to center the display without regard to the existence of
2234 the equation number. If that would make it too close (where ``too close''
2235 means that the space between display and equation number is less than the
2236 width of the equation number), we either center it in the remaining space
2237 or move it as far from the equation number as possible. The latter alternative
2238 is taken only if the display begins with glue, since we assume that the
2239 user put glue there to control the spacing precisely.
2241 d = half(line_w - eq_w);
2242 if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */
2243 d = half(line_w - eq_w - eqno_w);
2244 if (p != null)
2245 if (!is_char_node(p))
2246 if (type(p) == glue_node)
2247 d = 0;
2250 tail_append(new_penalty(pre_display_penalty_par));
2251 if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */
2252 g1 = above_display_skip_code;
2253 g2 = below_display_skip_code;
2254 } else {
2255 g1 = above_display_short_skip_code;
2256 g2 = below_display_short_skip_code;
2259 /* If the equation number is set on a line by itself, either before or
2260 after the formula, we append an infinite penalty so that no page break will
2261 separate the display from its number; and we use the same size and
2262 displacement for all three potential lines of the display, even though
2263 `\.{\\parshape}' may specify them differently.
2265 /* \.{\\leqno} on a forced single line due to |width=0| */
2266 /* it follows that |type(a)=hlist_node| */
2268 if (eqno_box && l && (eqno_w == 0)) {
2269 /* if (math_direction_par==dir_TLT) { */
2270 shift_amount(eqno_box) = 0;
2271 /* } else { */
2272 /* } */
2273 append_to_vlist(eqno_box,lua_key_index(equation_number));
2274 tail_append(new_penalty(inf_penalty));
2275 } else {
2276 inject_display_skip_before(g1);
2279 if (eqno_w != 0) {
2280 r = new_kern(line_w - eq_w - eqno_w - d);
2281 if (l) {
2282 if (swap_dir) {
2283 if (math_direction_par==dir_TLT) {
2284 /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */
2285 #ifdef DEBUG
2286 fprintf(stderr, "\nDEBUG: CASE 1\n");
2287 #endif
2288 s = new_kern(width(r) + eqno_w);
2289 try_couple_nodes(eqno_box,r);
2290 try_couple_nodes(r,eq_box);
2291 try_couple_nodes(eq_box,s);
2292 } else {
2293 /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */
2294 #ifdef DEBUG
2295 fprintf(stderr, "\nDEBUG: CASE 2\n");
2296 #endif
2297 try_couple_nodes(eqno_box,r);
2298 try_couple_nodes(r,eq_box);
2300 } else {
2301 if (math_direction_par==dir_TLT) {
2302 /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */
2303 #ifdef DEBUG
2304 fprintf(stderr, "\nDEBUG: CASE 3\n");
2305 #endif
2306 s = new_kern(width(r) + eqno_w);
2307 } else {
2308 /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */
2309 #ifdef DEBUG
2310 fprintf(stderr, "\nDEBUG: CASE 4\n");
2311 #endif
2312 s = new_kern(width(r));
2314 try_couple_nodes(eqno_box,r);
2315 try_couple_nodes(r,eq_box);
2316 try_couple_nodes(eq_box,s);
2318 eq_box = eqno_box;
2319 } else {
2320 if (swap_dir) {
2321 if (math_direction_par==dir_TLT) {
2322 /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */
2323 #ifdef DEBUG
2324 fprintf(stderr, "\nDEBUG: CASE 5\n");
2325 #endif
2326 } else {
2327 /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */
2328 #ifdef DEBUG
2329 fprintf(stderr, "\nDEBUG: CASE 6\n");
2330 #endif
2332 try_couple_nodes(eq_box,r);
2333 try_couple_nodes(r,eqno_box);
2334 } else {
2335 if (math_direction_par==dir_TLT) {
2336 /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */
2337 #ifdef DEBUG
2338 fprintf(stderr, "\nDEBUG: CASE 7\n");
2339 #endif
2340 s = new_kern(d);
2341 } else {
2342 /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */
2343 #ifdef DEBUG
2344 fprintf(stderr, "\nDEBUG: CASE 8\n");
2345 #endif
2346 s = new_kern(width(r) + eqno_w);
2348 try_couple_nodes(s,eq_box);
2349 try_couple_nodes(eq_box,r);
2350 try_couple_nodes(r,eqno_box);
2351 eq_box = s;
2354 eq_box = hpack(eq_box, 0, additional, -1);
2355 subtype(eq_box) = equation_list; /* new */
2356 build_attribute_list(eq_box);
2357 shift_amount(eq_box) = line_s;
2358 } else {
2359 shift_amount(eq_box) = line_s + d;
2361 /* check for prev: */
2362 append_to_vlist(eq_box,lua_key_index(equation));
2364 if ((eqno_box != null) && (eqno_w == 0) && !l) {
2365 tail_append(new_penalty(inf_penalty));
2366 /* if (math_direction_par==dir_TLT) { */
2367 shift_amount(eqno_box) = line_s + line_w - eqno_width ;
2368 /* } else { */
2369 /* } */
2370 append_to_vlist(eqno_box,lua_key_index(equation_number));
2371 g2 = 0;
2373 if (t != adjust_head) { /* migrating material comes after equation number */
2374 vlink(tail) = vlink(adjust_head);
2375 /* needs testing */
2376 alink(adjust_tail) = alink(tail);
2377 tail = t;
2379 if (pre_t != pre_adjust_head) {
2380 vlink(tail) = vlink(pre_adjust_head);
2381 /* needs testing */
2382 alink(pre_adjust_tail) = alink(tail);
2383 tail = pre_t;
2385 tail_append(new_penalty(post_display_penalty_par));
2386 inject_display_skip_after(g2);
2387 resume_after_display();
2390 @ @c
2391 void after_math(void)
2393 int m; /* |mmode| or |-mmode| */
2394 pointer p; /* the formula */
2395 pointer a = null; /* box containing equation number */
2396 boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
2397 m = mode;
2398 p = fin_mlist(null); /* this pops the nest */
2399 if (cur_cmd == math_shift_cs_cmd &&
2400 (cur_chr == text_style || cur_chr == display_style)) {
2401 you_cant();
2403 if (mode == -m) { /* end of equation number */
2404 if (cur_cmd == math_shift_cmd) {
2405 check_second_math_shift();
2406 } else {
2407 check_display_math_end();
2409 run_mlist_to_hlist(p, false, text_style);
2410 a = hpack(vlink(temp_head), 0, additional, -1);
2411 build_attribute_list(a);
2412 unsave_math();
2413 decr(save_ptr); /* now |cur_group=math_shift_group| */
2414 assert(saved_type(0) == saved_eqno);
2415 if (saved_value(0) == 1)
2416 l = true;
2417 m = mode;
2418 p = fin_mlist(null);
2421 if (m < 0) {
2422 /* The |unsave| is done after everything else here; hence an appearance of
2423 `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
2424 particular \.\$'s. This is consistent with the conventions of
2425 `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
2426 space above that display.
2428 if (cur_cmd == math_shift_cs_cmd) {
2429 check_inline_math_end();
2431 tail_append(new_math(math_surround_par, before));
2432 /* begin mathskip code */
2433 if (! glue_is_zero(math_skip_par)) {
2434 copy_glue_values(tail,math_skip_par);
2435 surround(tail) = 0;
2437 /* end mathskip code */
2438 if (dir_math_save) {
2439 tail_append(new_dir(math_direction_par));
2441 run_mlist_to_hlist(p, (mode > 0), text_style);
2442 vlink(tail) = vlink(temp_head);
2443 while (vlink(tail) != null) {
2444 tail = vlink(tail);
2446 if (dir_math_save) {
2447 tail_append(new_dir(math_direction_par - dir_swap));
2449 dir_math_save = false;
2450 tail_append(new_math(math_surround_par, after));
2451 /* begin mathskip code */
2452 if (! glue_is_zero(math_skip_par)) {
2453 copy_glue_values(tail,math_skip_par);
2454 surround(tail) = 0;
2456 /* end mathskip code */
2457 space_factor_par = 1000;
2458 unsave_math();
2459 } else {
2460 if (a == null) {
2461 if (cur_cmd == math_shift_cmd) {
2462 check_second_math_shift();
2463 } else {
2464 check_display_math_end();
2467 run_mlist_to_hlist(p, false, display_style);
2468 finish_displayed_math(l, a, vlink(temp_head));
2472 @ When \.{\\halign} appears in a display, the alignment routines operate
2473 essentially as they do in vertical mode. Then the following program is
2474 activated, with |p| and |q| pointing to the beginning and end of the
2475 resulting list, and with |aux_save| holding the |prev_depth| value.
2478 void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
2480 do_assignments();
2481 if (cur_cmd == math_shift_cmd) {
2482 check_second_math_shift();
2483 } else {
2484 check_display_math_end();
2486 pop_nest();
2487 tail_append(new_penalty(pre_display_penalty_par));
2488 inject_display_skip_before(above_display_skip_code);
2489 vlink(tail) = p;
2490 if (p != null)
2491 tail = q;
2492 tail_append(new_penalty(post_display_penalty_par));
2493 inject_display_skip_after(below_display_skip_code);
2494 cur_list.prev_depth_field = saved_prevdepth;
2495 resume_after_display();
2498 @ Interface to \.{\\Umath} and \.{\\mathstyle}
2501 void setup_math_style(void)
2503 pointer q;
2504 tail_append(new_noad());
2505 q = new_node(math_char_node, 0);
2506 nucleus(tail) = q;
2507 (void) scan_math_style(nucleus(tail), num_style(m_style));
2510 @ @c
2511 void print_math_style(void)
2513 if (abs(mode) == mmode)
2514 print_int(m_style);
2515 else
2516 print_int(-1);