3
Copyright 2008-2009 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/>. */
23
static const char _svn_version[] =
24
"$Id: texmath.c 3264 2009-12-18 16:10:53Z taco $ "
25
"$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.50.0/source/texk/web2c/luatexdir/tex/texmath.c $";
27
#define mode cur_list.mode_field
28
#define head cur_list.head_field
29
#define tail cur_list.tail_field
30
#define prev_graf cur_list.pg_field
31
#define eTeX_aux cur_list.eTeX_aux_field
32
#define delim_ptr eTeX_aux
33
#define aux cur_list.aux_field
34
#define prev_depth aux.cint
35
#define space_factor aux.hh.lhfield
36
#define incompleat_noad aux.cint
38
#define cur_fam int_par(cur_fam_code)
39
#define text_direction int_par(text_direction_code)
43
extern void rawset_sa_item(sa_tree hed, int n, int v);
45
/* TODO: not sure if this is the right order */
46
#define back_error(A,B) do { \
47
OK_to_interrupt=false; \
49
OK_to_interrupt=true; \
53
int scan_math(pointer, int);
54
pointer fin_mlist(pointer);
56
#define pre_display_size dimen_par(pre_display_size_code)
57
#define hsize dimen_par(hsize_code)
58
#define display_width dimen_par(display_width_code)
59
#define display_indent dimen_par(display_indent_code)
60
#define math_surround dimen_par(math_surround_code)
61
#define hang_indent dimen_par(hang_indent_code)
62
#define hang_after int_par(hang_after_code)
63
#define every_math equiv(every_math_loc)
64
#define every_display equiv(every_display_loc)
65
#define par_shape_ptr equiv(par_shape_loc)
68
When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
69
{\sl mlist}, which is essentially a tree structure representing that
70
formula. An mlist is a linear sequence of items, but we can regard it as
71
a tree structure because mlists can appear within mlists. For example, many
72
of the entries can be subscripted or superscripted, and such ``scripts''
73
are mlists in their own right.
75
An entire formula is parsed into such a tree before any of the actual
76
typesetting is done, because the current style of type is usually not
77
known until the formula has been fully scanned. For example, when the
78
formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
79
that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
81
During the scanning process, each element of the mlist being built is
82
classified as a relation, a binary operator, an open parenthesis, etc.,
83
or as a construct like `\.{\\sqrt}' that must be built up. This classification
84
appears in the mlist data structure.
86
After a formula has been fully scanned, the mlist is converted to an hlist
87
so that it can be incorporated into the surrounding text. This conversion is
88
controlled by a recursive procedure that decides all of the appropriate
89
styles by a ``top-down'' process starting at the outermost level and working
90
in towards the subformulas. The formula is ultimately pasted together using
91
combinations of horizontal and vertical boxes, with glue and penalty nodes
92
inserted as necessary.
94
An mlist is represented internally as a linked list consisting chiefly
95
of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
96
similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
97
allowed to appear in mlists together with the noads; \TeX\ tells the difference
98
by means of the |type| field, since a noad's |type| is always greater than
99
that of a node. An mlist does not contain character nodes, hlist nodes, vlist
100
nodes, math nodes or unset nodes; in particular, each mlist item appears in the
101
variable-size part of |mem|, so the |type| field is always present.
103
@ Each noad is five or more words long. The first word contains the
104
|type| and |subtype| and |link| fields that are already so familiar to
105
us; the second contains the attribute list pointer, and the third,
106
fourth an fifth words are called the noad's |nucleus|, |subscr|, and
107
|supscr| fields. (This use of a combined attribute list is temporary.
108
Eventually, each of fields need their own list)
110
Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
111
parsed into an mlist containing a single element called an |ord_noad|.
112
The |nucleus| of this noad is a representation of `\.x', the |subscr| is
113
empty, and the |supscr| is a representation of `\.2'.
115
The |nucleus|, |subscr|, and |supscr| fields are further broken into
116
subfields. If |p| points to a noad, and if |q| is one of its principal
117
fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the
118
corresponding attribute of noad |p| is not present). Otherwise, there are
119
several possibilities for the subfields, depending on the |type| of |q|.
121
\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of
122
the sixteen font families, and |character(q)| is the number of a character
123
within a font of that family, as in a character node.
125
\yskip\hang|type(q)=math_text_char_node| is similar, but the character is
126
unsubscripted and unsuperscripted and it is followed immediately by another
127
character from the same font. (This |type| setting appears only
128
briefly during the processing; it is used to suppress unwanted italic
131
\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box
132
node (either an |hlist_node| or a |vlist_node|) that should be used as the
133
value of the field. The |shift_amount| in the subsidiary box node is the
134
amount by which that box will be shifted downward.
136
\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to
137
an mlist; the mlist must be converted to an hlist in order to obtain
138
the value of this field.
140
\yskip\noindent In the latter case, we might have |math_list(q)=null|. This
141
is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}'
142
and `\.{\$P\$}' produce different results (the former will not have the
143
``italic correction'' added to the width of |P|, but the ``script skip''
149
void unsave_math(void)
153
flush_node_list(text_dir_ptr);
154
assert(saved_type(0) == saved_textdir);
155
text_dir_ptr = saved_value(0);
160
Sometimes it is necessary to destroy an mlist. The following
161
subroutine empties the current list, assuming that |abs(mode)=mmode|.
164
void flush_math(void)
166
flush_node_list(vlink(head));
167
flush_node_list(incompleat_noad);
170
incompleat_noad = null;
173
/* Before we can do anything in math mode, we need fonts. */
175
#define MATHFONTSTACK 8
176
#define MATHFONTDEFAULT 0 /* == nullfont */
178
static sa_tree math_fam_head = NULL;
180
int fam_fnt(int fam_id, int size_id)
182
int n = fam_id + (256 * size_id);
183
return (int) get_sa_item(math_fam_head, n);
186
void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
188
int n = fam_id + (256 * size_id);
189
set_sa_item(math_fam_head, n, f, lvl);
190
fixup_math_parameters(fam_id, size_id, f, lvl);
191
if (int_par(tracing_assigns_code) > 0) {
193
tprint("{assigning");
195
print_cmd_chr(def_family_cmd, size_id);
198
print_font_identifier(fam_fnt(fam_id, size_id));
200
end_diagnostic(false);
204
void unsave_math_fam_data(int gl)
207
if (math_fam_head->stack == NULL)
209
while (math_fam_head->stack_ptr > 0 &&
210
abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
212
st = math_fam_head->stack[math_fam_head->stack_ptr];
214
rawset_sa_item(math_fam_head, st.code, st.value);
215
/* now do a trace message, if requested */
216
if (int_par(tracing_restores_code) > 0) {
217
int size_id = st.code / 256;
218
int fam_id = st.code % 256;
220
tprint("{restoring");
222
print_cmd_chr(def_family_cmd, size_id);
225
print_font_identifier(fam_fnt(fam_id, size_id));
227
end_diagnostic(false);
230
(math_fam_head->stack_ptr)--;
238
#define MATHPARAMSTACK 8
239
#define MATHPARAMDEFAULT undefined_math_parameter
241
static sa_tree math_param_head = NULL;
243
void def_math_param(int param_id, int style_id, scaled value, int lvl)
245
int n = param_id + (256 * style_id);
246
set_sa_item(math_param_head, n, value, lvl);
247
if (int_par(tracing_assigns_code) > 0) {
249
tprint("{assigning");
251
print_cmd_chr(set_math_param_cmd, param_id);
252
print_cmd_chr(math_style_cmd, style_id);
256
end_diagnostic(false);
260
scaled get_math_param(int param_id, int style_id)
262
int n = param_id + (256 * style_id);
263
return (scaled) get_sa_item(math_param_head, n);
267
void unsave_math_param_data(int gl)
270
if (math_param_head->stack == NULL)
272
while (math_param_head->stack_ptr > 0 &&
273
abs(math_param_head->stack[math_param_head->stack_ptr].level)
275
st = math_param_head->stack[math_param_head->stack_ptr];
277
rawset_sa_item(math_param_head, st.code, st.value);
278
/* now do a trace message, if requested */
279
if (int_par(tracing_restores_code) > 0) {
280
int param_id = st.code % 256;
281
int style_id = st.code / 256;
283
tprint("{restoring");
285
print_cmd_chr(set_math_param_cmd, param_id);
286
print_cmd_chr(math_style_cmd, style_id);
288
print_int(get_math_param(param_id, style_id));
290
end_diagnostic(false);
293
(math_param_head->stack_ptr)--;
298
/* saving and unsaving of both */
300
void unsave_math_data(int gl)
302
unsave_math_fam_data(gl);
303
unsave_math_param_data(gl);
306
void dump_math_data(void)
308
if (math_fam_head == NULL)
309
math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT);
310
dump_sa_tree(math_fam_head);
311
if (math_param_head == NULL)
312
math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT);
313
dump_sa_tree(math_param_head);
316
void undump_math_data(void)
318
math_fam_head = undump_sa_tree();
319
math_param_head = undump_sa_tree();
324
void initialize_math(void)
326
if (math_fam_head == NULL)
327
math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT);
328
if (math_param_head == NULL) {
329
math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT);
330
initialize_math_spacing();
337
@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
338
Clo, Pun, or Inn, for purposes of spacing and line breaking. An
339
|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
340
|punct_noad|, or |inner_noad| is used to represent portions of the various
341
types. For example, an `\.=' sign in a formula leads to the creation of a
342
|rel_noad| whose |nucleus| field is a representation of an equals sign
343
(usually |fam=0|, |character=@'75|). A formula preceded by \.{\\mathrel}
344
also results in a |rel_noad|. When a |rel_noad| is followed by an
345
|op_noad|, say, and possibly separated by one or more ordinary nodes (not
346
noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
347
just after the formula that corresponds to the |rel_noad|, unless there
348
already was a penalty immediately following; and a ``thick space'' will be
349
inserted just before the formula that corresponds to the |op_noad|.
351
A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
352
has a |subtype=normal|. The only exception is that an |op_noad| might
353
have |subtype=limits| or |no_limits|, if the normal positioning of
354
limits has been overridden for this operator.
356
A |radical_noad| also has a |left_delimiter| field, which usually
357
represents a square root sign.
359
A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
361
Delimiter fields have four subfields
362
called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
363
represent variable-size delimiters by giving the ``small'' and ``large''
364
starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
365
@:TeXbook}{\sl The \TeX book@>
367
A |fraction_noad| is actually quite different from all other noads.
368
It has |thickness|, |denominator|, and |numerator| fields instead of
369
|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value
370
that tells how thick to make a fraction rule; however, the special
371
value |default_code| is used to stand for the
372
|default_rule_thickness| of the current size. The |numerator| and
373
|denominator| point to mlists that define a fraction; we always have
374
$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The
375
|left_delimiter| and |right_delimiter| fields specify delimiters that will
376
be placed at the left and right of the fraction. In this way, a
377
|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
378
\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
379
\.{\\abovewithdelims}.
383
/* The |new_noad| function creates an |ord_noad| that is completely null */
385
pointer new_noad(void)
388
p = new_node(simple_noad, ord_noad_type);
389
/* all noad fields are zero after this */
393
pointer new_sub_box(pointer cur_box)
397
q = new_node(sub_box_node, 0);
399
math_list(nucleus(p)) = cur_box;
404
@ A few more kinds of noads will complete the set: An |under_noad| has its
405
nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
406
an accent over its nucleus; the accent character appears as
407
|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad|
408
centers its nucleus vertically with respect to the axis of the formula;
409
in such noads we always have |type(nucleus(p))=sub_box|.
411
And finally, we have the |fence_noad| type, to implement
412
\TeX's \.{\\left} and \.{\\right} as well as \eTeX's \.{\\middle}.
413
The |nucleus| of such noads is
414
replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
415
a |fence_noad| such that |delimiter(p)| holds the family and character
416
codes for all left parentheses. A |fence_noad| of subtype |left_noad_side|
417
never appears in an mlist except as the first element, and a |fence_noad|
418
with subtype |right_noad_side| never appears in an mlist
419
except as the last element; furthermore, we either have both a |left_noad_side|
420
and a |right_noad_side|, or neither one is present.
424
@ Math formulas can also contain instructions like \.{\\textstyle} that
425
override \TeX's normal style rules. A |style_node| is inserted into the
426
data structure to record such instructions; it is three words long, so it
427
is considered a node instead of a noad. The |subtype| is either |display_style|
428
or |text_style| or |script_style| or |script_script_style|. The
429
second and third words of a |style_node| are not used, but they are
430
present because a |choice_node| is converted to a |style_node|.
432
\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
433
|display_style|, \dots, |script_script_style|, and adds~1 to get the
434
``cramped'' versions of these styles. This gives a numerical order that
435
is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
436
i.e., a smaller style has a larger numerical value.
437
@:TeXbook}{\sl The \TeX book@>
440
const char *math_style_names[] = {
441
"display", "crampeddisplay",
442
"text", "crampedtext",
443
"script", "crampedscript",
444
"scriptscript", "crampedscriptscript",
448
const char *math_param_names[] = {
449
"quad", "axis", "operatorsize",
450
"overbarkern", "overbarrule", "overbarvgap",
451
"underbarkern", "underbarrule", "underbarvgap",
452
"radicalkern", "radicalrule", "radicalvgap",
453
"radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
454
"stackvgap", "stacknumup", "stackdenomdown",
455
"fractionrule", "fractionnumvgap", "fractionnumup",
456
"fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
457
"limitabovevgap", "limitabovebgap", "limitabovekern",
458
"limitbelowvgap", "limitbelowbgap", "limitbelowkern",
459
"underdelimitervgap", "underdelimiterbgap",
460
"overdelimitervgap", "overdelimiterbgap",
461
"subshiftdrop", "supshiftdrop", "subshiftdown",
462
"subsupshiftdown", "subtopmax", "supshiftup",
463
"supbottommin", "supsubbottommax", "subsupvgap",
464
"spaceafterscript", "connectoroverlapmin",
465
"ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
466
"ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
467
"opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
468
"opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
469
"binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
470
"binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
471
"relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
472
"relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
473
"openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
474
"openopenspacing", "openclosespacing", "openpunctspacing",
476
"closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
477
"closeopenspacing", "closeclosespacing", "closepunctspacing",
479
"punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
480
"punctopenspacing", "punctclosespacing", "punctpunctspacing",
482
"innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
483
"inneropenspacing", "innerclosespacing", "innerpunctspacing",
488
pointer new_style(small_number s)
489
{ /* create a style node */
491
return new_node(style_node, s);
494
/* Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
495
has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
496
and |script_script_mlist| pointing to the mlists for each style.
500
pointer new_choice(void)
501
{ /* create a choice node */
502
return new_node(choice_node, 0); /* the |subtype| is not used */
506
@ Let's consider now the previously unwritten part of |show_node_list|
507
that displays the things that can only be present in mlists; this
508
program illustrates how to access the data structures just defined.
510
In the context of the following program, |p| points to a node or noad that
511
should be displayed, and the current string contains the ``recursion history''
512
that leads to this point. The recursion history consists of a dot for each
513
outer level in which |p| is subsidiary to some node, or in which |p| is
514
subsidiary to the |nucleus| field of some noad; the dot is replaced by
515
`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
516
or |supscr| or |denominator| or |numerator| fields of noads. For example,
517
the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
518
|x| in the (ridiculous) formula
519
`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
523
void display_normal_noad(pointer p); /* forward */
524
void display_fence_noad(pointer p); /* forward */
525
void display_fraction_noad(pointer p); /* forward */
527
void show_math_node(pointer p)
531
print_cmd_chr(math_style_cmd, subtype(p));
534
tprint_esc("mathchoice");
536
show_node_list(display_mlist(p));
539
show_node_list(text_mlist(p));
542
show_node_list(script_mlist(p));
545
show_node_list(script_script_mlist(p));
551
display_normal_noad(p);
554
display_fence_noad(p);
557
display_fraction_noad(p);
560
tprint("Unknown node type!");
566
/* Here are some simple routines used in the display of noads. */
568
void print_fam_and_char(pointer p)
569
{ /* prints family and character */
571
print_int(math_fam(p));
573
print(math_character(p));
576
void print_delimiter(pointer p)
579
if (small_fam(p) < 0) {
580
print_int(-1); /* this should never happen */
581
} else if (small_fam(p) < 16 && large_fam(p) < 16 &&
582
small_char(p) < 256 && large_char(p) < 256) {
583
/* traditional tex style */
584
a = small_fam(p) * 256 + small_char(p);
585
a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
587
} else if ((large_fam(p) == 0 && large_char(p) == 0) ||
588
small_char(p) > 65535 || large_char(p) > 65535) {
589
/* modern xetex/luatex style */
590
print_hex(small_fam(p));
591
print_hex(small_char(p));
593
/* assume this is omega-style */
594
a = small_fam(p) * 65536 + small_char(p);
596
a = large_fam(p) * 65536 + large_char(p);
602
@ The next subroutine will descend to another level of recursion when a
603
subsidiary mlist needs to be displayed. The parameter |c| indicates what
604
character is to become part of the recursion history. An empty mlist is
605
distinguished from a missing field, because these are not equivalent
606
(as explained above).
611
void print_subsidiary_data(pointer p, ASCII_code c)
612
{ /* display a noad field */
613
if ((int) cur_length >= depth_threshold) {
617
append_char(c); /* include |c| in the recursion history */
622
print_current_string();
623
print_fam_and_char(p);
626
show_node_list(math_list(p));
629
if (math_list(p) == null) {
631
print_current_string();
634
show_node_list(math_list(p));
639
flush_char(); /* remove |c| from the recursion history */
643
void display_normal_noad(pointer p)
647
switch (subtype(p)) {
649
tprint_esc("mathord");
651
case op_noad_type_normal:
652
case op_noad_type_limits:
653
case op_noad_type_no_limits:
654
tprint_esc("mathop");
655
if (subtype(p) == op_noad_type_limits)
656
tprint_esc("limits");
657
else if (subtype(p) == op_noad_type_no_limits)
658
tprint_esc("nolimits");
661
tprint_esc("mathbin");
664
tprint_esc("mathrel");
667
tprint_esc("mathopen");
669
case close_noad_type:
670
tprint_esc("mathclose");
672
case punct_noad_type:
673
tprint_esc("mathpunct");
675
case inner_noad_type:
676
tprint_esc("mathinner");
679
tprint_esc("overline");
681
case under_noad_type:
682
tprint_esc("underline");
684
case vcenter_noad_type:
685
tprint_esc("vcenter");
688
tprint("<unknown noad type!>");
694
tprint_esc("Udelimiterover");
695
else if (subtype(p) == 6)
696
tprint_esc("Udelimiterunder");
697
else if (subtype(p) == 5)
698
tprint_esc("Uoverdelimiter");
699
else if (subtype(p) == 4)
700
tprint_esc("Uunderdelimiter");
701
else if (subtype(p) == 3)
704
tprint_esc("radical");
705
print_delimiter(left_delimiter(p));
706
if (degree(p) != null) {
707
print_subsidiary_data(degree(p), '/');
711
if (accent_chr(p) != null) {
712
if (bot_accent_chr(p) != null) {
713
tprint_esc("Umathaccents");
714
print_fam_and_char(accent_chr(p));
715
print_fam_and_char(bot_accent_chr(p));
717
tprint_esc("accent");
718
print_fam_and_char(accent_chr(p));
721
tprint_esc("Umathbotaccent");
722
print_fam_and_char(bot_accent_chr(p));
726
print_subsidiary_data(nucleus(p), '.');
727
print_subsidiary_data(supscr(p), '^');
728
print_subsidiary_data(subscr(p), '_');
731
void display_fence_noad(pointer p)
733
if (subtype(p) == right_noad_side)
735
else if (subtype(p) == left_noad_side)
738
tprint_esc("middle");
739
print_delimiter(delimiter(p));
742
void display_fraction_noad(pointer p)
744
tprint_esc("fraction, thickness ");
745
if (thickness(p) == default_code)
748
print_scaled(thickness(p));
749
if ((left_delimiter(p) != null) &&
750
((small_fam(left_delimiter(p)) != 0) ||
751
(small_char(left_delimiter(p)) != 0) ||
752
(large_fam(left_delimiter(p)) != 0) ||
753
(large_char(left_delimiter(p)) != 0))) {
754
tprint(", left-delimiter ");
755
print_delimiter(left_delimiter(p));
757
if ((right_delimiter(p) != null) &&
758
((small_fam(right_delimiter(p)) != 0) ||
759
(small_char(right_delimiter(p)) != 0) ||
760
(large_fam(right_delimiter(p)) != 0) ||
761
(large_char(right_delimiter(p)) != 0))) {
762
tprint(", right-delimiter ");
763
print_delimiter(right_delimiter(p));
765
print_subsidiary_data(numerator(p), '\\');
766
print_subsidiary_data(denominator(p), '/');
770
The routines that \TeX\ uses to create mlists are similar to those we have
771
just seen for the generation of hlists and vlists. But it is necessary to
772
make ``noads'' as well as nodes, so the reader should review the
773
discussion of math mode data structures before trying to make sense out of
774
the following program.
776
Here is a little routine that needs to be done whenever a subformula
777
is about to be processed. The parameter is a code like |math_group|.
780
void new_save_level_math(group_code c)
782
set_saved_record(0, saved_textdir, 0, text_dir_ptr);
783
text_dir_ptr = new_dir(math_direction);
786
eq_word_define(int_base + body_direction_code, math_direction);
787
eq_word_define(int_base + par_direction_code, math_direction);
788
eq_word_define(int_base + text_direction_code, math_direction);
791
void push_math(group_code c, int mstyle)
793
if (math_direction != text_direction)
794
dir_math_save = true;
797
incompleat_noad = null;
799
new_save_level_math(c);
802
void enter_ordinary_math(void)
804
push_math(math_shift_group, text_style);
805
eq_word_define(int_base + cur_fam_code, -1);
806
if (every_math != null)
807
begin_token_list(every_math, every_math_text);
810
void enter_display_math(void);
812
/* We get into math mode from horizontal mode when a `\.\$' (i.e., a
813
|math_shift| character) is scanned. We must check to see whether this
814
`\.\$' is immediately followed by another, in case display math mode is
820
if (cur_cmd == math_shift_cmd) {
821
get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
822
if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
823
enter_display_math();
826
enter_ordinary_math();
828
} else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style) {
829
enter_display_math();
830
} else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
831
enter_ordinary_math();
838
We get into ordinary math mode from display math mode when `\.{\\eqno}' or
839
`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
840
the value of |cur_chr| is placed onto |save_stack| for safe keeping.
845
When \TeX\ is in display math mode, |cur_group=math_shift_group|,
846
so it is not necessary for the |start_eq_no| procedure to test for
850
void start_eq_no(void)
852
set_saved_record(0, saved_eqno, 0, cur_chr);
854
enter_ordinary_math();
858
Subformulas of math formulas cause a new level of math mode to be entered,
859
on the semantic nest as well as the save stack. These subformulas arise in
860
several ways: (1)~A left brace by itself indicates the beginning of a
861
subformula that will be put into a box, thereby freezing its glue and
862
preventing line breaks. (2)~A subscript or superscript is treated as a
863
subformula if it is not a single character; the same applies to
864
the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
865
initiates a subformula that will be terminated by a matching \.{\\right}.
866
The group codes placed on |save_stack| in these three cases are
867
|math_group|, |math_group|, and |math_left_group|, respectively.
869
Here is the code that handles case (1); the other cases are not quite as
870
trivial, so we shall consider them later.
873
void math_left_brace(void)
876
tail_append(new_noad());
877
q = new_node(math_char_node, 0);
880
(void) scan_math(nucleus(tail), m_style);
884
When we enter display math mode, we need to call |line_break| to
885
process the partial paragraph that has just been interrupted by the
886
display. Then we can set the proper values of |display_width| and
887
|display_indent| and |pre_display_size|.
891
void enter_display_math(void)
893
scaled w; /* new or partial |pre_display_size| */
894
scaled l; /* new |display_width| */
895
scaled s; /* new |display_indent| */
897
int n; /* scope of paragraph shape specification */
898
if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
899
(vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
900
type(tail) == whatsit_node &&
901
subtype(tail) == local_par_node && vlink(tail) == null)) {
902
if (vlink(head) == tail) {
903
/* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
904
there is another display immediately following, we have to get rid
911
line_break(true, math_shift_group);
912
w = actual_box_width(just_box, (2 * quad(get_cur_font())));
914
/* now we are in vertical mode, working on the list that will contain the display */
915
/* A displayed equation is considered to be three lines long, so we
916
calculate the length and offset of line number |prev_graf+2|. */
917
if (par_shape_ptr == null) {
918
if ((hang_indent != 0) &&
919
(((hang_after >= 0) && (prev_graf + 2 > hang_after)) ||
920
(prev_graf + 1 < -hang_after))) {
921
l = hsize - abs(hang_indent);
931
n = vinfo(par_shape_ptr + 1);
932
if (prev_graf + 2 >= n)
933
p = par_shape_ptr + 2 * n + 1;
935
p = par_shape_ptr + 2 * (prev_graf + 2) + 1;
936
s = varmem[(p - 1)].cint;
940
push_math(math_shift_group, display_style);
942
eq_word_define(int_base + cur_fam_code, -1);
943
eq_word_define(dimen_base + pre_display_size_code, w);
944
eq_word_define(dimen_base + display_width_code, l);
945
eq_word_define(dimen_base + display_indent_code, s);
946
if (every_display != null)
947
begin_token_list(every_display, every_display_text);
950
lua_node_filter_s(buildpage_filter_callback, "before_display");
955
/* The next routine parses all variations of a delimiter code. The |extcode|
956
* tells what syntax form to use (\TeX, \Aleph, \XeTeX, \XeTeXnum, ...) , the
957
* |doclass| tells whether or not read a math class also (for \delimiter c.s.).
958
* (the class is passed on for conversion to |\mathchar|).
961
#define fam_in_range ((cur_fam>=0)&&(cur_fam<256))
963
delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
966
"I'm going to use 0 instead of that illegal code value.",
970
int cur_val1; /* and the global |cur_val| */
971
int mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
973
if (extcode == tex_mathcode) { /* \delcode, this is the easiest */
975
/* "MFCCFCC or "FCCFCC */
977
mcls = (cur_val / 0x1000000);
978
cur_val = (cur_val & 0xFFFFFF);
980
if (cur_val > 0xFFFFFF) {
981
tex_error("Invalid delimiter code", hlp);
984
msfam = (cur_val / 0x100000);
985
mschr = (cur_val % 0x100000) / 0x1000;
986
mlfam = (cur_val & 0xFFF) / 0x100;
987
mlchr = (cur_val % 0x100);
988
} else if (extcode == aleph_mathcode) { /* \odelcode */
989
/* "MFFCCCC"FFCCCC or "FFCCCC"FFCCCC */
992
mcls = (cur_val / 0x1000000);
993
cur_val = (cur_val & 0xFFFFFF);
997
if ((cur_val1 > 0xFFFFFF) || (cur_val > 0xFFFFFF)) {
998
tex_error("Invalid delimiter code", hlp);
1002
msfam = (cur_val1 / 0x10000);
1003
mschr = (cur_val1 % 0x10000);
1004
mlfam = (cur_val / 0x10000);
1005
mlchr = (cur_val % 0x10000);
1006
} else if (extcode == xetex_mathcode) { /* \Udelcode */
1007
/* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */
1016
if (msfam < 0 || msfam > 255) {
1017
tex_error("Invalid delimiter code", hlp);
1023
} else if (extcode == xetexnum_mathcode) { /* \Udelcodenum */
1025
/* the largest numeric value is $2^29-1$, but
1026
the top of bit 21 can't be used as it contains invalid USV's
1028
if (doclass) { /* such a primitive doesn't exist */
1029
confusion("xetexnum_mathcode");
1032
msfam = (cur_val / 0x200000);
1033
mschr = cur_val & 0x1FFFFF;
1034
if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
1035
tex_error("Invalid delimiter code", hlp);
1042
/* something's gone wrong */
1043
confusion("unknown_extcode");
1045
d.origin_value = extcode;
1046
d.class_value = mcls;
1047
d.small_family_value = msfam;
1048
d.small_character_value = mschr;
1049
d.large_family_value = mlfam;
1050
d.large_character_value = mlchr;
1054
void scan_extdef_del_code(int level, int extcode)
1060
scan_optional_equals();
1061
d = do_scan_extdef_del_code(extcode, false);
1062
set_del_code(p, extcode, d.small_family_value, d.small_character_value,
1063
d.large_family_value, d.large_character_value, level);
1066
mathcodeval scan_mathchar(int extcode)
1069
"I'm going to use 0 instead of that illegal code value.",
1073
int mcls = 0, mfam = 0, mchr = 0;
1074
if (extcode == tex_mathcode) { /* \mathcode */
1077
if (cur_val > 0x8000) {
1078
tex_error("Invalid math code", hlp);
1081
mcls = (cur_val / 0x1000);
1082
mfam = ((cur_val % 0x1000) / 0x100);
1083
mchr = (cur_val % 0x100);
1084
} else if (extcode == aleph_mathcode) { /* \omathcode */
1087
if (cur_val > 0x8000000) {
1088
tex_error("Invalid math code", hlp);
1091
mcls = (cur_val / 0x1000000);
1092
mfam = ((cur_val % 0x1000000) / 0x10000);
1093
mchr = (cur_val % 0x10000);
1094
} else if (extcode == xetex_mathcode) {
1095
/* <0-0x7> <0-0xFF> <0-0x10FFFF> */
1102
if (mcls < 0 || mcls > 7 || mfam > 255) {
1103
tex_error("Invalid math code", hlp);
1108
} else if (extcode == xetexnum_mathcode) {
1110
/* the largest numeric value is $2^32-1$, but
1111
the top of bit 21 can't be used as it contains invalid USV's
1113
/* Note: |scan_int| won't accept families 128-255 because these use bit 32 */
1115
mfam = (cur_val / 0x200000) & 0x7FF;
1118
mchr = cur_val & 0x1FFFFF;
1119
if (mchr > 0x10FFFF) {
1120
tex_error("Invalid math code", hlp);
1126
/* something's gone wrong */
1127
confusion("unknown_extcode");
1129
d.class_value = mcls;
1130
d.family_value = mfam;
1131
d.origin_value = extcode;
1132
d.character_value = mchr;
1137
void scan_extdef_math_code(int level, int extcode)
1143
scan_optional_equals();
1144
d = scan_mathchar(extcode);
1145
set_math_code(p, extcode, d.class_value,
1146
d.family_value, d.character_value, level);
1150
/* this reads in a delcode when actually a mathcode is needed */
1152
mathcodeval scan_delimiter_as_mathchar(int extcode)
1156
dval = do_scan_extdef_del_code(extcode, true);
1157
mval.origin_value = 0;
1158
mval.class_value = dval.class_value;
1159
mval.family_value = dval.small_family_value;
1160
mval.character_value = dval.small_character_value;
1164
/* this has to match the inverse routine in the pascal code
1165
* where the |\Umathchardef| is executed
1168
mathcodeval mathchar_from_integer(int value, int extcode)
1171
mval.origin_value = extcode;
1172
if (extcode == tex_mathcode) {
1173
mval.class_value = (value / 0x1000);
1174
mval.family_value = ((value % 0x1000) / 0x100);
1175
mval.character_value = (value % 0x100);
1176
} else if (extcode == aleph_mathcode) {
1177
mval.class_value = (value / 0x1000000);
1178
mval.family_value = ((value % 0x1000000) / 0x10000);
1179
mval.character_value = (value % 0x10000);
1180
} else { /* some xetexended xetex thing */
1181
int mfam = (value / 0x200000) & 0x7FF;
1182
mval.class_value = mfam % 0x08;
1183
mval.family_value = mfam / 0x08;
1184
mval.character_value = value & 0x1FFFFF;
1189
/* Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad
1190
are broken down into subfields called |type| and either |math_list| or
1191
|(math_fam,math_character)|. The job of |scan_math| is to figure out
1192
what to place in one of these principal fields; it looks at the
1193
subformula that comes next in the input, and places an encoding of
1194
that subformula into a given word of |mem|.
1198
#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
1201
int scan_math(pointer p, int mstyle)
1203
/* label restart,reswitch,exit; */
1211
case other_char_cmd:
1212
case char_given_cmd:
1213
mval = get_math_code(cur_chr);
1214
if (mval.class_value == 8) {
1215
/* An active character that is an |outer_call| is allowed here */
1216
cur_cs = active_to_cs(cur_chr, true);
1217
cur_cmd = eq_type(cur_cs);
1218
cur_chr = equiv(cur_cs);
1227
cur_cmd = char_given_cmd;
1230
case math_char_num_cmd:
1232
mval = scan_mathchar(tex_mathcode);
1233
else if (cur_chr == 1)
1234
mval = scan_mathchar(aleph_mathcode);
1235
else if (cur_chr == 2)
1236
mval = scan_mathchar(xetex_mathcode);
1237
else if (cur_chr == 3)
1238
mval = scan_mathchar(xetexnum_mathcode);
1240
confusion("scan_math");
1242
case math_given_cmd:
1243
mval = mathchar_from_integer(cur_chr, tex_mathcode);
1245
case omath_given_cmd:
1246
mval = mathchar_from_integer(cur_chr, aleph_mathcode);
1248
case xmath_given_cmd:
1249
mval = mathchar_from_integer(cur_chr, xetex_mathcode);
1253
mval = scan_delimiter_as_mathchar(tex_mathcode);
1254
else if (cur_chr == 1)
1255
mval = scan_delimiter_as_mathchar(aleph_mathcode);
1256
else if (cur_chr == 2)
1257
mval = scan_delimiter_as_mathchar(xetex_mathcode);
1259
confusion("scan_math");
1262
/* The pointer |p| is placed on |save_stack| while a complex subformula
1263
is being scanned. */
1266
set_saved_record(0, saved_math, 0, p);
1268
push_math(math_group, mstyle);
1271
type(p) = math_char_node;
1272
math_character(p) = mval.character_value;
1273
if ((mval.class_value == var_code) && fam_in_range)
1274
math_fam(p) = cur_fam;
1276
math_fam(p) = mval.family_value;
1282
The |set_math_char| procedure creates a new noad appropriate to a given
1283
math code, and appends it to the current mlist. However, if the math code
1284
is sufficiently large, the |cur_chr| is treated as an active character and
1285
nothing is appended.
1288
void set_math_char(mathcodeval mval)
1290
pointer p; /* the new noad */
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);
1301
q = new_node(math_char_node, 0);
1303
math_character(nucleus(p)) = mval.character_value;
1304
math_fam(nucleus(p)) = mval.family_value;
1305
if (mval.class_value == var_code) {
1307
math_fam(nucleus(p)) = cur_fam;
1308
subtype(p) = ord_noad_type;
1310
switch (mval.class_value) {
1312
case 0: subtype(p) = ord_noad_type; break;
1313
case 1: subtype(p) = op_noad_type_normal; break;
1314
case 2: subtype(p) = bin_noad_type; break;
1315
case 3: subtype(p) = rel_noad_type; break;
1316
case 4: subtype(p) = open_noad_type; break;
1317
case 5: subtype(p) = close_noad_type; break;
1318
case 6: subtype(p) = punct_noad_type; break;
1329
The |math_char_in_text| procedure creates a new node representing a math char
1330
in text code, and appends it to the current list. However, if the math code
1331
is sufficiently large, the |cur_chr| is treated as an active character and
1332
nothing is appended.
1335
void math_char_in_text(mathcodeval mval)
1337
pointer p; /* the new node */
1338
if (mval.class_value == 8) {
1339
/* An active character that is an |outer_call| is allowed here */
1340
cur_cs = active_to_cs(cur_chr, true);
1341
cur_cmd = eq_type(cur_cs);
1342
cur_chr = equiv(cur_cs);
1346
p = new_char(fam_fnt(mval.family_value, text_size),
1347
mval.character_value);
1354
void math_math_comp(void)
1357
tail_append(new_noad());
1358
subtype(tail) = cur_chr;
1359
q = new_node(math_char_node, 0);
1361
if (cur_chr == over_noad_type)
1362
(void) scan_math(nucleus(tail), cramped_style(m_style));
1364
(void) scan_math(nucleus(tail), m_style);
1368
void math_limit_switch(void)
1371
"I'm ignoring this misplaced \\limits or \\nolimits command.",
1375
if (type(tail) == simple_noad) {
1376
subtype(tail) = cur_chr;
1380
tex_error("Limit controls must follow a math operator", hlp);
1384
Delimiter fields of noads are filled in by the |scan_delimiter| routine.
1385
The first parameter of this procedure is the |mem| address where the
1386
delimiter is to be placed; the second tells if this delimiter follows
1387
\.{\\radical} or not.
1391
void scan_delimiter(pointer p, int r)
1394
if (r == tex_mathcode) { /* \radical */
1395
dval = do_scan_extdef_del_code(tex_mathcode, true);
1396
} else if (r == aleph_mathcode) { /* \oradical */
1397
dval = do_scan_extdef_del_code(aleph_mathcode, true);
1398
} else if (r == xetex_mathcode) { /* \Uradical */
1399
dval = do_scan_extdef_del_code(xetex_mathcode, false);
1400
} else if (r == no_mathcode) {
1404
case other_char_cmd:
1405
dval = get_del_code(cur_chr);
1408
if (cur_chr == 0) /* \delimiter */
1409
dval = do_scan_extdef_del_code(tex_mathcode, true);
1410
else if (cur_chr == 1) /* \odelimiter */
1411
dval = do_scan_extdef_del_code(aleph_mathcode, true);
1412
else if (cur_chr == 2) /* \Udelimiter */
1413
dval = do_scan_extdef_del_code(xetex_mathcode, true);
1415
confusion("scan_delimiter1");
1418
dval.small_family_value = -1;
1422
confusion("scan_delimiter2");
1426
if (dval.small_family_value < 0) {
1428
"I was expecting to see something like `(' or `\\{' or",
1429
"`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
1430
"should probably delete the `{' by typing `1' now, so that",
1431
"braces don't get unbalanced. Otherwise just proceed",
1432
"Acceptable delimiters are characters whose \\delcode is",
1433
"nonnegative, or you can use `\\delimiter <delimiter code>'.",
1436
back_error("Missing delimiter (. inserted)", hlp);
1442
small_fam(p) = dval.small_family_value;
1443
small_char(p) = dval.small_character_value;
1444
large_fam(p) = dval.large_family_value;
1445
large_char(p) = dval.large_character_value;
1451
void math_radical(void)
1454
int chr_code = cur_chr;
1455
tail_append(new_node(radical_noad, chr_code));
1456
q = new_node(delim_node, 0);
1457
left_delimiter(tail) = q;
1458
if (chr_code == 0) /* \radical */
1459
scan_delimiter(left_delimiter(tail), tex_mathcode);
1460
else if (chr_code == 1) /* \oradical */
1461
scan_delimiter(left_delimiter(tail), aleph_mathcode);
1462
else if (chr_code == 2) /* \Uradical */
1463
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1464
else if (chr_code == 3) /* \Uroot */
1465
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1466
else if (chr_code == 4) /* \Uunderdelimiter */
1467
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1468
else if (chr_code == 5) /* \Uoverdelimiter */
1469
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1470
else if (chr_code == 6) /* \Udelimiterunder */
1471
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1472
else if (chr_code == 7) /* \Udelimiterover */
1473
scan_delimiter(left_delimiter(tail), xetex_mathcode);
1475
confusion("math_radical");
1476
if (chr_code == 3) {
1477
/* the trick with the |vlink(q)| is used by |scan_math|
1478
to decide whether it needs to go on */
1479
q = new_node(math_char_node, 0);
1482
if (!scan_math(degree(tail), sup_sup_style(m_style))) {
1483
vlink(degree(tail)) = null;
1484
q = new_node(math_char_node, 0);
1486
(void) scan_math(nucleus(tail), cramped_style(m_style));
1489
q = new_node(math_char_node, 0);
1491
(void) scan_math(nucleus(tail), cramped_style(m_style));
1499
t.character_value = 0;
1501
b.character_value = 0;
1503
if (cur_cmd == accent_cmd) {
1505
"I'm changing \\accent to \\mathaccent here; wish me luck.",
1506
"(Accents are not the same in formulas as they are in text.)",
1509
tex_error("Please use \\mathaccent for accents in math mode", hlp);
1511
tail_append(new_node(accent_noad, 0));
1512
if (cur_chr == 0) { /* \mathaccent */
1513
t = scan_mathchar(tex_mathcode);
1514
} else if (cur_chr == 1) { /* \omathaccent */
1515
t = scan_mathchar(aleph_mathcode);
1516
} else if (cur_chr == 2) { /* \Umathaccent */
1517
t = scan_mathchar(xetex_mathcode);
1518
} else if (cur_chr == 3) { /* \Umathbotaccent */
1519
b = scan_mathchar(xetex_mathcode);
1520
} else if (cur_chr == 4) { /* \Umathaccents */
1521
t = scan_mathchar(xetex_mathcode);
1522
b = scan_mathchar(xetex_mathcode);
1524
confusion("math_ac");
1526
if (!(t.character_value == 0 && t.family_value == 0)) {
1527
q = new_node(math_char_node, 0);
1528
accent_chr(tail) = q;
1529
math_character(accent_chr(tail)) = t.character_value;
1530
if ((t.class_value == var_code) && fam_in_range)
1531
math_fam(accent_chr(tail)) = cur_fam;
1533
math_fam(accent_chr(tail)) = t.family_value;
1535
if (!(b.character_value == 0 && b.family_value == 0)) {
1536
q = new_node(math_char_node, 0);
1537
bot_accent_chr(tail) = q;
1538
math_character(bot_accent_chr(tail)) = b.character_value;
1539
if ((b.class_value == var_code) && fam_in_range)
1540
math_fam(bot_accent_chr(tail)) = cur_fam;
1542
math_fam(bot_accent_chr(tail)) = b.family_value;
1544
q = new_node(math_char_node, 0);
1546
(void) scan_math(nucleus(tail), cramped_style(m_style));
1549
pointer math_vcenter_group(pointer p)
1553
subtype(q) = vcenter_noad_type;
1554
r = new_node(sub_box_node, 0);
1556
math_list(nucleus(q)) = p;
1561
The routine that scans the four mlists of a \.{\\mathchoice} is very
1562
much like the routine that builds discretionary nodes.
1565
void append_choices(void)
1567
tail_append(new_choice());
1569
set_saved_record(-1, saved_choices, 0, 0);
1570
push_math(math_choice_group, display_style);
1574
void build_choices(void)
1576
pointer p; /* the current mlist */
1578
prev_style = m_style;
1580
p = fin_mlist(null);
1581
assert(saved_type(-1) == saved_choices);
1582
switch (saved_value(-1)) {
1584
display_mlist(tail) = p;
1587
text_mlist(tail) = p;
1590
script_mlist(tail) = p;
1593
script_script_mlist(tail) = p;
1597
} /* there are no other cases */
1598
set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
1599
push_math(math_choice_group, (prev_style + 2));
1604
Subscripts and superscripts are attached to the previous nucleus by the
1605
action procedure called |sub_sup|.
1611
if (tail == head || (!scripts_allowed(tail))) {
1612
tail_append(new_noad());
1613
q = new_node(sub_mlist_node, 0);
1616
if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* super_sub_script */
1617
if (supscr(tail) != null) {
1619
"I treat `x^1^2' essentially like `x^1{}^2'.", NULL
1621
tail_append(new_noad());
1622
q = new_node(sub_mlist_node, 0);
1624
tex_error("Double superscript", hlp);
1626
q = new_node(math_char_node, 0);
1628
(void) scan_math(supscr(tail), sup_style(m_style));
1629
} else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
1630
if (subscr(tail) != null) {
1632
"I treat `x_1_2' essentially like `x_1{}_2'.", NULL
1634
tail_append(new_noad());
1635
q = new_node(sub_mlist_node, 0);
1637
tex_error("Double subscript", hlp);
1639
q = new_node(math_char_node, 0);
1641
(void) scan_math(subscr(tail), sub_style(m_style));
1646
An operation like `\.{\\over}' causes the current mlist to go into a
1647
state of suspended animation: |incompleat_noad| points to a |fraction_noad|
1648
that contains the mlist-so-far as its numerator, while the denominator
1649
is yet to come. Finally when the mlist is finished, the denominator will
1650
go into the incompleat fraction noad, and that noad will become the
1651
whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
1655
void math_fraction(void)
1657
small_number c; /* the type of generalized fraction we are scanning */
1660
if (incompleat_noad != null) {
1662
"I'm ignoring this fraction specification, since I don't",
1663
"know whether a construction like `x \\over y \\over z'",
1664
"means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
1667
if (c >= delimited_code) {
1668
scan_delimiter(null, no_mathcode);
1669
scan_delimiter(null, no_mathcode);
1671
if ((c % delimited_code) == above_code)
1672
scan_normal_dimen();
1673
tex_error("Ambiguous; you need another { and }", hlp);
1675
incompleat_noad = new_node(fraction_noad, 0);
1676
numerator(incompleat_noad) = new_node(sub_mlist_node, 0);
1677
math_list(numerator(incompleat_noad)) = vlink(head);
1680
m_style = cramped_style(m_style);
1682
if (c >= delimited_code) {
1683
q = new_node(delim_node, 0);
1684
left_delimiter(incompleat_noad) = q;
1685
q = new_node(delim_node, 0);
1686
right_delimiter(incompleat_noad) = q;
1687
scan_delimiter(left_delimiter(incompleat_noad), no_mathcode);
1688
scan_delimiter(right_delimiter(incompleat_noad), no_mathcode);
1690
switch (c % delimited_code) {
1692
scan_normal_dimen();
1693
thickness(incompleat_noad) = cur_val;
1696
thickness(incompleat_noad) = default_code;
1699
thickness(incompleat_noad) = 0;
1701
} /* there are no other cases */
1707
/* At the end of a math formula or subformula, the |fin_mlist| routine is
1708
called upon to return a pointer to the newly completed mlist, and to
1709
pop the nest back to the enclosing semantic level. The parameter to
1710
|fin_mlist|, if not null, points to a |fence_noad| that ends the
1711
current mlist; this |fence_noad| has not yet been appended.
1715
pointer fin_mlist(pointer p)
1717
pointer q; /* the mlist to return */
1718
if (incompleat_noad != null) {
1719
if (denominator(incompleat_noad) != null) {
1720
type(denominator(incompleat_noad)) = sub_mlist_node;
1722
q = new_node(sub_mlist_node, 0);
1723
denominator(incompleat_noad) = q;
1725
math_list(denominator(incompleat_noad)) = vlink(head);
1727
q = incompleat_noad;
1729
q = math_list(numerator(incompleat_noad));
1730
if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
1731
|| (delim_ptr == null))
1732
confusion("right"); /* this can't happen */
1733
math_list(numerator(incompleat_noad)) = vlink(delim_ptr);
1734
vlink(delim_ptr) = incompleat_noad;
1735
vlink(incompleat_noad) = p;
1746
/* Now at last we're ready to see what happens when a right brace occurs
1747
in a math formula. Two special cases are simplified here: Braces are effectively
1748
removed when they surround a single Ord without sub/superscripts, or when they
1749
surround an accent that is the nucleus of an Ord atom.
1752
void close_math_group(pointer p)
1755
int old_style = m_style;
1759
assert(saved_type(0) == saved_math);
1760
type(saved_value(0)) = sub_mlist_node;
1761
p = fin_mlist(null);
1762
math_list(saved_value(0)) = p;
1764
if (vlink(p) == null) {
1765
if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
1766
if (subscr(p) == null && supscr(p) == null) {
1767
type(saved_value(0)) = type(nucleus(p));
1768
if (type(nucleus(p)) == math_char_node) {
1769
math_fam(saved_value(0)) = math_fam(nucleus(p));
1770
math_character(saved_value(0)) =
1771
math_character(nucleus(p));
1773
math_list(saved_value(0)) = math_list(nucleus(p));
1774
math_list(nucleus(p)) = null;
1776
delete_attribute_ref(node_attr(saved_value(0)));
1777
node_attr(saved_value(0)) = node_attr(nucleus(p));
1778
node_attr(nucleus(p)) = null;
1781
} else if (type(p) == accent_noad) {
1782
if (saved_value(0) == nucleus(tail)) {
1783
if (type(tail) == simple_noad
1784
&& subtype(tail) == ord_noad_type) {
1786
while (vlink(q) != tail)
1789
nucleus(tail) = null;
1790
subscr(tail) = null;
1791
supscr(tail) = null;
1792
delete_attribute_ref(node_attr(p));
1793
node_attr(p) = node_attr(tail);
1794
node_attr(tail) = null;
1802
if (vlink(saved_value(0)) > 0) {
1804
q = new_node(math_char_node, 0);
1805
nucleus(vlink(saved_value(0))) = q;
1806
vlink(saved_value(0)) = null;
1808
(void) scan_math(saved_value(0), old_style);
1814
We have dealt with all constructions of math mode except `\.{\\left}' and
1815
`\.{\\right}', so the picture is completed by the following sections of
1816
the program. The |middle| feature of \eTeX\ allows one ore several \.{\\middle}
1817
delimiters to appear between \.{\\left} and \.{\\right}.
1820
void math_left_right(void)
1822
small_number t; /* |left_noad_side| .. |right_noad_side| */
1823
pointer p; /* new noad */
1824
pointer q; /* resulting mlist */
1825
pointer r; /* temporary */
1827
if ((t != left_noad_side) && (cur_group != math_left_group)) {
1828
if (cur_group == math_shift_group) {
1829
scan_delimiter(null, no_mathcode);
1830
if (t == middle_noad_side) {
1832
"I'm ignoring a \\middle that had no matching \\left.",
1835
tex_error("Extra \\middle", hlp);
1838
"I'm ignoring a \\right that had no matching \\left.",
1841
tex_error("Extra \\right", hlp);
1848
type(p) = fence_noad;
1850
r = new_node(delim_node, 0);
1852
scan_delimiter(delimiter(p), no_mathcode);
1853
if (t == left_noad_side) {
1859
if (t != right_noad_side) {
1860
push_math(math_left_group, m_style);
1865
tail_append(new_noad());
1866
subtype(tail) = inner_noad_type;
1867
r = new_node(sub_mlist_node, 0);
1869
math_list(nucleus(tail)) = q;
1875
/* \TeX\ gets to the following part of the program when
1876
the first `\.\$' ending a display has been scanned.
1879
static void check_second_math_shift(void)
1882
if (cur_cmd != math_shift_cmd) {
1884
"The `$' that I just saw supposedly matches a previous `$$'.",
1885
"So I shall assume that you typed `$$' both times.",
1888
back_error("Display math should end with $$", hlp);
1892
static void check_display_math_end(void)
1894
if (cur_chr != cramped_display_style) {
1896
"I shall assume that you typed that.",
1899
tex_error("Display math should end with \\Ustopdisplaymath", hlp);
1903
static void check_inline_math_end(void)
1905
if (cur_chr != cramped_text_style) {
1907
"I shall assume that you typed that.",
1910
tex_error("Inline math should end with \\Ustopmath", hlp);
1914
void resume_after_display(void)
1916
if (cur_group != math_shift_group)
1917
confusion("display");
1919
prev_graf = prev_graf + 3;
1922
space_factor = 1000;
1923
tail_append(make_local_par_node()); /* this needs to be intercepted in
1924
the display math start ! */
1926
if (cur_cmd != spacer_cmd)
1928
if (nest_ptr == 1) {
1929
lua_node_filter_s(buildpage_filter_callback, "after_display");
1937
If the inline directions of \.{\\pardir} and \.{\\mathdir} are
1938
opposite, then this function will return true. Discovering that fact
1939
is somewhat odd because it needs traversal of the |save_stack|.
1940
The occurance of displayed equations is weird enough that this is
1941
probably still better than having yet another field in the |input_stack|
1944
None of this makes much sense if the inline direction of either one of
1945
\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current
1946
math machinery is ill suited anyway so I do not bother to test that.
1950
static boolean math_and_text_reversed_p(void)
1952
int i = save_ptr - 1;
1953
while (save_type(i) != level_boundary)
1955
while (i < save_ptr) {
1956
if (save_type(i) == restore_old_value &&
1957
save_value(i) == int_base + par_direction_code) {
1958
if (textdir_opposite(math_direction, save_value(i - 1)))
1968
The fussiest part of math mode processing occurs when a displayed formula is
1969
being centered and placed with an optional equation number.
1972
/* At this time we are in vertical mode (or internal vertical mode).
1974
|p| points to the mlist for the formula.
1975
|a| is either |null| or it points to a box containing the equation number.
1976
|l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
1980
static void finish_displayed_math(boolean l, pointer a, pointer p)
1982
pointer eq_box; /* box containing the equation */
1983
scaled eq_w; /* width of the equation */
1984
scaled line_w; /* width of the line */
1985
scaled eqno_w; /* width of equation number */
1986
scaled eqno_w2; /* width of equation number plus space to separate from equation */
1987
scaled line_s; /* move the line right this much */
1988
scaled d; /* displacement of equation in the line */
1989
small_number g1, g2; /* glue parameter codes for before and after */
1990
pointer r; /* kern node used to position the display */
1991
pointer t; /* tail of adjustment list */
1992
pointer pre_t; /* tail of pre-adjustment list */
1993
boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
1994
swap_dir = math_and_text_reversed_p();
1996
adjust_tail = adjust_head;
1997
pre_adjust_tail = pre_adjust_head;
1998
eq_box = hpack(p, 0, additional, -1);
1999
p = list_ptr(eq_box);
2002
pre_t = pre_adjust_tail;
2003
pre_adjust_tail = null;
2004
eq_w = width(eq_box);
2005
line_w = display_width;
2006
line_s = display_indent;
2012
eqno_w2 = eqno_w + get_math_quad(text_size);
2014
if (eq_w + eqno_w2 > line_w) {
2015
/* The user can force the equation number to go on a separate line
2016
by causing its width to be zero. */
2018
&& ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
2019
|| (total_shrink[sfi] != 0) || (total_shrink[fil] != 0)
2020
|| (total_shrink[fill] != 0)
2021
|| (total_shrink[filll] != 0))) {
2022
list_ptr(eq_box) = null;
2024
eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
2027
if (eq_w > line_w) {
2028
list_ptr(eq_box) = null;
2030
eq_box = hpack(p, line_w, exactly, -1);
2033
eq_w = width(eq_box);
2035
/* We try first to center the display without regard to the existence of
2036
the equation number. If that would make it too close (where ``too close''
2037
means that the space between display and equation number is less than the
2038
width of the equation number), we either center it in the remaining space
2039
or move it as far from the equation number as possible. The latter alternative
2040
is taken only if the display begins with glue, since we assume that the
2041
user put glue there to control the spacing precisely.
2043
d = half(line_w - eq_w);
2044
if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */
2045
d = half(line_w - eq_w - eqno_w);
2047
if (!is_char_node(p))
2048
if (type(p) == glue_node)
2052
/* If the equation number is set on a line by itself, either before or
2053
after the formula, we append an infinite penalty so that no page break will
2054
separate the display from its number; and we use the same size and
2055
displacement for all three potential lines of the display, even though
2056
`\.{\\parshape}' may specify them differently.
2058
tail_append(new_penalty(int_par(pre_display_penalty_code)));
2059
if ((d + line_s <= pre_display_size) || l) { /* not enough clearance */
2060
g1 = above_display_skip_code;
2061
g2 = below_display_skip_code;
2063
g1 = above_display_short_skip_code;
2064
g2 = below_display_short_skip_code;
2067
if (l && (eqno_w == 0)) { /* \leqno on a forced single line due to |width=0| */
2068
/* it follows that |type(a)=hlist_node| */
2070
shift_amount(a) = line_w + line_s;
2072
shift_amount(a) = line_s;
2075
tail_append(new_penalty(inf_penalty));
2077
tail_append(new_param_glue(g1));
2081
r = new_kern(line_w - eq_w - eqno_w - d);
2091
eq_box = hpack(eq_box, 0, additional, -1);
2094
/* d = line_w - d; */
2097
d = line_w - width(eq_box);
2101
d = line_w - eq_w - eqno_w - d;
2104
shift_amount(eq_box) = line_s + d;
2105
append_to_vlist(eq_box);
2107
if ((a != null) && (eqno_w == 0) && !l) {
2108
tail_append(new_penalty(inf_penalty));
2110
shift_amount(a) = line_s + line_w - width(a);
2112
shift_amount(a) = line_s;
2117
if (t != adjust_head) { /* migrating material comes after equation number */
2118
vlink(tail) = vlink(adjust_head);
2121
if (pre_t != pre_adjust_head) {
2122
vlink(tail) = vlink(pre_adjust_head);
2125
tail_append(new_penalty(int_par(post_display_penalty_code)));
2127
tail_append(new_param_glue(g2));
2129
resume_after_display();
2134
void after_math(void)
2136
int m; /* |mmode| or |-mmode| */
2137
pointer p; /* the formula */
2138
pointer a = null; /* box containing equation number */
2139
boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
2141
p = fin_mlist(null); /* this pops the nest */
2142
if (cur_cmd == math_shift_cs_cmd &&
2143
(cur_chr == text_style || cur_chr == display_style)) {
2146
if (mode == -m) { /* end of equation number */
2147
if (cur_cmd == math_shift_cmd) {
2148
check_second_math_shift();
2150
check_display_math_end();
2152
run_mlist_to_hlist(p, text_style, false);
2153
a = hpack(vlink(temp_head), 0, additional, -1);
2155
decr(save_ptr); /* now |cur_group=math_shift_group| */
2156
assert(saved_type(0) == saved_eqno);
2157
if (saved_value(0) == 1)
2160
p = fin_mlist(null);
2163
/* The |unsave| is done after everything else here; hence an appearance of
2164
`\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
2165
particular \.\$'s. This is consistent with the conventions of
2166
`\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
2167
space above that display.
2169
if (cur_cmd == math_shift_cs_cmd) {
2170
check_inline_math_end();
2172
tail_append(new_math(math_surround, before));
2173
if (dir_math_save) {
2174
tail_append(new_dir(math_direction));
2176
run_mlist_to_hlist(p, text_style, (mode > 0));
2177
vlink(tail) = vlink(temp_head);
2178
while (vlink(tail) != null)
2180
if (dir_math_save) {
2181
tail_append(new_dir(math_direction - 64));
2183
dir_math_save = false;
2184
tail_append(new_math(math_surround, after));
2185
space_factor = 1000;
2189
if (cur_cmd == math_shift_cmd) {
2190
check_second_math_shift();
2192
check_display_math_end();
2195
run_mlist_to_hlist(p, display_style, false);
2196
finish_displayed_math(l, a, vlink(temp_head));
2201
When \.{\\halign} appears in a display, the alignment routines operate
2202
essentially as they do in vertical mode. Then the following program is
2203
activated, with |p| and |q| pointing to the beginning and end of the
2204
resulting list, and with |aux_save| holding the |prev_depth| value.
2208
void finish_display_alignment(pointer p, pointer q, memory_word aux_save)
2211
if (cur_cmd != math_shift_cmd) {
2213
"Displays can use special alignments (like \\eqalignno)",
2214
"only if nothing but the alignment itself is between $$'s.",
2217
back_error("Missing $$ inserted", hlp);
2219
check_second_math_shift();
2222
tail_append(new_penalty(int_par(pre_display_penalty_code)));
2223
tail_append(new_param_glue(above_display_skip_code));
2227
tail_append(new_penalty(int_par(post_display_penalty_code)));
2228
tail_append(new_param_glue(below_display_skip_code));
2229
prev_depth = aux_save.cint;
2230
resume_after_display();
2233
/* Interface to \.{\\Umath} and \.{\\mathstyle} */
2235
void setup_math_style(void)
2238
tail_append(new_noad());
2239
q = new_node(math_char_node, 0);
2241
(void) scan_math(nucleus(tail), num_style(m_style));
2245
void print_math_style(void)
2247
if (abs(mode) == mmode)