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