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
/>.
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
)
41 tex normally always inserts before and only after when larger than zero
50 #define display_skip_mode int_par
(math_display_skip_mode_code
)
52 #define math_skip glue_par
(math_skip_code
)
56 @ TODO
: not sure if this is the right order
58 #define back_error
(A
,B
) do
{ \
59 OK_to_interrupt
=false
; \
61 OK_to_interrupt
=true
; \
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
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''
162 static void unsave_math
(void
)
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
);
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;
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) {
208 tprint
("{assigning");
210 print_cmd_chr
(def_family_cmd
, size_id
);
213 print_font_identifier
(fam_fnt
(fam_id
, size_id
));
215 end_diagnostic
(false
);
220 static void unsave_math_fam_data
(int gl
)
223 if
(math_fam_head-
>stack
== NULL)
225 while
(math_fam_head-
>stack_ptr
> 0 &&
226 abs
(math_fam_head-
>stack
[math_fam_head-
>stack_ptr
].level
)
228 st
= math_fam_head-
>stack
[math_fam_head-
>stack_ptr
];
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;
236 tprint
("{restoring");
238 print_cmd_chr
(def_family_cmd
, size_id
);
241 print_font_identifier
(fam_fnt
(fam_id
, size_id
));
243 end_diagnostic
(false
);
246 (math_fam_head-
>stack_ptr
)--;
253 #define MATHPARAMSTACK
8
254 #define MATHPARAMDEFAULT undefined_math_parameter
256 static sa_tree math_param_head
= NULL;
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) {
267 tprint
("{assigning");
269 print_cmd_chr
(set_math_param_cmd
, param_id
);
270 print_cmd_chr
(math_style_cmd
, style_id
);
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
;
285 static void unsave_math_param_data
(int gl
)
288 if
(math_param_head-
>stack
== NULL)
290 while
(math_param_head-
>stack_ptr
> 0 &&
291 abs
(math_param_head-
>stack
[math_param_head-
>stack_ptr
].level
)
293 st
= math_param_head-
>stack
[math_param_head-
>stack_ptr
];
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;
301 tprint
("{restoring");
303 print_cmd_chr
(set_math_param_cmd
, param_id
);
304 print_cmd_chr
(math_style_cmd
, style_id
);
306 print_int
(get_math_param
(param_id
, style_id
));
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");
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
();
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
)
413 p
= new_node
(simple_noad
, ord_noad_type
);
414 /* all noad fields are zero after this
*/
419 pointer new_sub_box
(pointer curbox
)
423 q
= new_node
(sub_box_node
, 0);
425 math_list
(nucleus
(p
)) = curbox
;
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",
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",
499 "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
500 "closeopenspacing", "closeclosespacing", "closepunctspacing",
502 "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
503 "punctopenspacing", "punctclosespacing", "punctpunctspacing",
505 "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
506 "inneropenspacing", "innerclosespacing", "innerpunctspacing",
512 pointer new_style
(small_number s
)
513 { /* create a style node
*/
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
)
552 print_cmd_chr
(math_style_cmd
, subtype
(p
));
555 tprint_esc
("mathchoice");
557 show_node_list
(display_mlist
(p
));
560 show_node_list
(text_mlist
(p
));
563 show_node_list
(script_mlist
(p
));
566 show_node_list
(script_script_mlist
(p
));
572 display_normal_noad
(p
);
575 display_fence_noad
(p
);
578 display_fraction_noad
(p
);
581 tprint
("Unknown node type!");
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
*/
592 print_int
(math_fam
(p
));
594 print
(math_character
(p
));
598 static void print_delimiter
(pointer p
)
601 if
(delimiteroptionset
(p
)) {
603 if
(delimiteraxis
(p
))
605 if
(delimiternoaxis
(p
))
607 if
(delimiterexact
(p
))
611 if
(delimiterheight
(p
)) {
613 print_scaled
(delimiterheight
(p
));
616 if
(delimiterdepth
(p
)) {
618 print_scaled
(delimiterdepth
(p
));
621 if
(delimiterclass
(p
)) {
623 print_int
(delimiterclass
(p
));
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
);
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
).
650 static void print_subsidiary_data
(pointer p
, ASCII_code c
)
651 { /* display a noad field
*/
652 if
((int
) cur_length
>= depth_threshold
) {
656 append_char
(c
); /* include |c| in the recursion history
*/
661 print_current_string
();
662 print_fam_and_char
(p
);
665 show_node_list
(math_list
(p
));
668 if
(math_list
(p
) == null
) {
670 print_current_string
();
673 show_node_list
(math_list
(p
));
678 flush_char
(); /* remove |c| from the recursion history
*/
683 void display_normal_noad
(pointer p
)
687 switch
(subtype
(p
)) {
689 tprint_esc
("mathord");
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");
701 tprint_esc
("mathbin");
704 tprint_esc
("mathrel");
707 tprint_esc
("mathopen");
709 case close_noad_type
:
710 tprint_esc
("mathclose");
712 case punct_noad_type
:
713 tprint_esc
("mathpunct");
715 case inner_noad_type
:
716 tprint_esc
("mathinner");
719 tprint_esc
("overline");
721 case under_noad_type
:
722 tprint_esc
("underline");
724 case vcenter_noad_type
:
725 tprint_esc
("vcenter");
728 tprint
("<unknown noad type!>");
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)
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
)) {
751 print_scaled
(radicalwidth
(p
));
754 if
(radicaloptionset
(p
)) {
760 if
(radicalmiddle
(p
))
768 if
(top_accent_chr
(p
) != null
) {
769 if
(bot_accent_chr
(p
) != null
) {
770 tprint_esc
("Umathaccent both");
772 tprint_esc
("Umathaccent");
774 } else if
(bot_accent_chr
(p
) != null
) {
775 tprint_esc
("Umathaccent bottom");
777 tprint_esc
("Umathaccent overlay");
779 if
(accentfraction
(p
)) {
780 tprint
(" fraction=");
781 print_int
(accentfraction
(p
));
784 switch
(subtype
(p
)) {
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
));
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
));
796 print_fam_and_char
(overlay_accent_chr
(p
));
800 if
(top_accent_chr
(p
) != null
) {
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
));
807 confusion
("display_accent_noad");
811 if
(bot_accent_chr
(p
) != null
) {
812 if
(top_accent_chr
(p
) != null
) {
813 print_fam_and_char
(top_accent_chr
(p
));
816 print_fam_and_char
(bot_accent_chr
(p
));
818 confusion
("display_accent_noad");
822 if
(top_accent_chr
(p
) != null
&& bot_accent_chr(p) != null) {
824 print_fam_and_char
(top_accent_chr
(p
));
826 print_fam_and_char
(bot_accent_chr
(p
));
828 confusion
("display_accent_noad");
834 print_subsidiary_data
(nucleus
(p
), '.'
);
835 print_subsidiary_data
(supscr
(p
), '^'
);
836 print_subsidiary_data
(subscr
(p
), '_'
);
840 void display_fence_noad
(pointer p
)
842 if
(subtype
(p
) == right_noad_side
)
844 else if
(subtype
(p
) == left_noad_side
)
847 tprint_esc
("middle");
848 print_delimiter
(delimiter
(p
));
852 void display_fraction_noad
(pointer p
)
854 tprint_esc
("fraction, thickness ");
855 if
(thickness
(p
) == default_code
)
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
);
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
);
901 static void push_math
(group_code c
, int mstyle
)
903 if
(math_direction
!= text_direction
)
904 dir_math_save
= true
;
907 incompleat_noad
= null
;
909 new_save_level_math
(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
);
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
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
();
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
();
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
958 void start_eq_no
(void
)
960 set_saved_record
(0, saved_eqno
, 0, cur_chr
);
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
)
983 tail_append
(new_noad
());
984 q
= new_node
(math_char_node
, 0);
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|
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
)
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)))
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|
*/
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
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)
1063 n
= vinfo
(par_shape_ptr
+ 1);
1064 if
(prev_graf
+ 2 >= n
)
1065 p
= par_shape_ptr
+ 2 * n
+ 1;
1067 p
= par_shape_ptr
+ 2 * (prev_graf
+ 2) + 1;
1068 s
= varmem
[(p
- 1)].cint
;
1072 push_math
(math_shift_group
, display_style
);
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) {
1083 lua_node_filter_s
(buildpage_filter_callback
,lua_key_index
(before_display
));
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.",
1103 int mcls
= 0, msfam
= 0, mschr
= 0, mlfam
= 0, mlchr
= 0;
1104 if
(extcode
== tex_mathcode
) { /* \.
{\\delcode
}, this is the easiest
*/
1106 /* "MFCCFCC or "FCCFCC
*/
1108 mcls
= (cur_val
/ 0x1000000);
1109 cur_val
= (cur_val
& 0xFFFFFF);
1111 if
(cur_val
> 0xFFFFFF) {
1112 tex_error
("Invalid delimiter code", hlp
);
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> */
1129 if
(msfam
< 0 || msfam
> 255) {
1130 tex_error
("Invalid delimiter code", hlp
);
1136 } else if
(extcode
== umathnum_mathcode
) { /* \.
{\\Udelcodenum
} */
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
");
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);
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;
1167 void scan_extdef_del_code(int level, int extcode)
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));
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.
",
1189 int mcls = 0, mfam = 0, mchr = 0;
1190 if (extcode == tex_mathcode) { /* \.{\\mathcode} */
1193 if
(cur_val
> 0x8000) {
1195 tex_error
("Invalid math code", hlp
);
1198 /* needed for latex
: fallback to umathnum_mathcode
*/
1199 mfam
= (cur_val
/ 0x200000) & 0x7FF;
1202 mchr
= cur_val
& 0x1FFFFF;
1203 if
(mchr
> 0x10FFFF) {
1204 tex_error
("Invalid math code during > 0x8000 mathcode fallback", hlp
);
1211 snprintf
(errstr
, 255, "Bad mathchar (%d)", (int
)cur_val
);
1212 tex_error
(errstr
, hlp
);
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> */
1227 if
(mcls
< 0 || mcls
> 7 || mfam
> 255) {
1228 tex_error
("Invalid math code", hlp
);
1233 } else if
(extcode
== umathnum_mathcode
) {
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 */
1240 mfam = (cur_val / 0x200000) & 0x7FF;
1243 mchr = cur_val & 0x1FFFFF;
1244 if (mchr > 0x10FFFF) {
1245 tex_error("Invalid math code
", hlp);
1251 /* something's gone wrong */
1252 confusion("unknown_extcode
");
1254 d.class_value = mcls;
1255 d.family_value = mfam;
1256 d.character_value = mchr;
1261 void scan_extdef_math_code(int level, int extcode)
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)
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;
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)
1301 set_saved_record(0, saved_math, 0, p);
1303 push_math(math_group, mstyle);
1307 int scan_math(pointer p, int mstyle)
1309 /* label restart,reswitch,exit; */
1310 mathcodeval mval = { 0, 0, 0 };
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);
1333 cur_cmd = char_given_cmd;
1336 case math_char_num_cmd:
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);
1344 confusion("scan_math
");
1346 case math_given_cmd:
1347 mval = mathchar_from_integer(cur_chr, tex_mathcode);
1349 case xmath_given_cmd:
1350 mval = mathchar_from_integer(cur_chr, umath_mathcode);
1354 mval = scan_delimiter_as_mathchar(tex_mathcode);
1355 else if (cur_chr == 1)
1356 mval = scan_delimiter_as_mathchar(umath_mathcode);
1358 confusion("scan_math
");
1361 /* The pointer |p| is placed on |save_stack| while a complex subformula
1362 is being scanned. */
1365 set_saved_record(0, saved_math, 0, p);
1367 push_math(math_group, mstyle);
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;
1375 math_fam(p) = mval.family_value;
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);
1398 q = new_node(math_char_node, 0);
1400 math_character(nucleus(p)) = mval.character_value;
1401 math_fam(nucleus(p)) = mval.family_value;
1402 if (mval.class_value == var_code) {
1404 math_fam(nucleus(p)) = cur_fam;
1405 subtype(p) = ord_noad_type;
1407 switch (mval.class_value) {
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;
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);
1441 p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value);
1448 void math_math_comp(void)
1451 tail_append(new_noad());
1452 subtype(tail) = (quarterword) cur_chr;
1453 q = new_node(math_char_node, 0);
1455 if (cur_chr == over_noad_type)
1456 (void) scan_math(nucleus(tail), cramped_style(m_style));
1458 (void) scan_math(nucleus(tail), m_style);
1462 void math_limit_switch(void)
1464 const char *hlp[] = {
1465 "I'm ignoring this misplaced \\limits or \\nolimits command.
",
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;
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) {
1497 case other_char_cmd:
1498 dval = get_del_code(cur_chr);
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);
1506 confusion("scan_delimiter1
");
1509 dval.small_family_value = -1;
1513 confusion("scan_delimiter2
");
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
>'.
",
1527 back_error("Missing delimiter
(. inserted
)", hlp);
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;
1542 void math_radical(void)
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;
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 ;
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);
1582 confusion("math_radical
");
1583 if (chr_code == 7) {
1584 q = new_node(sub_box_node, 0); /* type will change */
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);
1593 if (!scan_math(degree(tail), sup_sup_style(m_style))) {
1594 vlink(degree(tail)) = null;
1595 q = new_node(math_char_node, 0);
1597 (void) scan_math(nucleus(tail), cramped_style(m_style));
1600 q = new_node(math_char_node, 0);
1602 (void) scan_math(nucleus(tail), cramped_style(m_style));
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.
)",
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
")) {
1628 t = scan_mathchar(umath_mathcode);
1629 } else if (scan_keyword("both
")) {
1631 if (scan_keyword("fixed
")) {
1634 t = scan_mathchar(umath_mathcode);
1635 if (scan_keyword("fixed
")) {
1638 b = scan_mathchar(umath_mathcode);
1639 } else if (scan_keyword("bottom
")) {
1641 if (scan_keyword("fixed
")) {
1644 b = scan_mathchar(umath_mathcode);
1645 } else if (scan_keyword("top
")) {
1647 if (scan_keyword("fixed
")) {
1650 t = scan_mathchar(umath_mathcode);
1651 } else if (scan_keyword("overlay
")) {
1653 if (scan_keyword("fixed
")) {
1656 o = scan_mathchar(umath_mathcode);
1659 t = scan_mathchar(umath_mathcode);
1661 if (scan_keyword("fraction
")) {
1663 accentfraction(tail) = cur_val;
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;
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;
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;
1693 math_fam(overlay_accent_chr(tail)) = o.family_value;
1695 q = new_node(math_char_node, 0);
1697 (void) scan_math(nucleus(tail), cramped_style(m_style));
1701 pointer math_vcenter_group(pointer p)
1705 subtype(q) = vcenter_noad_type;
1706 r = new_node(sub_box_node, 0);
1708 math_list(nucleus(q)) = p;
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());
1720 set_saved_record(-1, saved_choices, 0, 0);
1721 push_math(math_choice_group, display_style);
1726 void build_choices(void)
1728 pointer p; /* the current mlist */
1730 prev_style = m_style;
1732 p = fin_mlist(null);
1733 assert(saved_type(-1) == saved_choices);
1734 switch (saved_value(-1)) {
1736 display_mlist(tail) = p;
1739 text_mlist(tail) = p;
1742 script_mlist(tail) = p;
1745 script_script_mlist(tail) = p;
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));
1755 @ Subscripts and superscripts are attached to the previous nucleus by the
1756 action procedure called |sub_sup|.
1762 if (tail == head || (!scripts_allowed(tail))) {
1763 tail_append(new_noad());
1764 q = new_node(sub_mlist_node, 0);
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);
1775 tex_error("Double superscript
", hlp);
1777 q = new_node(math_char_node, 0);
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);
1788 tex_error("Double subscript
", hlp);
1790 q = new_node(math_char_node, 0);
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}'
1805 void math_fraction(void)
1807 halfword c; /* the type of generalized fraction we are scanning */
1809 halfword options = 0;
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
}'.
",
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);
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);
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) {
1849 if (scan_keyword("exact
")) {
1850 options = options | noad_option_exact ;
1855 fractionoptions(incompleat_noad) = options;
1856 scan_normal_dimen();
1857 thickness(incompleat_noad) = cur_val;
1860 thickness(incompleat_noad) = default_code;
1863 thickness(incompleat_noad) = 0;
1867 if (scan_keyword("exact
")) {
1868 options = options | noad_option_exact ;
1869 } else if (scan_keyword("noaxis
")) {
1870 options = options | noad_option_no_axis ;
1875 fractionoptions(incompleat_noad) = options;
1876 thickness(incompleat_noad) = 0;
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;
1896 q = new_node(sub_mlist_node, 0);
1897 denominator(incompleat_noad) = q;
1899 math_list(denominator(incompleat_noad)) = vlink(head);
1901 q = incompleat_noad;
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;
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;
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;
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));
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;
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) {
1958 while (vlink(q) != tail)
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;
1974 if (vlink(saved_value(0)) > 0) {
1976 q = new_node(math_char_node, 0);
1977 nucleus(vlink(saved_value(0))) = q;
1978 vlink(saved_value(0)) = null;
1980 (void) scan_math(saved_value(0), old_style);
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 */
1999 halfword options = 0;
2000 halfword type = -1 ;
2004 /* we have \Uleft \Uright \Umiddle */
2007 if (scan_keyword("height
")) {
2008 scan_dimen(false,false,false);
2010 } else if (scan_keyword("depth
")) {
2011 scan_dimen(false,false,false);
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
")) {
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.
",
2036 tex_error("Extra \\middle
", hlp);
2038 const char *hlp[] = {
2039 "I'm ignoring a \\right that had no matching \\left.
",
2042 tex_error("Extra \\right
", hlp);
2049 type(p) = fence_noad;
2050 subtype(p) = (quarterword) t;
2051 r = new_node(delim_node, 0);
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);
2067 math_list(nucleus(tail)) = p;
2071 if (t == left_noad_side) {
2077 if (t != right_noad_side) {
2078 push_math(math_left_group, m_style);
2083 tail_append(new_noad());
2084 subtype(tail) = inner_noad_type;
2085 r = new_node(sub_mlist_node, 0);
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)
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.
",
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.
",
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.
",
2127 tex_error("Inline math should end with \\Ustopmath
", hlp);
2132 static void resume_after_display(void)
2134 if (cur_group != math_shift_group)
2135 confusion("display
");
2137 prev_graf = prev_graf + 3;
2140 space_factor = 1000;
2141 tail_append(make_local_par_node()); /* this needs to be intercepted in
2142 the display math start ! */
2144 if (cur_cmd != spacer_cmd)
2146 if (nest_ptr == 1) {
2147 lua_node_filter_s(buildpage_filter_callback,lua_key_index(after_display));
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 */
2177 swap_dir = (int_par(pre_display_direction_code) < 0 ? true : false );
2178 if (eqno_box != null && swap_dir)
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);
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) {
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;
2214 eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
2215 subtype(eq_box) = equation_list; /* new */
2216 build_attribute_list(eq_box);
2219 if (eq_w > line_w) {
2220 list_ptr(eq_box) = null;
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);
2241 if (!is_char_node(p))
2242 if (type(p) == glue_node)
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;
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;
2269 append_to_vlist(eqno_box,lua_key_index(equation_number));
2270 tail_append(new_penalty(inf_penalty));
2272 switch (display_skip_mode) {
2273 case 0 : /* normal tex */
2274 tail_append(new_param_glue(g1));
2276 case 1 : /* always */
2277 tail_append(new_param_glue(g1));
2279 case 2 : /* non-zero */
2281 tail_append(new_param_glue(g1));
2283 case 3: /* ignore */
2289 r = new_kern(line_w - eq_w - eqno_w - d);
2292 if (math_direction==dir_TLT) {
2293 /* TRT + TLT + \eqno, (swap_dir=true, math_direction=TLT, l=true) */
2295 fprintf(stderr, "\nDEBUG
: CASE 1\n
");
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);
2302 /* TLT + TRT + \eqno, (swap_dir=true, math_direction=TRT, l=true) */
2304 fprintf(stderr, "\nDEBUG
: CASE 2\n
");
2306 try_couple_nodes(eqno_box,r);
2307 try_couple_nodes(r,eq_box);
2310 if (math_direction==dir_TLT) {
2311 /* TLT + TLT + \leqno, (swap_dir=false, math_direction=TLT, l=true) */ /* OK */
2313 fprintf(stderr, "\nDEBUG
: CASE 3\n
");
2315 s = new_kern(width(r) + eqno_w);
2317 /* TRT + TRT + \leqno, (swap_dir=false, math_direction=TRT, l=true) */
2319 fprintf(stderr, "\nDEBUG
: CASE 4\n
");
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);
2330 if (math_direction==dir_TLT) {
2331 /* TRT + TLT + \leqno, (swap_dir=true, math_direction=TLT, l=false) */
2333 fprintf(stderr, "\nDEBUG
: CASE 5\n
");
2336 /* TLT + TRT + \leqno, (swap_dir=true, math_direction=TRT, l=false) */
2338 fprintf(stderr, "\nDEBUG
: CASE 6\n
");
2341 try_couple_nodes(eq_box,r);
2342 try_couple_nodes(r,eqno_box);
2344 if (math_direction==dir_TLT) {
2345 /* TLT + TLT + \eqno, (swap_dir=false, math_direction=TLT, l=false) */ /* OK */
2347 fprintf(stderr, "\nDEBUG
: CASE 7\n
");
2351 /* TRT + TRT + \eqno, (swap_dir=false, math_direction=TRT, l=false) */
2353 fprintf(stderr, "\nDEBUG
: CASE 8\n
");
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);
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;
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 ;
2379 append_to_vlist(eqno_box,lua_key_index(equation_number));
2382 if (t != adjust_head) { /* migrating material comes after equation number */
2383 vlink(tail) = vlink(adjust_head);
2385 alink(adjust_tail) = alink(tail);
2388 if (pre_t != pre_adjust_head) {
2389 vlink(tail) = vlink(pre_adjust_head);
2391 alink(pre_adjust_tail) = alink(tail);
2394 tail_append(new_penalty(int_par(post_display_penalty_code)));
2396 switch (display_skip_mode) {
2397 case 0 : /* normal tex */
2399 tail_append(new_param_glue(g2));
2401 case 1 : /* always */
2402 tail_append(new_param_glue(g2));
2404 case 2 : /* non-zero */
2406 tail_append(new_param_glue(g2));
2408 case 3: /* ignore */
2412 resume_after_display();
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}' */
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)) {
2428 if (mode == -m) { /* end of equation number */
2429 if (cur_cmd == math_shift_cmd) {
2430 check_second_math_shift();
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);
2438 decr(save_ptr); /* now |cur_group=math_shift_group| */
2439 assert(saved_type(0) == saved_eqno);
2440 if (saved_value(0) == 1)
2443 p = fin_mlist(null);
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)
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;
2485 if (cur_cmd == math_shift_cmd) {
2486 check_second_math_shift();
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)
2505 if (cur_cmd == math_shift_cmd) {
2506 check_second_math_shift();
2508 check_display_math_end();
2511 tail_append(new_penalty(int_par(pre_display_penalty_code)));
2512 tail_append(new_param_glue(above_display_skip_code));
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)
2528 tail_append(new_noad());
2529 q = new_node(math_char_node, 0);
2531 (void) scan_math_style(nucleus(tail), num_style(m_style));
2535 void print_math_style(void)
2537 if (abs(mode) == mmode)