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