~ubuntu-branches/ubuntu/wily/luatex/wily

« 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: 2010-04-29 00:47:19 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20100429004719-o42etkqe90n97b9e
Tags: 0.60.1-1
* new upstream release, adapt build-script patch
* disable patch: upstream-epstopdf_cc_no_xpdf_patching, included upstream
* disable patch: libpoppler-0.12, 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
 
}