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