~ubuntu-branches/ubuntu/saucy/luatex/saucy

« back to all changes in this revision

Viewing changes to source/texk/web2c/luatexdir/tex/texmath.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Preining
  • Date: 2009-12-25 09:47:05 UTC
  • mfrom: (1.1.9 upstream) (4.2.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091225094705-y33rpflo8t4u9nag
Tags: 0.50.0-1
* new upstream release
* disable fix-hurd-ftbfs patch, included upstream
* disable upstram-fixes, included upstream
* disable ubuntu_libpoppler-0.11, not needed anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* math.c
 
2
 
 
3
   Copyright 2008-2009 Taco Hoekwater <taco@luatex.org>
 
4
 
 
5
   This file is part of LuaTeX.
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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/>. */
 
19
 
 
20
 
 
21
#include <ptexlib.h>
 
22
 
 
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 $";
 
26
 
 
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
 
37
 
 
38
#define cur_fam int_par(cur_fam_code)
 
39
#define text_direction int_par(text_direction_code)
 
40
 
 
41
#define var_code 7
 
42
 
 
43
extern void rawset_sa_item(sa_tree hed, int n, int v);
 
44
 
 
45
/* TODO: not sure if this is the right order */
 
46
#define back_error(A,B) do {                    \
 
47
    OK_to_interrupt=false;                      \
 
48
    back_input();                               \
 
49
    OK_to_interrupt=true;                       \
 
50
    tex_error(A,B);                             \
 
51
  } while (0)
 
52
 
 
53
int scan_math(pointer, int);
 
54
pointer fin_mlist(pointer);
 
55
 
 
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)
 
66
 
 
67
/*
 
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.
 
74
 
 
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.
 
80
 
 
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.
 
85
 
 
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.
 
93
 
 
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.
 
102
 
 
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)
 
109
 
 
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'.
 
114
 
 
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|.
 
120
 
 
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.
 
124
 
 
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
 
129
corrections.)
 
130
 
 
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.
 
135
 
 
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.
 
139
 
 
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''
 
144
will be added).
 
145
 
 
146
*/
 
147
 
 
148
 
 
149
void unsave_math(void)
 
150
{
 
151
    unsave();
 
152
    decr(save_ptr);
 
153
    flush_node_list(text_dir_ptr);
 
154
    assert(saved_type(0) == saved_textdir);
 
155
    text_dir_ptr = saved_value(0);
 
156
}
 
157
 
 
158
 
 
159
/*
 
160
Sometimes it is necessary to destroy an mlist. The following
 
161
subroutine empties the current list, assuming that |abs(mode)=mmode|.
 
162
*/
 
163
 
 
164
void flush_math(void)
 
165
{
 
166
    flush_node_list(vlink(head));
 
167
    flush_node_list(incompleat_noad);
 
168
    vlink(head) = null;
 
169
    tail = head;
 
170
    incompleat_noad = null;
 
171
}
 
172
 
 
173
/* Before we can do anything in math mode, we need fonts. */
 
174
 
 
175
#define MATHFONTSTACK  8
 
176
#define MATHFONTDEFAULT 0       /* == nullfont */
 
177
 
 
178
static sa_tree math_fam_head = NULL;
 
179
 
 
180
int fam_fnt(int fam_id, int size_id)
 
181
{
 
182
    int n = fam_id + (256 * size_id);
 
183
    return (int) get_sa_item(math_fam_head, n);
 
184
}
 
185
 
 
186
void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
 
187
{
 
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) {
 
192
        begin_diagnostic();
 
193
        tprint("{assigning");
 
194
        print_char(' ');
 
195
        print_cmd_chr(def_family_cmd, size_id);
 
196
        print_int(fam_id);
 
197
        print_char('=');
 
198
        print_font_identifier(fam_fnt(fam_id, size_id));
 
199
        print_char('}');
 
200
        end_diagnostic(false);
 
201
    }
 
202
}
 
203
 
 
204
void unsave_math_fam_data(int gl)
 
205
{
 
206
    sa_stack_item st;
 
207
    if (math_fam_head->stack == NULL)
 
208
        return;
 
209
    while (math_fam_head->stack_ptr > 0 &&
 
210
           abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
 
211
           >= (int) gl) {
 
212
        st = math_fam_head->stack[math_fam_head->stack_ptr];
 
213
        if (st.level > 0) {
 
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;
 
219
                begin_diagnostic();
 
220
                tprint("{restoring");
 
221
                print_char(' ');
 
222
                print_cmd_chr(def_family_cmd, size_id);
 
223
                print_int(fam_id);
 
224
                print_char('=');
 
225
                print_font_identifier(fam_fnt(fam_id, size_id));
 
226
                print_char('}');
 
227
                end_diagnostic(false);
 
228
            }
 
229
        }
 
230
        (math_fam_head->stack_ptr)--;
 
231
    }
 
232
}
 
233
 
 
234
 
 
235
 
 
236
/* and parameters */
 
237
 
 
238
#define MATHPARAMSTACK  8
 
239
#define MATHPARAMDEFAULT undefined_math_parameter
 
240
 
 
241
static sa_tree math_param_head = NULL;
 
242
 
 
243
void def_math_param(int param_id, int style_id, scaled value, int lvl)
 
244
{
 
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) {
 
248
        begin_diagnostic();
 
249
        tprint("{assigning");
 
250
        print_char(' ');
 
251
        print_cmd_chr(set_math_param_cmd, param_id);
 
252
        print_cmd_chr(math_style_cmd, style_id);
 
253
        print_char('=');
 
254
        print_int(value);
 
255
        print_char('}');
 
256
        end_diagnostic(false);
 
257
    }
 
258
}
 
259
 
 
260
scaled get_math_param(int param_id, int style_id)
 
261
{
 
262
    int n = param_id + (256 * style_id);
 
263
    return (scaled) get_sa_item(math_param_head, n);
 
264
}
 
265
 
 
266
 
 
267
void unsave_math_param_data(int gl)
 
268
{
 
269
    sa_stack_item st;
 
270
    if (math_param_head->stack == NULL)
 
271
        return;
 
272
    while (math_param_head->stack_ptr > 0 &&
 
273
           abs(math_param_head->stack[math_param_head->stack_ptr].level)
 
274
           >= (int) gl) {
 
275
        st = math_param_head->stack[math_param_head->stack_ptr];
 
276
        if (st.level > 0) {
 
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;
 
282
                begin_diagnostic();
 
283
                tprint("{restoring");
 
284
                print_char(' ');
 
285
                print_cmd_chr(set_math_param_cmd, param_id);
 
286
                print_cmd_chr(math_style_cmd, style_id);
 
287
                print_char('=');
 
288
                print_int(get_math_param(param_id, style_id));
 
289
                print_char('}');
 
290
                end_diagnostic(false);
 
291
            }
 
292
        }
 
293
        (math_param_head->stack_ptr)--;
 
294
    }
 
295
}
 
296
 
 
297
 
 
298
/* saving and unsaving of both */
 
299
 
 
300
void unsave_math_data(int gl)
 
301
{
 
302
    unsave_math_fam_data(gl);
 
303
    unsave_math_param_data(gl);
 
304
}
 
305
 
 
306
void dump_math_data(void)
 
307
{
 
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);
 
314
}
 
315
 
 
316
void undump_math_data(void)
 
317
{
 
318
    math_fam_head = undump_sa_tree();
 
319
    math_param_head = undump_sa_tree();
 
320
}
 
321
 
 
322
/*  */
 
323
 
 
324
void initialize_math(void)
 
325
{
 
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();
 
331
    }
 
332
    return;
 
333
}
 
334
 
 
335
 
 
336
/*
 
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|.
 
350
 
 
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.
 
355
 
 
356
A |radical_noad| also has a |left_delimiter| field, which usually
 
357
represents a square root sign.
 
358
 
 
359
A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
 
360
 
 
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@>
 
366
 
 
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}.
 
380
*/
 
381
 
 
382
 
 
383
/* The |new_noad| function creates an |ord_noad| that is completely null */
 
384
 
 
385
pointer new_noad(void)
 
386
{
 
387
    pointer p;
 
388
    p = new_node(simple_noad, ord_noad_type);
 
389
    /* all noad fields are zero after this */
 
390
    return p;
 
391
}
 
392
 
 
393
pointer new_sub_box(pointer cur_box)
 
394
{
 
395
    pointer p, q;
 
396
    p = new_noad();
 
397
    q = new_node(sub_box_node, 0);
 
398
    nucleus(p) = q;
 
399
    math_list(nucleus(p)) = cur_box;
 
400
    return p;
 
401
}
 
402
 
 
403
/*
 
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|.
 
410
 
 
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.
 
421
*/
 
422
 
 
423
/*
 
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|.
 
431
 
 
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@>
 
438
*/
 
439
 
 
440
const char *math_style_names[] = {
 
441
    "display", "crampeddisplay",
 
442
    "text", "crampedtext",
 
443
    "script", "crampedscript",
 
444
    "scriptscript", "crampedscriptscript",
 
445
    NULL
 
446
};
 
447
 
 
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",
 
475
    "openinnerspacing",
 
476
    "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
 
477
    "closeopenspacing", "closeclosespacing", "closepunctspacing",
 
478
    "closeinnerspacing",
 
479
    "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
 
480
    "punctopenspacing", "punctclosespacing", "punctpunctspacing",
 
481
    "punctinnerspacing",
 
482
    "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
 
483
    "inneropenspacing", "innerclosespacing", "innerpunctspacing",
 
484
    "innerinnerspacing",
 
485
    NULL
 
486
};
 
487
 
 
488
pointer new_style(small_number s)
 
489
{                               /* create a style node */
 
490
    m_style = s;
 
491
    return new_node(style_node, s);
 
492
}
 
493
 
 
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.
 
497
*/
 
498
 
 
499
 
 
500
pointer new_choice(void)
 
501
{                               /* create a choice node */
 
502
    return new_node(choice_node, 0);    /* the |subtype| is not used */
 
503
}
 
504
 
 
505
/*
 
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.
 
509
 
 
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\}\}\}\}\$}'.
 
520
*/
 
521
 
 
522
 
 
523
void display_normal_noad(pointer p);    /* forward */
 
524
void display_fence_noad(pointer p);     /* forward */
 
525
void display_fraction_noad(pointer p);  /* forward */
 
526
 
 
527
void show_math_node(pointer p)
 
528
{
 
529
    switch (type(p)) {
 
530
    case style_node:
 
531
        print_cmd_chr(math_style_cmd, subtype(p));
 
532
        break;
 
533
    case choice_node:
 
534
        tprint_esc("mathchoice");
 
535
        append_char('D');
 
536
        show_node_list(display_mlist(p));
 
537
        flush_char();
 
538
        append_char('T');
 
539
        show_node_list(text_mlist(p));
 
540
        flush_char();
 
541
        append_char('S');
 
542
        show_node_list(script_mlist(p));
 
543
        flush_char();
 
544
        append_char('s');
 
545
        show_node_list(script_script_mlist(p));
 
546
        flush_char();
 
547
        break;
 
548
    case simple_noad:
 
549
    case radical_noad:
 
550
    case accent_noad:
 
551
        display_normal_noad(p);
 
552
        break;
 
553
    case fence_noad:
 
554
        display_fence_noad(p);
 
555
        break;
 
556
    case fraction_noad:
 
557
        display_fraction_noad(p);
 
558
        break;
 
559
    default:
 
560
        tprint("Unknown node type!");
 
561
        break;
 
562
    }
 
563
}
 
564
 
 
565
 
 
566
/* Here are some simple routines used in the display of noads. */
 
567
 
 
568
void print_fam_and_char(pointer p)
 
569
{                               /* prints family and character */
 
570
    tprint_esc("fam");
 
571
    print_int(math_fam(p));
 
572
    print_char(' ');
 
573
    print(math_character(p));
 
574
}
 
575
 
 
576
void print_delimiter(pointer p)
 
577
{
 
578
    int a;
 
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);
 
586
        print_hex(a);
 
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));
 
592
    } else {
 
593
        /* assume this is omega-style */
 
594
        a = small_fam(p) * 65536 + small_char(p);
 
595
        print_hex(a);
 
596
        a = large_fam(p) * 65536 + large_char(p);
 
597
        print_hex(a);
 
598
    }
 
599
}
 
600
 
 
601
/*
 
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).
 
607
@^recursion@>
 
608
*/
 
609
 
 
610
 
 
611
void print_subsidiary_data(pointer p, ASCII_code c)
 
612
{                               /* display a noad field */
 
613
    if ((int) cur_length >= depth_threshold) {
 
614
        if (p != null)
 
615
            tprint(" []");
 
616
    } else {
 
617
        append_char(c);         /* include |c| in the recursion history */
 
618
        if (p != null) {
 
619
            switch (type(p)) {
 
620
            case math_char_node:
 
621
                print_ln();
 
622
                print_current_string();
 
623
                print_fam_and_char(p);
 
624
                break;
 
625
            case sub_box_node:
 
626
                show_node_list(math_list(p));
 
627
                break;
 
628
            case sub_mlist_node:
 
629
                if (math_list(p) == null) {
 
630
                    print_ln();
 
631
                    print_current_string();
 
632
                    tprint("{}");
 
633
                } else {
 
634
                    show_node_list(math_list(p));
 
635
                }
 
636
                break;
 
637
            }
 
638
        }
 
639
        flush_char();           /* remove |c| from the recursion history */
 
640
    }
 
641
}
 
642
 
 
643
void display_normal_noad(pointer p)
 
644
{
 
645
    switch (type(p)) {
 
646
    case simple_noad:
 
647
        switch (subtype(p)) {
 
648
        case ord_noad_type:
 
649
            tprint_esc("mathord");
 
650
            break;
 
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");
 
659
            break;
 
660
        case bin_noad_type:
 
661
            tprint_esc("mathbin");
 
662
            break;
 
663
        case rel_noad_type:
 
664
            tprint_esc("mathrel");
 
665
            break;
 
666
        case open_noad_type:
 
667
            tprint_esc("mathopen");
 
668
            break;
 
669
        case close_noad_type:
 
670
            tprint_esc("mathclose");
 
671
            break;
 
672
        case punct_noad_type:
 
673
            tprint_esc("mathpunct");
 
674
            break;
 
675
        case inner_noad_type:
 
676
            tprint_esc("mathinner");
 
677
            break;
 
678
        case over_noad_type:
 
679
            tprint_esc("overline");
 
680
            break;
 
681
        case under_noad_type:
 
682
            tprint_esc("underline");
 
683
            break;
 
684
        case vcenter_noad_type:
 
685
            tprint_esc("vcenter");
 
686
            break;
 
687
        default:
 
688
            tprint("<unknown noad type!>");
 
689
            break;
 
690
        }
 
691
        break;
 
692
    case radical_noad:
 
693
        if (subtype(p) == 7)
 
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)
 
702
            tprint_esc("Uroot");
 
703
        else
 
704
            tprint_esc("radical");
 
705
        print_delimiter(left_delimiter(p));
 
706
        if (degree(p) != null) {
 
707
            print_subsidiary_data(degree(p), '/');
 
708
        }
 
709
        break;
 
710
    case accent_noad:
 
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));
 
716
            } else {
 
717
                tprint_esc("accent");
 
718
                print_fam_and_char(accent_chr(p));
 
719
            }
 
720
        } else {
 
721
            tprint_esc("Umathbotaccent");
 
722
            print_fam_and_char(bot_accent_chr(p));
 
723
        }
 
724
        break;
 
725
    }
 
726
    print_subsidiary_data(nucleus(p), '.');
 
727
    print_subsidiary_data(supscr(p), '^');
 
728
    print_subsidiary_data(subscr(p), '_');
 
729
}
 
730
 
 
731
void display_fence_noad(pointer p)
 
732
{
 
733
    if (subtype(p) == right_noad_side)
 
734
        tprint_esc("right");
 
735
    else if (subtype(p) == left_noad_side)
 
736
        tprint_esc("left");
 
737
    else
 
738
        tprint_esc("middle");
 
739
    print_delimiter(delimiter(p));
 
740
}
 
741
 
 
742
void display_fraction_noad(pointer p)
 
743
{
 
744
    tprint_esc("fraction, thickness ");
 
745
    if (thickness(p) == default_code)
 
746
        tprint("= default");
 
747
    else
 
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));
 
756
    }
 
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));
 
764
    }
 
765
    print_subsidiary_data(numerator(p), '\\');
 
766
    print_subsidiary_data(denominator(p), '/');
 
767
}
 
768
 
 
769
/*
 
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.
 
775
 
 
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|.
 
778
*/
 
779
 
 
780
void new_save_level_math(group_code c)
 
781
{
 
782
    set_saved_record(0, saved_textdir, 0, text_dir_ptr);
 
783
    text_dir_ptr = new_dir(math_direction);
 
784
    incr(save_ptr);
 
785
    new_save_level(c);
 
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);
 
789
}
 
790
 
 
791
void push_math(group_code c, int mstyle)
 
792
{
 
793
    if (math_direction != text_direction)
 
794
        dir_math_save = true;
 
795
    push_nest();
 
796
    mode = -mmode;
 
797
    incompleat_noad = null;
 
798
    m_style = mstyle;
 
799
    new_save_level_math(c);
 
800
}
 
801
 
 
802
void enter_ordinary_math(void)
 
803
{
 
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);
 
808
}
 
809
 
 
810
void enter_display_math(void);
 
811
 
 
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
 
815
called for.
 
816
*/
 
817
 
 
818
void init_math(void)
 
819
{
 
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();
 
824
        } else {
 
825
            back_input();
 
826
            enter_ordinary_math();
 
827
        }
 
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();
 
832
    } else {
 
833
        you_cant();
 
834
    }
 
835
}
 
836
 
 
837
/*
 
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.
 
841
*/
 
842
 
 
843
 
 
844
/*
 
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
 
847
this condition.
 
848
*/
 
849
 
 
850
void start_eq_no(void)
 
851
{
 
852
    set_saved_record(0, saved_eqno, 0, cur_chr);
 
853
    incr(save_ptr);
 
854
    enter_ordinary_math();
 
855
}
 
856
 
 
857
/*
 
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.
 
868
 
 
869
Here is the code that handles case (1); the other cases are not quite as
 
870
trivial, so we shall consider them later.
 
871
*/
 
872
 
 
873
void math_left_brace(void)
 
874
{
 
875
    pointer q;
 
876
    tail_append(new_noad());
 
877
    q = new_node(math_char_node, 0);
 
878
    nucleus(tail) = q;
 
879
    back_input();
 
880
    (void) scan_math(nucleus(tail), m_style);
 
881
}
 
882
 
 
883
/*
 
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|.
 
888
*/
 
889
 
 
890
 
 
891
void enter_display_math(void)
 
892
{
 
893
    scaled w;                   /* new or partial |pre_display_size| */
 
894
    scaled l;                   /* new |display_width| */
 
895
    scaled s;                   /* new |display_indent| */
 
896
    pointer p;
 
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
 
905
               of that node */
 
906
            flush_node(tail);
 
907
        }
 
908
        pop_nest();
 
909
        w = -max_dimen;
 
910
    } else {
 
911
        line_break(true, math_shift_group);
 
912
        w = actual_box_width(just_box, (2 * quad(get_cur_font())));
 
913
    }
 
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);
 
922
            if (hang_indent > 0)
 
923
                s = hang_indent;
 
924
            else
 
925
                s = 0;
 
926
        } else {
 
927
            l = hsize;
 
928
            s = 0;
 
929
        }
 
930
    } else {
 
931
        n = vinfo(par_shape_ptr + 1);
 
932
        if (prev_graf + 2 >= n)
 
933
            p = par_shape_ptr + 2 * n + 1;
 
934
        else
 
935
            p = par_shape_ptr + 2 * (prev_graf + 2) + 1;
 
936
        s = varmem[(p - 1)].cint;
 
937
        l = varmem[p].cint;
 
938
    }
 
939
 
 
940
    push_math(math_shift_group, display_style);
 
941
    mode = mmode;
 
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);
 
948
    if (nest_ptr == 1) {
 
949
        if (!output_active)
 
950
            lua_node_filter_s(buildpage_filter_callback, "before_display");
 
951
        build_page();
 
952
    }
 
953
}
 
954
 
 
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|).
 
959
 */
 
960
 
 
961
#define fam_in_range ((cur_fam>=0)&&(cur_fam<256))
 
962
 
 
963
delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
 
964
{
 
965
    char *hlp[] = {
 
966
        "I'm going to use 0 instead of that illegal code value.",
 
967
        NULL
 
968
    };
 
969
    delcodeval d;
 
970
    int cur_val1;               /* and the global |cur_val| */
 
971
    int mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
 
972
    mcls = 0;
 
973
    if (extcode == tex_mathcode) {      /* \delcode, this is the easiest */
 
974
        scan_int();
 
975
        /*  "MFCCFCC or "FCCFCC */
 
976
        if (doclass) {
 
977
            mcls = (cur_val / 0x1000000);
 
978
            cur_val = (cur_val & 0xFFFFFF);
 
979
        }
 
980
        if (cur_val > 0xFFFFFF) {
 
981
            tex_error("Invalid delimiter code", hlp);
 
982
            cur_val = 0;
 
983
        }
 
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 */
 
990
        scan_int();
 
991
        if (doclass) {
 
992
            mcls = (cur_val / 0x1000000);
 
993
            cur_val = (cur_val & 0xFFFFFF);
 
994
        }
 
995
        cur_val1 = cur_val;
 
996
        scan_int();
 
997
        if ((cur_val1 > 0xFFFFFF) || (cur_val > 0xFFFFFF)) {
 
998
            tex_error("Invalid delimiter code", hlp);
 
999
            cur_val1 = 0;
 
1000
            cur_val = 0;
 
1001
        }
 
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> */
 
1008
        if (doclass) {
 
1009
            scan_int();
 
1010
            mcls = cur_val;
 
1011
        }
 
1012
        scan_int();
 
1013
        msfam = cur_val;
 
1014
        scan_char_num();
 
1015
        mschr = cur_val;
 
1016
        if (msfam < 0 || msfam > 255) {
 
1017
            tex_error("Invalid delimiter code", hlp);
 
1018
            msfam = 0;
 
1019
            mschr = 0;
 
1020
        }
 
1021
        mlfam = 0;
 
1022
        mlchr = 0;
 
1023
    } else if (extcode == xetexnum_mathcode) {  /* \Udelcodenum */
 
1024
        /* "FF<21bits> */
 
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
 
1027
         */
 
1028
        if (doclass) {          /* such a primitive doesn't exist */
 
1029
            confusion("xetexnum_mathcode");
 
1030
        }
 
1031
        scan_int();
 
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);
 
1036
            msfam = 0;
 
1037
            mschr = 0;
 
1038
        }
 
1039
        mlfam = 0;
 
1040
        mlchr = 0;
 
1041
    } else {
 
1042
        /* something's gone wrong */
 
1043
        confusion("unknown_extcode");
 
1044
    }
 
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;
 
1051
    return d;
 
1052
}
 
1053
 
 
1054
void scan_extdef_del_code(int level, int extcode)
 
1055
{
 
1056
    delcodeval d;
 
1057
    int p;
 
1058
    scan_char_num();
 
1059
    p = cur_val;
 
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);
 
1064
}
 
1065
 
 
1066
mathcodeval scan_mathchar(int extcode)
 
1067
{
 
1068
    char *hlp[] = {
 
1069
        "I'm going to use 0 instead of that illegal code value.",
 
1070
        NULL
 
1071
    };
 
1072
    mathcodeval d;
 
1073
    int mcls = 0, mfam = 0, mchr = 0;
 
1074
    if (extcode == tex_mathcode) {      /* \mathcode */
 
1075
        /* "TFCC */
 
1076
        scan_int();
 
1077
        if (cur_val > 0x8000) {
 
1078
            tex_error("Invalid math code", hlp);
 
1079
            cur_val = 0;
 
1080
        }
 
1081
        mcls = (cur_val / 0x1000);
 
1082
        mfam = ((cur_val % 0x1000) / 0x100);
 
1083
        mchr = (cur_val % 0x100);
 
1084
    } else if (extcode == aleph_mathcode) {     /* \omathcode */
 
1085
        /* "TFFCCCC */
 
1086
        scan_int();
 
1087
        if (cur_val > 0x8000000) {
 
1088
            tex_error("Invalid math code", hlp);
 
1089
            cur_val = 0;
 
1090
        }
 
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> */
 
1096
        scan_int();
 
1097
        mcls = cur_val;
 
1098
        scan_int();
 
1099
        mfam = cur_val;
 
1100
        scan_char_num();
 
1101
        mchr = cur_val;
 
1102
        if (mcls < 0 || mcls > 7 || mfam > 255) {
 
1103
            tex_error("Invalid math code", hlp);
 
1104
            mchr = 0;
 
1105
            mfam = 0;
 
1106
            mcls = 0;
 
1107
        }
 
1108
    } else if (extcode == xetexnum_mathcode) {
 
1109
        /* "FFT<21bits> */
 
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
 
1112
         */
 
1113
        /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */
 
1114
        scan_int();
 
1115
        mfam = (cur_val / 0x200000) & 0x7FF;
 
1116
        mcls = mfam % 0x08;
 
1117
        mfam = mfam / 0x08;
 
1118
        mchr = cur_val & 0x1FFFFF;
 
1119
        if (mchr > 0x10FFFF) {
 
1120
            tex_error("Invalid math code", hlp);
 
1121
            mcls = 0;
 
1122
            mfam = 0;
 
1123
            mchr = 0;
 
1124
        }
 
1125
    } else {
 
1126
        /* something's gone wrong */
 
1127
        confusion("unknown_extcode");
 
1128
    }
 
1129
    d.class_value = mcls;
 
1130
    d.family_value = mfam;
 
1131
    d.origin_value = extcode;
 
1132
    d.character_value = mchr;
 
1133
    return d;
 
1134
}
 
1135
 
 
1136
 
 
1137
void scan_extdef_math_code(int level, int extcode)
 
1138
{
 
1139
    mathcodeval d;
 
1140
    int p;
 
1141
    scan_char_num();
 
1142
    p = cur_val;
 
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);
 
1147
}
 
1148
 
 
1149
 
 
1150
/* this reads in a delcode when actually a mathcode is needed */
 
1151
 
 
1152
mathcodeval scan_delimiter_as_mathchar(int extcode)
 
1153
{
 
1154
    delcodeval dval;
 
1155
    mathcodeval mval;
 
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;
 
1161
    return mval;
 
1162
}
 
1163
 
 
1164
/* this has to match the inverse routine in the pascal code
 
1165
 * where the |\Umathchardef| is executed 
 
1166
 */
 
1167
 
 
1168
mathcodeval mathchar_from_integer(int value, int extcode)
 
1169
{
 
1170
    mathcodeval mval;
 
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;
 
1185
    }
 
1186
    return mval;
 
1187
}
 
1188
 
 
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|.
 
1195
*/
 
1196
 
 
1197
 
 
1198
#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
 
1199
 
 
1200
 
 
1201
int scan_math(pointer p, int mstyle)
 
1202
{
 
1203
    /* label restart,reswitch,exit; */
 
1204
    mathcodeval mval;
 
1205
    assert(p != null);
 
1206
  RESTART:
 
1207
    get_next_nb_nr();
 
1208
  RESWITCH:
 
1209
    switch (cur_cmd) {
 
1210
    case letter_cmd:
 
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);
 
1219
            x_token();
 
1220
            back_input();
 
1221
            goto RESTART;
 
1222
        }
 
1223
        break;
 
1224
    case char_num_cmd:
 
1225
        scan_char_num();
 
1226
        cur_chr = cur_val;
 
1227
        cur_cmd = char_given_cmd;
 
1228
        goto RESWITCH;
 
1229
        break;
 
1230
    case math_char_num_cmd:
 
1231
        if (cur_chr == 0)
 
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);
 
1239
        else
 
1240
            confusion("scan_math");
 
1241
        break;
 
1242
    case math_given_cmd:
 
1243
        mval = mathchar_from_integer(cur_chr, tex_mathcode);
 
1244
        break;
 
1245
    case omath_given_cmd:
 
1246
        mval = mathchar_from_integer(cur_chr, aleph_mathcode);
 
1247
        break;
 
1248
    case xmath_given_cmd:
 
1249
        mval = mathchar_from_integer(cur_chr, xetex_mathcode);
 
1250
        break;
 
1251
    case delim_num_cmd:
 
1252
        if (cur_chr == 0)
 
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);
 
1258
        else
 
1259
            confusion("scan_math");
 
1260
        break;
 
1261
    default:
 
1262
        /* The pointer |p| is placed on |save_stack| while a complex subformula
 
1263
           is being scanned. */
 
1264
        back_input();
 
1265
        scan_left_brace();
 
1266
        set_saved_record(0, saved_math, 0, p);
 
1267
        incr(save_ptr);
 
1268
        push_math(math_group, mstyle);
 
1269
        return 1;
 
1270
    }
 
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;
 
1275
    else
 
1276
        math_fam(p) = mval.family_value;
 
1277
    return 0;
 
1278
}
 
1279
 
 
1280
 
 
1281
/*
 
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.
 
1286
*/
 
1287
 
 
1288
void set_math_char(mathcodeval mval)
 
1289
{
 
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);
 
1296
        x_token();
 
1297
        back_input();
 
1298
    } else {
 
1299
        pointer q;
 
1300
        p = new_noad();
 
1301
        q = new_node(math_char_node, 0);
 
1302
        nucleus(p) = q;
 
1303
        math_character(nucleus(p)) = mval.character_value;
 
1304
        math_fam(nucleus(p)) = mval.family_value;
 
1305
        if (mval.class_value == var_code) {
 
1306
            if (fam_in_range)
 
1307
                math_fam(nucleus(p)) = cur_fam;
 
1308
            subtype(p) = ord_noad_type;
 
1309
        } else {
 
1310
            switch (mval.class_value) {
 
1311
          /* *INDENT-OFF* */
 
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;
 
1319
          /* *INDENT-ON* */
 
1320
            }
 
1321
        }
 
1322
        vlink(tail) = p;
 
1323
        tail = p;
 
1324
    }
 
1325
}
 
1326
 
 
1327
 
 
1328
/*
 
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.
 
1333
*/
 
1334
 
 
1335
void math_char_in_text(mathcodeval mval)
 
1336
{
 
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);
 
1343
        x_token();
 
1344
        back_input();
 
1345
    } else {
 
1346
        p = new_char(fam_fnt(mval.family_value, text_size),
 
1347
                     mval.character_value);
 
1348
        vlink(tail) = p;
 
1349
        tail = p;
 
1350
    }
 
1351
}
 
1352
 
 
1353
 
 
1354
void math_math_comp(void)
 
1355
{
 
1356
    pointer q;
 
1357
    tail_append(new_noad());
 
1358
    subtype(tail) = cur_chr;
 
1359
    q = new_node(math_char_node, 0);
 
1360
    nucleus(tail) = q;
 
1361
    if (cur_chr == over_noad_type)
 
1362
        (void) scan_math(nucleus(tail), cramped_style(m_style));
 
1363
    else
 
1364
        (void) scan_math(nucleus(tail), m_style);
 
1365
}
 
1366
 
 
1367
 
 
1368
void math_limit_switch(void)
 
1369
{
 
1370
    char *hlp[] = {
 
1371
        "I'm ignoring this misplaced \\limits or \\nolimits command.",
 
1372
        NULL
 
1373
    };
 
1374
    if (head != tail) {
 
1375
        if (type(tail) == simple_noad) {
 
1376
            subtype(tail) = cur_chr;
 
1377
            return;
 
1378
        }
 
1379
    }
 
1380
    tex_error("Limit controls must follow a math operator", hlp);
 
1381
}
 
1382
 
 
1383
/*
 
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.
 
1388
*/
 
1389
 
 
1390
 
 
1391
void scan_delimiter(pointer p, int r)
 
1392
{
 
1393
    delcodeval dval;
 
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) {
 
1401
        get_next_nb_nr();
 
1402
        switch (cur_cmd) {
 
1403
        case letter_cmd:
 
1404
        case other_char_cmd:
 
1405
            dval = get_del_code(cur_chr);
 
1406
            break;
 
1407
        case delim_num_cmd:
 
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);
 
1414
            else
 
1415
                confusion("scan_delimiter1");
 
1416
            break;
 
1417
        default:
 
1418
            dval.small_family_value = -1;
 
1419
            break;
 
1420
        }
 
1421
    } else {
 
1422
        confusion("scan_delimiter2");
 
1423
    }
 
1424
    if (p == null)
 
1425
        return;
 
1426
    if (dval.small_family_value < 0) {
 
1427
        char *hlp[] = {
 
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>'.",
 
1434
            NULL
 
1435
        };
 
1436
        back_error("Missing delimiter (. inserted)", hlp);
 
1437
        small_fam(p) = 0;
 
1438
        small_char(p) = 0;
 
1439
        large_fam(p) = 0;
 
1440
        large_char(p) = 0;
 
1441
    } else {
 
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;
 
1446
    }
 
1447
    return;
 
1448
}
 
1449
 
 
1450
 
 
1451
void math_radical(void)
 
1452
{
 
1453
    halfword q;
 
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);
 
1474
    else
 
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);
 
1480
        vlink(q) = tail;
 
1481
        degree(tail) = q;
 
1482
        if (!scan_math(degree(tail), sup_sup_style(m_style))) {
 
1483
            vlink(degree(tail)) = null;
 
1484
            q = new_node(math_char_node, 0);
 
1485
            nucleus(tail) = q;
 
1486
            (void) scan_math(nucleus(tail), cramped_style(m_style));
 
1487
        }
 
1488
    } else {
 
1489
        q = new_node(math_char_node, 0);
 
1490
        nucleus(tail) = q;
 
1491
        (void) scan_math(nucleus(tail), cramped_style(m_style));
 
1492
    }
 
1493
}
 
1494
 
 
1495
void math_ac(void)
 
1496
{
 
1497
    halfword q;
 
1498
    mathcodeval t, b;
 
1499
    t.character_value = 0;
 
1500
    t.family_value = 0;
 
1501
    b.character_value = 0;
 
1502
    b.family_value = 0;
 
1503
    if (cur_cmd == accent_cmd) {
 
1504
        char *hlp[] = {
 
1505
            "I'm changing \\accent to \\mathaccent here; wish me luck.",
 
1506
            "(Accents are not the same in formulas as they are in text.)",
 
1507
            NULL
 
1508
        };
 
1509
        tex_error("Please use \\mathaccent for accents in math mode", hlp);
 
1510
    }
 
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);
 
1523
    } else {
 
1524
        confusion("math_ac");
 
1525
    }
 
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;
 
1532
        else
 
1533
            math_fam(accent_chr(tail)) = t.family_value;
 
1534
    }
 
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;
 
1541
        else
 
1542
            math_fam(bot_accent_chr(tail)) = b.family_value;
 
1543
    }
 
1544
    q = new_node(math_char_node, 0);
 
1545
    nucleus(tail) = q;
 
1546
    (void) scan_math(nucleus(tail), cramped_style(m_style));
 
1547
}
 
1548
 
 
1549
pointer math_vcenter_group(pointer p)
 
1550
{
 
1551
    pointer q, r;
 
1552
    q = new_noad();
 
1553
    subtype(q) = vcenter_noad_type;
 
1554
    r = new_node(sub_box_node, 0);
 
1555
    nucleus(q) = r;
 
1556
    math_list(nucleus(q)) = p;
 
1557
    return q;
 
1558
}
 
1559
 
 
1560
/*
 
1561
The routine that scans the four mlists of a \.{\\mathchoice} is very
 
1562
much like the routine that builds discretionary nodes.
 
1563
*/
 
1564
 
 
1565
void append_choices(void)
 
1566
{
 
1567
    tail_append(new_choice());
 
1568
    incr(save_ptr);
 
1569
    set_saved_record(-1, saved_choices, 0, 0);
 
1570
    push_math(math_choice_group, display_style);
 
1571
    scan_left_brace();
 
1572
}
 
1573
 
 
1574
void build_choices(void)
 
1575
{
 
1576
    pointer p;                  /* the current mlist */
 
1577
    int prev_style;
 
1578
    prev_style = m_style;
 
1579
    unsave_math();
 
1580
    p = fin_mlist(null);
 
1581
    assert(saved_type(-1) == saved_choices);
 
1582
    switch (saved_value(-1)) {
 
1583
    case 0:
 
1584
        display_mlist(tail) = p;
 
1585
        break;
 
1586
    case 1:
 
1587
        text_mlist(tail) = p;
 
1588
        break;
 
1589
    case 2:
 
1590
        script_mlist(tail) = p;
 
1591
        break;
 
1592
    case 3:
 
1593
        script_script_mlist(tail) = p;
 
1594
        decr(save_ptr);
 
1595
        return;
 
1596
        break;
 
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));
 
1600
    scan_left_brace();
 
1601
}
 
1602
 
 
1603
/*
 
1604
 Subscripts and superscripts are attached to the previous nucleus by the
 
1605
 action procedure called |sub_sup|. 
 
1606
*/
 
1607
 
 
1608
void sub_sup(void)
 
1609
{
 
1610
    pointer q;
 
1611
    if (tail == head || (!scripts_allowed(tail))) {
 
1612
        tail_append(new_noad());
 
1613
        q = new_node(sub_mlist_node, 0);
 
1614
        nucleus(tail) = q;
 
1615
    }
 
1616
    if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) {   /* super_sub_script */
 
1617
        if (supscr(tail) != null) {
 
1618
            char *hlp[] = {
 
1619
                "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
 
1620
            };
 
1621
            tail_append(new_noad());
 
1622
            q = new_node(sub_mlist_node, 0);
 
1623
            nucleus(tail) = q;
 
1624
            tex_error("Double superscript", hlp);
 
1625
        }
 
1626
        q = new_node(math_char_node, 0);
 
1627
        supscr(tail) = q;
 
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) {
 
1631
            char *hlp[] = {
 
1632
                "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
 
1633
            };
 
1634
            tail_append(new_noad());
 
1635
            q = new_node(sub_mlist_node, 0);
 
1636
            nucleus(tail) = q;
 
1637
            tex_error("Double subscript", hlp);
 
1638
        }
 
1639
        q = new_node(math_char_node, 0);
 
1640
        subscr(tail) = q;
 
1641
        (void) scan_math(subscr(tail), sub_style(m_style));
 
1642
    }
 
1643
}
 
1644
 
 
1645
/*
 
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}'
 
1652
delimiters. 
 
1653
*/
 
1654
 
 
1655
void math_fraction(void)
 
1656
{
 
1657
    small_number c;             /* the type of generalized fraction we are scanning */
 
1658
    pointer q;
 
1659
    c = cur_chr;
 
1660
    if (incompleat_noad != null) {
 
1661
        char *hlp[] = {
 
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}'.",
 
1665
            NULL
 
1666
        };
 
1667
        if (c >= delimited_code) {
 
1668
            scan_delimiter(null, no_mathcode);
 
1669
            scan_delimiter(null, no_mathcode);
 
1670
        }
 
1671
        if ((c % delimited_code) == above_code)
 
1672
            scan_normal_dimen();
 
1673
        tex_error("Ambiguous; you need another { and }", hlp);
 
1674
    } else {
 
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);
 
1678
        vlink(head) = null;
 
1679
        tail = head;
 
1680
        m_style = cramped_style(m_style);
 
1681
 
 
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);
 
1689
        }
 
1690
        switch (c % delimited_code) {
 
1691
        case above_code:
 
1692
            scan_normal_dimen();
 
1693
            thickness(incompleat_noad) = cur_val;
 
1694
            break;
 
1695
        case over_code:
 
1696
            thickness(incompleat_noad) = default_code;
 
1697
            break;
 
1698
        case atop_code:
 
1699
            thickness(incompleat_noad) = 0;
 
1700
            break;
 
1701
        }                       /* there are no other cases */
 
1702
    }
 
1703
}
 
1704
 
 
1705
 
 
1706
 
 
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.
 
1712
*/
 
1713
 
 
1714
 
 
1715
pointer fin_mlist(pointer p)
 
1716
{
 
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;
 
1721
        } else {
 
1722
            q = new_node(sub_mlist_node, 0);
 
1723
            denominator(incompleat_noad) = q;
 
1724
        }
 
1725
        math_list(denominator(incompleat_noad)) = vlink(head);
 
1726
        if (p == null) {
 
1727
            q = incompleat_noad;
 
1728
        } else {
 
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;
 
1736
        }
 
1737
    } else {
 
1738
        vlink(tail) = p;
 
1739
        q = vlink(head);
 
1740
    }
 
1741
    pop_nest();
 
1742
    return q;
 
1743
}
 
1744
 
 
1745
 
 
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.
 
1750
*/
 
1751
 
 
1752
void close_math_group(pointer p)
 
1753
{
 
1754
    pointer q;
 
1755
    int old_style = m_style;
 
1756
    unsave_math();
 
1757
 
 
1758
    decr(save_ptr);
 
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;
 
1763
    if (p != null) {
 
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));
 
1772
                    } else {
 
1773
                        math_list(saved_value(0)) = math_list(nucleus(p));
 
1774
                        math_list(nucleus(p)) = null;
 
1775
                    }
 
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;
 
1779
                    flush_node(p);
 
1780
                }
 
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) {
 
1785
                        q = head;
 
1786
                        while (vlink(q) != tail)
 
1787
                            q = vlink(q);
 
1788
                        vlink(q) = p;
 
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;
 
1795
                        flush_node(tail);
 
1796
                        tail = p;
 
1797
                    }
 
1798
                }
 
1799
            }
 
1800
        }
 
1801
    }
 
1802
    if (vlink(saved_value(0)) > 0) {
 
1803
        pointer q;
 
1804
        q = new_node(math_char_node, 0);
 
1805
        nucleus(vlink(saved_value(0))) = q;
 
1806
        vlink(saved_value(0)) = null;
 
1807
        saved_value(0) = q;
 
1808
        (void) scan_math(saved_value(0), old_style);
 
1809
        /* restart */
 
1810
    }
 
1811
}
 
1812
 
 
1813
/*
 
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}.
 
1818
*/
 
1819
 
 
1820
void math_left_right(void)
 
1821
{
 
1822
    small_number t;             /* |left_noad_side| .. |right_noad_side| */
 
1823
    pointer p;                  /* new noad */
 
1824
    pointer q;                  /* resulting mlist */
 
1825
    pointer r;                  /* temporary */
 
1826
    t = cur_chr;
 
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) {
 
1831
                char *hlp[] = {
 
1832
                    "I'm ignoring a \\middle that had no matching \\left.",
 
1833
                    NULL
 
1834
                };
 
1835
                tex_error("Extra \\middle", hlp);
 
1836
            } else {
 
1837
                char *hlp[] = {
 
1838
                    "I'm ignoring a \\right that had no matching \\left.",
 
1839
                    NULL
 
1840
                };
 
1841
                tex_error("Extra \\right", hlp);
 
1842
            }
 
1843
        } else {
 
1844
            off_save();
 
1845
        }
 
1846
    } else {
 
1847
        p = new_noad();
 
1848
        type(p) = fence_noad;
 
1849
        subtype(p) = t;
 
1850
        r = new_node(delim_node, 0);
 
1851
        delimiter(p) = r;
 
1852
        scan_delimiter(delimiter(p), no_mathcode);
 
1853
        if (t == left_noad_side) {
 
1854
            q = p;
 
1855
        } else {
 
1856
            q = fin_mlist(p);
 
1857
            unsave_math();
 
1858
        }
 
1859
        if (t != right_noad_side) {
 
1860
            push_math(math_left_group, m_style);
 
1861
            vlink(head) = q;
 
1862
            tail = p;
 
1863
            delim_ptr = p;
 
1864
        } else {
 
1865
            tail_append(new_noad());
 
1866
            subtype(tail) = inner_noad_type;
 
1867
            r = new_node(sub_mlist_node, 0);
 
1868
            nucleus(tail) = r;
 
1869
            math_list(nucleus(tail)) = q;
 
1870
        }
 
1871
    }
 
1872
}
 
1873
 
 
1874
 
 
1875
/* \TeX\ gets to the following part of the program when 
 
1876
the first `\.\$' ending a display has been scanned.
 
1877
*/
 
1878
 
 
1879
static void check_second_math_shift(void)
 
1880
{
 
1881
    get_x_token();
 
1882
    if (cur_cmd != math_shift_cmd) {
 
1883
        char *hlp[] = {
 
1884
            "The `$' that I just saw supposedly matches a previous `$$'.",
 
1885
            "So I shall assume that you typed `$$' both times.",
 
1886
            NULL
 
1887
        };
 
1888
        back_error("Display math should end with $$", hlp);
 
1889
    }
 
1890
}
 
1891
 
 
1892
static void check_display_math_end(void)
 
1893
{
 
1894
    if (cur_chr != cramped_display_style) {
 
1895
        char *hlp[] = {
 
1896
            "I shall assume that you typed that.",
 
1897
            NULL
 
1898
        };
 
1899
        tex_error("Display math should end with \\Ustopdisplaymath", hlp);
 
1900
    }
 
1901
}
 
1902
 
 
1903
static void check_inline_math_end(void)
 
1904
{
 
1905
    if (cur_chr != cramped_text_style) {
 
1906
        char *hlp[] = {
 
1907
            "I shall assume that you typed that.",
 
1908
            NULL
 
1909
        };
 
1910
        tex_error("Inline math should end with \\Ustopmath", hlp);
 
1911
    }
 
1912
}
 
1913
 
 
1914
void resume_after_display(void)
 
1915
{
 
1916
    if (cur_group != math_shift_group)
 
1917
        confusion("display");
 
1918
    unsave_math();
 
1919
    prev_graf = prev_graf + 3;
 
1920
    push_nest();
 
1921
    mode = hmode;
 
1922
    space_factor = 1000;
 
1923
    tail_append(make_local_par_node()); /* this needs to be intercepted in 
 
1924
                                           the display math start ! */
 
1925
    get_x_token();
 
1926
    if (cur_cmd != spacer_cmd)
 
1927
        back_input();
 
1928
    if (nest_ptr == 1) {
 
1929
        lua_node_filter_s(buildpage_filter_callback, "after_display");
 
1930
        build_page();
 
1931
    }
 
1932
}
 
1933
 
 
1934
 
 
1935
/* 
 
1936
 
 
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| 
 
1942
structures.
 
1943
 
 
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.
 
1947
 
 
1948
*/
 
1949
 
 
1950
static boolean math_and_text_reversed_p(void)
 
1951
{
 
1952
    int i = save_ptr - 1;
 
1953
    while (save_type(i) != level_boundary)
 
1954
        i--;
 
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)))
 
1959
                return true;
 
1960
        }
 
1961
        i++;
 
1962
    }
 
1963
    return false;
 
1964
}
 
1965
 
 
1966
 
 
1967
/*
 
1968
  The fussiest part of math mode processing occurs when a displayed formula is 
 
1969
  being centered and placed with an optional equation number.
 
1970
*/
 
1971
 
 
1972
/* At this time we are in vertical mode (or internal vertical mode).  
 
1973
 
 
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).
 
1977
 
 
1978
*/
 
1979
 
 
1980
static void finish_displayed_math(boolean l, pointer a, pointer p)
 
1981
{
 
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();
 
1995
 
 
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);
 
2000
    t = adjust_tail;
 
2001
    adjust_tail = null;
 
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;
 
2007
    if (a == null) {
 
2008
        eqno_w = 0;
 
2009
        eqno_w2 = 0;
 
2010
    } else {
 
2011
        eqno_w = width(a);
 
2012
        eqno_w2 = eqno_w + get_math_quad(text_size);
 
2013
    }
 
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. */
 
2017
        if ((eqno_w != 0)
 
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;
 
2023
            flush_node(eq_box);
 
2024
            eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
 
2025
        } else {
 
2026
            eqno_w = 0;
 
2027
            if (eq_w > line_w) {
 
2028
                list_ptr(eq_box) = null;
 
2029
                flush_node(eq_box);
 
2030
                eq_box = hpack(p, line_w, exactly, -1);
 
2031
            }
 
2032
        }
 
2033
        eq_w = width(eq_box);
 
2034
    }
 
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.
 
2042
     */
 
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);
 
2046
        if (p != null)
 
2047
            if (!is_char_node(p))
 
2048
                if (type(p) == glue_node)
 
2049
                    d = 0;
 
2050
    }
 
2051
 
 
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.
 
2057
     */
 
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;
 
2062
    } else {
 
2063
        g1 = above_display_short_skip_code;
 
2064
        g2 = below_display_short_skip_code;
 
2065
    }
 
2066
 
 
2067
    if (l && (eqno_w == 0)) {   /* \leqno on a forced single line due to |width=0| */
 
2068
        /* it follows that |type(a)=hlist_node| */
 
2069
        if (swap_dir) {
 
2070
            shift_amount(a) = line_w + line_s;
 
2071
        } else {
 
2072
            shift_amount(a) = line_s;
 
2073
        }
 
2074
        append_to_vlist(a);
 
2075
        tail_append(new_penalty(inf_penalty));
 
2076
    } else {
 
2077
        tail_append(new_param_glue(g1));
 
2078
    }
 
2079
 
 
2080
    if (eqno_w != 0) {
 
2081
        r = new_kern(line_w - eq_w - eqno_w - d);
 
2082
        if (l) {
 
2083
            vlink(a) = r;
 
2084
            vlink(r) = eq_box;
 
2085
            eq_box = a;
 
2086
            d = 0;
 
2087
        } else {
 
2088
            vlink(eq_box) = r;
 
2089
            vlink(r) = a;
 
2090
        }
 
2091
        eq_box = hpack(eq_box, 0, additional, -1);
 
2092
    }
 
2093
    if (swap_dir) {
 
2094
        /*    d = line_w - d; */
 
2095
        if (eqno_w != 0) {
 
2096
            if (l)
 
2097
                d = line_w - width(eq_box);
 
2098
            else
 
2099
                d = 0;
 
2100
        } else {
 
2101
            d = line_w - eq_w - eqno_w - d;
 
2102
        }
 
2103
    }
 
2104
    shift_amount(eq_box) = line_s + d;
 
2105
    append_to_vlist(eq_box);
 
2106
 
 
2107
    if ((a != null) && (eqno_w == 0) && !l) {
 
2108
        tail_append(new_penalty(inf_penalty));
 
2109
        if (!swap_dir) {
 
2110
            shift_amount(a) = line_s + line_w - width(a);
 
2111
        } else {
 
2112
            shift_amount(a) = line_s;
 
2113
        }
 
2114
        append_to_vlist(a);
 
2115
        g2 = 0;
 
2116
    }
 
2117
    if (t != adjust_head) {     /* migrating material comes after equation number */
 
2118
        vlink(tail) = vlink(adjust_head);
 
2119
        tail = t;
 
2120
    }
 
2121
    if (pre_t != pre_adjust_head) {
 
2122
        vlink(tail) = vlink(pre_adjust_head);
 
2123
        tail = pre_t;
 
2124
    }
 
2125
    tail_append(new_penalty(int_par(post_display_penalty_code)));
 
2126
    if (g2 > 0)
 
2127
        tail_append(new_param_glue(g2));
 
2128
 
 
2129
    resume_after_display();
 
2130
}
 
2131
 
 
2132
 
 
2133
 
 
2134
void after_math(void)
 
2135
{
 
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}' */
 
2140
    m = mode;
 
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)) {
 
2144
        you_cant();
 
2145
    }
 
2146
    if (mode == -m) {           /* end of equation number */
 
2147
        if (cur_cmd == math_shift_cmd) {
 
2148
            check_second_math_shift();
 
2149
        } else {
 
2150
            check_display_math_end();
 
2151
        }
 
2152
        run_mlist_to_hlist(p, text_style, false);
 
2153
        a = hpack(vlink(temp_head), 0, additional, -1);
 
2154
        unsave_math();
 
2155
        decr(save_ptr);         /* now |cur_group=math_shift_group| */
 
2156
        assert(saved_type(0) == saved_eqno);
 
2157
        if (saved_value(0) == 1)
 
2158
            l = true;
 
2159
        m = mode;
 
2160
        p = fin_mlist(null);
 
2161
    }
 
2162
    if (m < 0) {
 
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.
 
2168
         */
 
2169
        if (cur_cmd == math_shift_cs_cmd) {
 
2170
            check_inline_math_end();
 
2171
        }
 
2172
        tail_append(new_math(math_surround, before));
 
2173
        if (dir_math_save) {
 
2174
            tail_append(new_dir(math_direction));
 
2175
        }
 
2176
        run_mlist_to_hlist(p, text_style, (mode > 0));
 
2177
        vlink(tail) = vlink(temp_head);
 
2178
        while (vlink(tail) != null)
 
2179
            tail = vlink(tail);
 
2180
        if (dir_math_save) {
 
2181
            tail_append(new_dir(math_direction - 64));
 
2182
        }
 
2183
        dir_math_save = false;
 
2184
        tail_append(new_math(math_surround, after));
 
2185
        space_factor = 1000;
 
2186
        unsave_math();
 
2187
    } else {
 
2188
        if (a == null) {
 
2189
            if (cur_cmd == math_shift_cmd) {
 
2190
                check_second_math_shift();
 
2191
            } else {
 
2192
                check_display_math_end();
 
2193
            }
 
2194
        }
 
2195
        run_mlist_to_hlist(p, display_style, false);
 
2196
        finish_displayed_math(l, a, vlink(temp_head));
 
2197
    }
 
2198
}
 
2199
 
 
2200
/*
 
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.
 
2205
*/
 
2206
 
 
2207
 
 
2208
void finish_display_alignment(pointer p, pointer q, memory_word aux_save)
 
2209
{
 
2210
    do_assignments();
 
2211
    if (cur_cmd != math_shift_cmd) {
 
2212
        char *hlp[] = {
 
2213
            "Displays can use special alignments (like \\eqalignno)",
 
2214
            "only if nothing but the alignment itself is between $$'s.",
 
2215
            NULL
 
2216
        };
 
2217
        back_error("Missing $$ inserted", hlp);
 
2218
    } else {
 
2219
        check_second_math_shift();
 
2220
    }
 
2221
    pop_nest();
 
2222
    tail_append(new_penalty(int_par(pre_display_penalty_code)));
 
2223
    tail_append(new_param_glue(above_display_skip_code));
 
2224
    vlink(tail) = p;
 
2225
    if (p != null)
 
2226
        tail = q;
 
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();
 
2231
}
 
2232
 
 
2233
/* Interface to \.{\\Umath} and \.{\\mathstyle} */
 
2234
 
 
2235
void setup_math_style(void)
 
2236
{
 
2237
    pointer q;
 
2238
    tail_append(new_noad());
 
2239
    q = new_node(math_char_node, 0);
 
2240
    nucleus(tail) = q;
 
2241
    (void) scan_math(nucleus(tail), num_style(m_style));
 
2242
}
 
2243
 
 
2244
 
 
2245
void print_math_style(void)
 
2246
{
 
2247
    if (abs(mode) == mmode)
 
2248
        print_int(m_style);
 
2249
    else
 
2250
        print_int(-1);
 
2251
}