1% texmath.w 2% 3% Copyright 2008-2010 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@ @c 21 22 23#include "ptexlib.h" 24 25@ @c 26#define mode cur_list.mode_field 27#define head cur_list.head_field 28#define tail cur_list.tail_field 29#define prev_graf cur_list.pg_field 30#define eTeX_aux cur_list.eTeX_aux_field 31#define delim_ptr eTeX_aux 32#define space_factor cur_list.space_factor_field 33#define incompleat_noad cur_list.incompleat_noad_field 34 35#define cur_fam int_par(cur_fam_code) 36#define text_direction int_par(text_direction_code) 37 38#define var_code 7 39 40@ TODO: not sure if this is the right order 41@c 42#define back_error(A,B) do { \ 43 OK_to_interrupt=false; \ 44 back_input(); \ 45 OK_to_interrupt=true; \ 46 tex_error(A,B); \ 47 } while (0) 48 49@ @c 50int scan_math(pointer, int); 51pointer fin_mlist(pointer); 52 53#define pre_display_size dimen_par(pre_display_size_code) 54#define hsize dimen_par(hsize_code) 55#define display_width dimen_par(display_width_code) 56#define display_indent dimen_par(display_indent_code) 57#define math_surround dimen_par(math_surround_code) 58#define hang_indent dimen_par(hang_indent_code) 59#define hang_after int_par(hang_after_code) 60#define every_math equiv(every_math_loc) 61#define every_display equiv(every_display_loc) 62#define par_shape_ptr equiv(par_shape_loc) 63 64 65@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an 66{\sl mlist}, which is essentially a tree structure representing that 67formula. An mlist is a linear sequence of items, but we can regard it as 68a tree structure because mlists can appear within mlists. For example, many 69of the entries can be subscripted or superscripted, and such ``scripts'' 70are mlists in their own right. 71 72An entire formula is parsed into such a tree before any of the actual 73typesetting is done, because the current style of type is usually not 74known until the formula has been fully scanned. For example, when the 75formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell 76that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. 77 78During the scanning process, each element of the mlist being built is 79classified as a relation, a binary operator, an open parenthesis, etc., 80or as a construct like `\.{\\sqrt}' that must be built up. This classification 81appears in the mlist data structure. 82 83After a formula has been fully scanned, the mlist is converted to an hlist 84so that it can be incorporated into the surrounding text. This conversion is 85controlled by a recursive procedure that decides all of the appropriate 86styles by a ``top-down'' process starting at the outermost level and working 87in towards the subformulas. The formula is ultimately pasted together using 88combinations of horizontal and vertical boxes, with glue and penalty nodes 89inserted as necessary. 90 91An mlist is represented internally as a linked list consisting chiefly 92of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat 93similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are 94allowed to appear in mlists together with the noads; \TeX\ tells the difference 95by means of the |type| field, since a noad's |type| is always greater than 96that of a node. An mlist does not contain character nodes, hlist nodes, vlist 97nodes, math nodes or unset nodes; in particular, each mlist item appears in the 98variable-size part of |mem|, so the |type| field is always present. 99 100Each noad is five or more words long. The first word contains the 101|type| and |subtype| and |link| fields that are already so familiar to 102us; the second contains the attribute list pointer, and the third, 103fourth an fifth words are called the noad's |nucleus|, |subscr|, and 104|supscr| fields. (This use of a combined attribute list is temporary. 105Eventually, each of fields need their own list) 106 107Consider, for example, the simple formula `\.{\$x\^2\$}', which would be 108parsed into an mlist containing a single element called an |ord_noad|. 109The |nucleus| of this noad is a representation of `\.x', the |subscr| is 110empty, and the |supscr| is a representation of `\.2'. 111 112The |nucleus|, |subscr|, and |supscr| fields are further broken into 113subfields. If |p| points to a noad, and if |q| is one of its principal 114fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the 115corresponding attribute of noad |p| is not present). Otherwise, there are 116several possibilities for the subfields, depending on the |type| of |q|. 117 118\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of 119the sixteen font families, and |character(q)| is the number of a character 120within a font of that family, as in a character node. 121 122\yskip\hang|type(q)=math_text_char_node| is similar, but the character is 123unsubscripted and unsuperscripted and it is followed immediately by another 124character from the same font. (This |type| setting appears only 125briefly during the processing; it is used to suppress unwanted italic 126corrections.) 127 128\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box 129node (either an |hlist_node| or a |vlist_node|) that should be used as the 130value of the field. The |shift_amount| in the subsidiary box node is the 131amount by which that box will be shifted downward. 132 133\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to 134an mlist; the mlist must be converted to an hlist in order to obtain 135the value of this field. 136 137\yskip\noindent In the latter case, we might have |math_list(q)=null|. This 138is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}' 139and `\.{\$P\$}' produce different results (the former will not have the 140``italic correction'' added to the width of |P|, but the ``script skip'' 141will be added). 142 143@c 144static void unsave_math(void) 145{ 146 unsave(); 147 decr(save_ptr); 148 flush_node_list(text_dir_ptr); 149 assert(saved_type(0) == saved_textdir); 150 text_dir_ptr = saved_value(0); 151} 152 153 154@ Sometimes it is necessary to destroy an mlist. The following 155subroutine empties the current list, assuming that |abs(mode)=mmode|. 156 157@c 158void flush_math(void) 159{ 160 flush_node_list(vlink(head)); 161 flush_node_list(incompleat_noad); 162 vlink(head) = null; 163 tail = head; 164 incompleat_noad = null; 165} 166 167@ Before we can do anything in math mode, we need fonts. 168 169@c 170#define MATHFONTSTACK 8 171#define MATHFONTDEFAULT 0 /* == nullfont */ 172 173static sa_tree math_fam_head = NULL; 174 175@ @c 176int fam_fnt(int fam_id, int size_id) 177{ 178 int n = fam_id + (256 * size_id); 179 return (int) get_sa_item(math_fam_head, n); 180} 181 182void def_fam_fnt(int fam_id, int size_id, int f, int lvl) 183{ 184 int n = fam_id + (256 * size_id); 185 set_sa_item(math_fam_head, n, (sa_tree_item) f, lvl); 186 fixup_math_parameters(fam_id, size_id, f, lvl); 187 if (int_par(tracing_assigns_code) > 0) { 188 begin_diagnostic(); 189 tprint("{assigning"); 190 print_char(' '); 191 print_cmd_chr(def_family_cmd, size_id); 192 print_int(fam_id); 193 print_char('='); 194 print_font_identifier(fam_fnt(fam_id, size_id)); 195 print_char('}'); 196 end_diagnostic(false); 197 } 198} 199 200@ @c 201static void unsave_math_fam_data(int gl) 202{ 203 sa_stack_item st; 204 if (math_fam_head->stack == NULL) 205 return; 206 while (math_fam_head->stack_ptr > 0 && 207 abs(math_fam_head->stack[math_fam_head->stack_ptr].level) 208 >= (int) gl) { 209 st = math_fam_head->stack[math_fam_head->stack_ptr]; 210 if (st.level > 0) { 211 rawset_sa_item(math_fam_head, st.code, st.value); 212 /* now do a trace message, if requested */ 213 if (int_par(tracing_restores_code) > 0) { 214 int size_id = st.code / 256; 215 int fam_id = st.code % 256; 216 begin_diagnostic(); 217 tprint("{restoring"); 218 print_char(' '); 219 print_cmd_chr(def_family_cmd, size_id); 220 print_int(fam_id); 221 print_char('='); 222 print_font_identifier(fam_fnt(fam_id, size_id)); 223 print_char('}'); 224 end_diagnostic(false); 225 } 226 } 227 (math_fam_head->stack_ptr)--; 228 } 229} 230 231 232 233@ and parameters 234 235@c 236#define MATHPARAMSTACK 8 237#define MATHPARAMDEFAULT undefined_math_parameter 238 239static sa_tree math_param_head = NULL; 240 241@ @c 242void def_math_param(int param_id, int style_id, scaled value, int lvl) 243{ 244 int n = param_id + (256 * style_id); 245 set_sa_item(math_param_head, n, (sa_tree_item) value, lvl); 246 if (int_par(tracing_assigns_code) > 0) { 247 begin_diagnostic(); 248 tprint("{assigning"); 249 print_char(' '); 250 print_cmd_chr(set_math_param_cmd, param_id); 251 print_cmd_chr(math_style_cmd, style_id); 252 print_char('='); 253 print_int(value); 254 print_char('}'); 255 end_diagnostic(false); 256 } 257} 258 259scaled get_math_param(int param_id, int style_id) 260{ 261 int n = param_id + (256 * style_id); 262 return (scaled) get_sa_item(math_param_head, n); 263} 264 265 266@ @c 267static 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@c 301void unsave_math_data(int gl) 302{ 303 unsave_math_fam_data(gl); 304 unsave_math_param_data(gl); 305} 306 307@ Dumping and undumping 308@c 309void dump_math_data(void) 310{ 311 if (math_fam_head == NULL) 312 math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT); 313 dump_sa_tree(math_fam_head); 314 if (math_param_head == NULL) 315 math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT); 316 dump_sa_tree(math_param_head); 317} 318 319void undump_math_data(void) 320{ 321 math_fam_head = undump_sa_tree(); 322 math_param_head = undump_sa_tree(); 323} 324 325@ @c 326void initialize_math(void) 327{ 328 if (math_fam_head == NULL) 329 math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT); 330 if (math_param_head == NULL) { 331 math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT); 332 initialize_math_spacing(); 333 } 334 return; 335} 336 337 338 339@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, 340Clo, Pun, or Inn, for purposes of spacing and line breaking. An 341|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, 342|punct_noad|, or |inner_noad| is used to represent portions of the various 343types. For example, an `\.=' sign in a formula leads to the creation of a 344|rel_noad| whose |nucleus| field is a representation of an equals sign 345(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} 346also results in a |rel_noad|. When a |rel_noad| is followed by an 347|op_noad|, say, and possibly separated by one or more ordinary nodes (not 348noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) 349just after the formula that corresponds to the |rel_noad|, unless there 350already was a penalty immediately following; and a ``thick space'' will be 351inserted just before the formula that corresponds to the |op_noad|. 352 353A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually 354has a |subtype=normal|. The only exception is that an |op_noad| might 355have |subtype=limits| or |no_limits|, if the normal positioning of 356limits has been overridden for this operator. 357 358A |radical_noad| also has a |left_delimiter| field, which usually 359represents a square root sign. 360 361A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. 362 363Delimiter fields have four subfields 364called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields 365represent variable-size delimiters by giving the ``small'' and ``large'' 366starting characters, as explained in Chapter~17 of {\sl The \TeX book}. 367@:TeXbook}{\sl The \TeX book@> 368 369A |fraction_noad| is actually quite different from all other noads. 370It has |thickness|, |denominator|, and |numerator| fields instead of 371|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value 372that tells how thick to make a fraction rule; however, the special 373value |default_code| is used to stand for the 374|default_rule_thickness| of the current size. The |numerator| and 375|denominator| point to mlists that define a fraction; we always have 376$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The 377|left_delimiter| and |right_delimiter| fields specify delimiters that will 378be placed at the left and right of the fraction. In this way, a 379|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, 380\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and 381 \.{\\abovewithdelims}. 382 383 384 385@ The |new_noad| function creates an |ord_noad| that is completely null 386 387@c 388pointer new_noad(void) 389{ 390 pointer p; 391 p = new_node(simple_noad, ord_noad_type); 392 /* all noad fields are zero after this */ 393 return p; 394} 395 396@ @c 397pointer new_sub_box(pointer curbox) 398{ 399 pointer p, q; 400 p = new_noad(); 401 q = new_node(sub_box_node, 0); 402 nucleus(p) = q; 403 math_list(nucleus(p)) = curbox; 404 return p; 405} 406 407 408@ A few more kinds of noads will complete the set: An |under_noad| has its 409nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places 410an accent over its nucleus; the accent character appears as 411|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad| 412centers its nucleus vertically with respect to the axis of the formula; 413in such noads we always have |type(nucleus(p))=sub_box|. 414 415And finally, we have the |fence_noad| type, to implement 416\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. 417The |nucleus| of such noads is 418replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces 419a |fence_noad| such that |delimiter(p)| holds the family and character 420codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| 421never appears in an mlist except as the first element, and a |fence_noad| 422with subtype |right_noad_side| never appears in an mlist 423except as the last element; furthermore, we either have both a |left_noad_side| 424and a |right_noad_side|, or neither one is present. 425 426 427 428@ Math formulas can also contain instructions like \.{\\textstyle} that 429override \TeX's normal style rules. A |style_node| is inserted into the 430data structure to record such instructions; it is three words long, so it 431is considered a node instead of a noad. The |subtype| is either |display_style| 432or |text_style| or |script_style| or |script_script_style|. The 433second and third words of a |style_node| are not used, but they are 434present because a |choice_node| is converted to a |style_node|. 435 436\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles 437|display_style|, \dots, |script_script_style|, and adds~1 to get the 438``cramped'' versions of these styles. This gives a numerical order that 439is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; 440i.e., a smaller style has a larger numerical value. 441@:TeXbook}{\sl The \TeX book@> 442 443@c 444const char *math_style_names[] = { 445 "display", "crampeddisplay", 446 "text", "crampedtext", 447 "script", "crampedscript", 448 "scriptscript", "crampedscriptscript", 449 NULL 450}; 451 452const char *math_param_names[] = { 453 "quad", "axis", "operatorsize", 454 "overbarkern", "overbarrule", "overbarvgap", 455 "underbarkern", "underbarrule", "underbarvgap", 456 "radicalkern", "radicalrule", "radicalvgap", 457 "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", 458 "stackvgap", "stacknumup", "stackdenomdown", 459 "fractionrule", "fractionnumvgap", "fractionnumup", 460 "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", 461 "limitabovevgap", "limitabovebgap", "limitabovekern", 462 "limitbelowvgap", "limitbelowbgap", "limitbelowkern", 463 "underdelimitervgap", "underdelimiterbgap", 464 "overdelimitervgap", "overdelimiterbgap", 465 "subshiftdrop", "supshiftdrop", "subshiftdown", 466 "subsupshiftdown", "subtopmax", "supshiftup", 467 "supbottommin", "supsubbottommax", "subsupvgap", 468 "spaceafterscript", "connectoroverlapmin", 469 "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", 470 "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", 471 "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", 472 "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", 473 "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", 474 "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", 475 "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", 476 "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", 477 "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", 478 "openopenspacing", "openclosespacing", "openpunctspacing", 479 "openinnerspacing", 480 "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", 481 "closeopenspacing", "closeclosespacing", "closepunctspacing", 482 "closeinnerspacing", 483 "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", 484 "punctopenspacing", "punctclosespacing", "punctpunctspacing", 485 "punctinnerspacing", 486 "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", 487 "inneropenspacing", "innerclosespacing", "innerpunctspacing", 488 "innerinnerspacing", 489 NULL 490}; 491 492@ @c 493pointer new_style(small_number s) 494{ /* create a style node */ 495 m_style = s; 496 return new_node(style_node, s); 497} 498 499@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which 500has special subfields |display_mlist|, |text_mlist|, |script_mlist|, 501and |script_script_mlist| pointing to the mlists for each style. 502 503@c 504static pointer new_choice(void) 505{ /* create a choice node */ 506 return new_node(choice_node, 0); /* the |subtype| is not used */ 507} 508 509 510@ Let's consider now the previously unwritten part of |show_node_list| 511that displays the things that can only be present in mlists; this 512program illustrates how to access the data structures just defined. 513 514In the context of the following program, |p| points to a node or noad that 515should be displayed, and the current string contains the ``recursion history'' 516that leads to this point. The recursion history consists of a dot for each 517outer level in which |p| is subsidiary to some node, or in which |p| is 518subsidiary to the |nucleus| field of some noad; the dot is replaced by 519`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| 520or |supscr| or |denominator| or |numerator| fields of noads. For example, 521the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for 522|x| in the (ridiculous) formula 523`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. 524 525@c 526void display_normal_noad(pointer p); /* forward */ 527void display_fence_noad(pointer p); /* forward */ 528void display_fraction_noad(pointer p); /* forward */ 529 530void show_math_node(pointer p) 531{ 532 switch (type(p)) { 533 case style_node: 534 print_cmd_chr(math_style_cmd, subtype(p)); 535 break; 536 case choice_node: 537 tprint_esc("mathchoice"); 538 append_char('D'); 539 show_node_list(display_mlist(p)); 540 flush_char(); 541 append_char('T'); 542 show_node_list(text_mlist(p)); 543 flush_char(); 544 append_char('S'); 545 show_node_list(script_mlist(p)); 546 flush_char(); 547 append_char('s'); 548 show_node_list(script_script_mlist(p)); 549 flush_char(); 550 break; 551 case simple_noad: 552 case radical_noad: 553 case accent_noad: 554 display_normal_noad(p); 555 break; 556 case fence_noad: 557 display_fence_noad(p); 558 break; 559 case fraction_noad: 560 display_fraction_noad(p); 561 break; 562 default: 563 tprint("Unknown node type!"); 564 break; 565 } 566} 567 568 569@ Here are some simple routines used in the display of noads. 570 571@c 572static void print_fam_and_char(pointer p) 573{ /* prints family and character */ 574 tprint_esc("fam"); 575 print_int(math_fam(p)); 576 print_char(' '); 577 print(math_character(p)); 578} 579 580@ @c 581static void print_delimiter(pointer p) 582{ 583 int a; 584 if (small_fam(p) < 0) { 585 print_int(-1); /* this should never happen */ 586 } else if (small_fam(p) < 16 && large_fam(p) < 16 && 587 small_char(p) < 256 && large_char(p) < 256) { 588 /* traditional tex style */ 589 a = small_fam(p) * 256 + small_char(p); 590 a = a * 0x1000 + large_fam(p) * 256 + large_char(p); 591 print_hex(a); 592 } else if ((large_fam(p) == 0 && large_char(p) == 0) || 593 small_char(p) > 65535 || large_char(p) > 65535) { 594 /* modern xetex/luatex style */ 595 print_hex(small_fam(p)); 596 print_hex(small_char(p)); 597 } 598} 599 600 601@ The next subroutine will descend to another level of recursion when a 602subsidiary mlist needs to be displayed. The parameter |c| indicates what 603character is to become part of the recursion history. An empty mlist is 604distinguished from a missing field, because these are not equivalent 605(as explained above). 606@^recursion@> 607 608@c 609static void print_subsidiary_data(pointer p, ASCII_code c) 610{ /* display a noad field */ 611 if ((int) cur_length >= depth_threshold) { 612 if (p != null) 613 tprint(" []"); 614 } else { 615 append_char(c); /* include |c| in the recursion history */ 616 if (p != null) { 617 switch (type(p)) { 618 case math_char_node: 619 print_ln(); 620 print_current_string(); 621 print_fam_and_char(p); 622 break; 623 case sub_box_node: 624 show_node_list(math_list(p)); 625 break; 626 case sub_mlist_node: 627 if (math_list(p) == null) { 628 print_ln(); 629 print_current_string(); 630 tprint("{}"); 631 } else { 632 show_node_list(math_list(p)); 633 } 634 break; 635 } 636 } 637 flush_char(); /* remove |c| from the recursion history */ 638 } 639} 640 641@ @c 642void display_normal_noad(pointer p) 643{ 644 switch (type(p)) { 645 case simple_noad: 646 switch (subtype(p)) { 647 case ord_noad_type: 648 tprint_esc("mathord"); 649 break; 650 case op_noad_type_normal: 651 case op_noad_type_limits: 652 case op_noad_type_no_limits: 653 tprint_esc("mathop"); 654 if (subtype(p) == op_noad_type_limits) 655 tprint_esc("limits"); 656 else if (subtype(p) == op_noad_type_no_limits) 657 tprint_esc("nolimits"); 658 break; 659 case bin_noad_type: 660 tprint_esc("mathbin"); 661 break; 662 case rel_noad_type: 663 tprint_esc("mathrel"); 664 break; 665 case open_noad_type: 666 tprint_esc("mathopen"); 667 break; 668 case close_noad_type: 669 tprint_esc("mathclose"); 670 break; 671 case punct_noad_type: 672 tprint_esc("mathpunct"); 673 break; 674 case inner_noad_type: 675 tprint_esc("mathinner"); 676 break; 677 case over_noad_type: 678 tprint_esc("overline"); 679 break; 680 case under_noad_type: 681 tprint_esc("underline"); 682 break; 683 case vcenter_noad_type: 684 tprint_esc("vcenter"); 685 break; 686 default: 687 tprint("<unknown noad type!>"); 688 break; 689 } 690 break; 691 case radical_noad: 692 if (subtype(p) == 7) 693 tprint_esc("Udelimiterover"); 694 else if (subtype(p) == 6) 695 tprint_esc("Udelimiterunder"); 696 else if (subtype(p) == 5) 697 tprint_esc("Uoverdelimiter"); 698 else if (subtype(p) == 4) 699 tprint_esc("Uunderdelimiter"); 700 else if (subtype(p) == 3) 701 tprint_esc("Uroot"); 702 else 703 tprint_esc("radical"); 704 print_delimiter(left_delimiter(p)); 705 if (degree(p) != null) { 706 print_subsidiary_data(degree(p), '/'); 707 } 708 break; 709 case accent_noad: 710 if (accent_chr(p) != null) { 711 if (bot_accent_chr(p) != null) { 712 tprint_esc("Umathaccent both"); 713 } else { 714 tprint_esc("Umathaccent"); 715 } 716 } else { 717 tprint_esc("Umathaccent bottom"); 718 } 719 switch (subtype(p)) { 720 case 0: 721 if (accent_chr(p) != null) { 722 if (bot_accent_chr(p) != null) { 723 print_fam_and_char(accent_chr(p)); 724 print_fam_and_char(bot_accent_chr(p)); 725 } else { 726 print_fam_and_char(accent_chr(p)); 727 } 728 } else { 729 print_fam_and_char(bot_accent_chr(p)); 730 } 731 break; 732 case 1: 733 if (accent_chr(p) != null) { 734 tprint(" fixed "); 735 print_fam_and_char(accent_chr(p)); 736 if (bot_accent_chr(p) != null) { 737 print_fam_and_char(bot_accent_chr(p)); 738 } 739 } else { 740 confusion("display_accent_noad"); 741 } 742 break; 743 case 2: 744 if (bot_accent_chr(p) != null) { 745 if (accent_chr(p) != null) { 746 print_fam_and_char(accent_chr(p)); 747 } 748 tprint(" fixed "); 749 print_fam_and_char(bot_accent_chr(p)); 750 } else{ 751 confusion("display_accent_noad"); 752 } 753 break; 754 case 3: 755 if (accent_chr(p) != null && bot_accent_chr(p) != null) { 756 tprint(" fixed "); 757 print_fam_and_char(accent_chr(p)); 758 tprint(" fixed "); 759 print_fam_and_char(bot_accent_chr(p)); 760 } else { 761 confusion("display_accent_noad"); 762 } 763 break; 764 } 765 break; 766 } 767 print_subsidiary_data(nucleus(p), '.'); 768 print_subsidiary_data(supscr(p), '^'); 769 print_subsidiary_data(subscr(p), '_'); 770} 771 772@ @c 773void display_fence_noad(pointer p) 774{ 775 if (subtype(p) == right_noad_side) 776 tprint_esc("right"); 777 else if (subtype(p) == left_noad_side) 778 tprint_esc("left"); 779 else 780 tprint_esc("middle"); 781 print_delimiter(delimiter(p)); 782} 783 784@ @c 785void display_fraction_noad(pointer p) 786{ 787 tprint_esc("fraction, thickness "); 788 if (thickness(p) == default_code) 789 tprint("= default"); 790 else 791 print_scaled(thickness(p)); 792 if ((left_delimiter(p) != null) && 793 ((small_fam(left_delimiter(p)) != 0) || 794 (small_char(left_delimiter(p)) != 0) || 795 (large_fam(left_delimiter(p)) != 0) || 796 (large_char(left_delimiter(p)) != 0))) { 797 tprint(", left-delimiter "); 798 print_delimiter(left_delimiter(p)); 799 } 800 if ((right_delimiter(p) != null) && 801 ((small_fam(right_delimiter(p)) != 0) || 802 (small_char(right_delimiter(p)) != 0) || 803 (large_fam(right_delimiter(p)) != 0) || 804 (large_char(right_delimiter(p)) != 0))) { 805 tprint(", right-delimiter "); 806 print_delimiter(right_delimiter(p)); 807 } 808 print_subsidiary_data(numerator(p), '\\'); 809 print_subsidiary_data(denominator(p), '/'); 810} 811 812 813@ The routines that \TeX\ uses to create mlists are similar to those we have 814just seen for the generation of hlists and vlists. But it is necessary to 815make ``noads'' as well as nodes, so the reader should review the 816discussion of math mode data structures before trying to make sense out of 817the following program. 818 819Here is a little routine that needs to be done whenever a subformula 820is about to be processed. The parameter is a code like |math_group|. 821 822@c 823static void new_save_level_math(group_code c) 824{ 825 set_saved_record(0, saved_textdir, 0, text_dir_ptr); 826 text_dir_ptr = new_dir(math_direction); 827 incr(save_ptr); 828 new_save_level(c); 829 eq_word_define(int_base + body_direction_code, math_direction); 830 eq_word_define(int_base + par_direction_code, math_direction); 831 eq_word_define(int_base + text_direction_code, math_direction); 832} 833 834@ @c 835static void push_math(group_code c, int mstyle) 836{ 837 if (math_direction != text_direction) 838 dir_math_save = true; 839 push_nest(); 840 mode = -mmode; 841 incompleat_noad = null; 842 m_style = mstyle; 843 new_save_level_math(c); 844} 845 846@ @c 847static void enter_ordinary_math(void) 848{ 849 push_math(math_shift_group, text_style); 850 eq_word_define(int_base + cur_fam_code, -1); 851 if (every_math != null) 852 begin_token_list(every_math, every_math_text); 853} 854 855@ @c 856void enter_display_math(void); 857 858@ We get into math mode from horizontal mode when a `\.\$' (i.e., a 859|math_shift| character) is scanned. We must check to see whether this 860`\.\$' is immediately followed by another, in case display math mode is 861called for. 862 863@c 864void init_math(void) 865{ 866 if (cur_cmd == math_shift_cmd) { 867 get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ 868 if ((cur_cmd == math_shift_cmd) && (mode > 0)) { 869 enter_display_math(); 870 } else { 871 back_input(); 872 enter_ordinary_math(); 873 } 874 } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { 875 enter_display_math(); 876 } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { 877 enter_ordinary_math(); 878 } else { 879 you_cant(); 880 } 881} 882 883 884@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or 885`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; 886the value of |cur_chr| is placed onto |save_stack| for safe keeping. 887 888 889@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, 890so it is not necessary for the |start_eq_no| procedure to test for 891this condition. 892 893@c 894void start_eq_no(void) 895{ 896 set_saved_record(0, saved_eqno, 0, cur_chr); 897 incr(save_ptr); 898 enter_ordinary_math(); 899} 900 901@ Subformulas of math formulas cause a new level of math mode to be entered, 902on the semantic nest as well as the save stack. These subformulas arise in 903several ways: (1)~A left brace by itself indicates the beginning of a 904subformula that will be put into a box, thereby freezing its glue and 905preventing line breaks. (2)~A subscript or superscript is treated as a 906subformula if it is not a single character; the same applies to 907the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive 908initiates a subformula that will be terminated by a matching \.{\\right}. 909The group codes placed on |save_stack| in these three cases are 910|math_group|, |math_group|, and |math_left_group|, respectively. 911 912Here is the code that handles case (1); the other cases are not quite as 913trivial, so we shall consider them later. 914 915@c 916void math_left_brace(void) 917{ 918 pointer q; 919 tail_append(new_noad()); 920 q = new_node(math_char_node, 0); 921 nucleus(tail) = q; 922 back_input(); 923 (void) scan_math(nucleus(tail), m_style); 924} 925 926@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are 927opposite, then this function will return true. Discovering that fact 928is somewhat odd because it needs traversal of the |save_stack|. 929The occurance of displayed equations is weird enough that this is 930probably still better than having yet another field in the |input_stack| 931structures. 932 933None of this makes much sense if the inline direction of either one of 934\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current 935math machinery is ill suited anyway so I do not bother to test that. 936 937@c 938static boolean math_and_text_reversed_p(void) 939{ 940 int i = save_ptr - 1; 941 while (save_type(i) != level_boundary) 942 i--; 943 while (i < save_ptr) { 944 if (save_type(i) == restore_old_value && 945 save_value(i) == int_base + par_direction_code) { 946 if (textdir_opposite(math_direction, save_value(i - 1))) 947 return true; 948 } 949 i++; 950 } 951 return false; 952} 953 954 955 956 957 958@ When we enter display math mode, we need to call |line_break| to 959process the partial paragraph that has just been interrupted by the 960display. Then we can set the proper values of |display_width| and 961|display_indent| and |pre_display_size|. 962 963@c 964void enter_display_math(void) 965{ 966 scaled w; /* new or partial |pre_display_size| */ 967 scaled l; /* new |display_width| */ 968 scaled s; /* new |display_indent| */ 969 pointer p; 970 int n; /* scope of paragraph shape specification */ 971 if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ 972 (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ 973 type(tail) == whatsit_node && 974 subtype(tail) == local_par_node && vlink(tail) == null)) { 975 if (vlink(head) == tail) { 976 /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if 977 there is another display immediately following, we have to get rid 978 of that node */ 979 flush_node(tail); 980 } 981 pop_nest(); 982 w = -max_dimen; 983 } else { 984 line_break(true, math_shift_group); 985 w = actual_box_width(just_box, (2 * quad(get_cur_font()))); 986 } 987 /* now we are in vertical mode, working on the list that will contain the display */ 988 /* A displayed equation is considered to be three lines long, so we 989 calculate the length and offset of line number |prev_graf+2|. */ 990 if (par_shape_ptr == null) { 991 if ((hang_indent != 0) && 992 (((hang_after >= 0) && (prev_graf + 2 > hang_after)) || 993 (prev_graf + 1 < -hang_after))) { 994 l = hsize - abs(hang_indent); 995 if (hang_indent > 0) 996 s = hang_indent; 997 else 998 s = 0; 999 } else { 1000 l = hsize; 1001 s = 0; 1002 } 1003 } else { 1004 n = vinfo(par_shape_ptr + 1); 1005 if (prev_graf + 2 >= n) 1006 p = par_shape_ptr + 2 * n + 1; 1007 else 1008 p = par_shape_ptr + 2 * (prev_graf + 2) + 1; 1009 s = varmem[(p - 1)].cint; 1010 l = varmem[p].cint; 1011 } 1012 1013 push_math(math_shift_group, display_style); 1014 mode = mmode; 1015 eq_word_define(int_base + cur_fam_code, -1); 1016 eq_word_define(dimen_base + pre_display_size_code, w); 1017 eq_word_define(dimen_base + display_width_code, l); 1018 eq_word_define(dimen_base + display_indent_code, s); 1019 eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); 1020 if (every_display != null) 1021 begin_token_list(every_display, every_display_text); 1022 if (nest_ptr == 1) { 1023 if (!output_active) 1024 lua_node_filter_s(buildpage_filter_callback,lua_key_index(before_display)); 1025 build_page(); 1026 } 1027} 1028 1029@ The next routine parses all variations of a delimiter code. The |extcode| 1030 tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the 1031 |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). 1032 (the class is passed on for conversion to \.{\\mathchar}). 1033 1034@c 1035#define fam_in_range ((cur_fam>=0)&&(cur_fam<256)) 1036 1037static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) 1038{ 1039 const char *hlp[] = { 1040 "I'm going to use 0 instead of that illegal code value.", 1041 NULL 1042 }; 1043 delcodeval d; 1044 int mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; 1045 mcls = 0; 1046 if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ 1047 scan_int(); 1048 /* "MFCCFCC or "FCCFCC */ 1049 if (doclass) { 1050 mcls = (cur_val / 0x1000000); 1051 cur_val = (cur_val & 0xFFFFFF); 1052 } 1053 if (cur_val > 0xFFFFFF) { 1054 tex_error("Invalid delimiter code", hlp); 1055 cur_val = 0; 1056 } 1057 msfam = (cur_val / 0x100000); 1058 mschr = (cur_val % 0x100000) / 0x1000; 1059 mlfam = (cur_val & 0xFFF) / 0x100; 1060 mlchr = (cur_val % 0x100); 1061 } else if (extcode == xetex_mathcode) { /* \.{\\Udelcode} */ 1062 /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ 1063 if (doclass) { 1064 scan_int(); 1065 mcls = cur_val; 1066 } 1067 scan_int(); 1068 msfam = cur_val; 1069 scan_char_num(); 1070 mschr = cur_val; 1071 if (msfam < 0 || msfam > 255) { 1072 tex_error("Invalid delimiter code", hlp); 1073 msfam = 0; 1074 mschr = 0; 1075 } 1076 mlfam = 0; 1077 mlchr = 0; 1078 } else if (extcode == xetexnum_mathcode) { /* \.{\\Udelcodenum} */ 1079 /* "FF<21bits> */ 1080 /* the largest numeric value is $2^29-1$, but 1081 the top of bit 21 can't be used as it contains invalid USV's 1082 */ 1083 if (doclass) { /* such a primitive doesn't exist */ 1084 confusion("xetexnum_mathcode"); 1085 } 1086 scan_int(); 1087 msfam = (cur_val / 0x200000); 1088 mschr = cur_val & 0x1FFFFF; 1089 if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { 1090 tex_error("Invalid delimiter code", hlp); 1091 msfam = 0; 1092 mschr = 0; 1093 } 1094 mlfam = 0; 1095 mlchr = 0; 1096 } else { 1097 /* something's gone wrong */ 1098 confusion("unknown_extcode"); 1099 } 1100 d.origin_value = extcode; 1101 d.class_value = mcls; 1102 d.small_family_value = msfam; 1103 d.small_character_value = mschr; 1104 d.large_family_value = mlfam; 1105 d.large_character_value = mlchr; 1106 return d; 1107} 1108 1109@ @c 1110void scan_extdef_del_code(int level, int extcode) 1111{ 1112 delcodeval d; 1113 int p; 1114 scan_char_num(); 1115 p = cur_val; 1116 scan_optional_equals(); 1117 d = do_scan_extdef_del_code(extcode, false); 1118 set_del_code(p, extcode, d.small_family_value, d.small_character_value, 1119 d.large_family_value, d.large_character_value, 1120 (quarterword) (level)); 1121} 1122 1123@ @c 1124mathcodeval scan_mathchar(int extcode) 1125{ 1126 char errstr[255] = { 0 }; 1127 const char *hlp[] = { 1128 "I'm going to use 0 instead of that illegal code value.", 1129 NULL 1130 }; 1131 mathcodeval d; 1132 int mcls = 0, mfam = 0, mchr = 0; 1133 if (extcode == tex_mathcode) { /* \.{\\mathcode} */ 1134 /* "TFCC */ 1135 scan_int(); 1136 if (cur_val > 0x8000) { 1137 tex_error("Invalid math code", hlp); 1138 cur_val = 0; 1139 } 1140 if (cur_val < 0) { 1141 snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); 1142 tex_error(errstr, hlp); 1143 cur_val = 0; 1144 } 1145 mcls = (cur_val / 0x1000); 1146 mfam = ((cur_val % 0x1000) / 0x100); 1147 mchr = (cur_val % 0x100); 1148 } else if (extcode == xetex_mathcode) { 1149 /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ 1150 scan_int(); 1151 mcls = cur_val; 1152 scan_int(); 1153 mfam = cur_val; 1154 scan_char_num(); 1155 mchr = cur_val; 1156 if (mcls < 0 || mcls > 7 || mfam > 255) { 1157 tex_error("Invalid math code", hlp); 1158 mchr = 0; 1159 mfam = 0; 1160 mcls = 0; 1161 } 1162 } else if (extcode == xetexnum_mathcode) { 1163 /* "FFT<21bits> */ 1164 /* the largest numeric value is $2^32-1$, but 1165 the top of bit 21 can't be used as it contains invalid USV's 1166 */ 1167 /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */ 1168 scan_int(); 1169 mfam = (cur_val / 0x200000) & 0x7FF; 1170 mcls = mfam % 0x08; 1171 mfam = mfam / 0x08; 1172 mchr = cur_val & 0x1FFFFF; 1173 if (mchr > 0x10FFFF) { 1174 tex_error("Invalid math code", hlp); 1175 mcls = 0; 1176 mfam = 0; 1177 mchr = 0; 1178 } 1179 } else { 1180 /* something's gone wrong */ 1181 confusion("unknown_extcode"); 1182 } 1183 d.class_value = mcls; 1184 d.family_value = mfam; 1185 d.origin_value = extcode; 1186 d.character_value = mchr; 1187 return d; 1188} 1189 1190@ @c 1191void scan_extdef_math_code(int level, int extcode) 1192{ 1193 mathcodeval d; 1194 int p; 1195 scan_char_num(); 1196 p = cur_val; 1197 scan_optional_equals(); 1198 d = scan_mathchar(extcode); 1199 set_math_code(p, extcode, d.class_value, 1200 d.family_value, d.character_value, (quarterword) (level)); 1201} 1202 1203 1204@ this reads in a delcode when actually a mathcode is needed 1205@c 1206mathcodeval scan_delimiter_as_mathchar(int extcode) 1207{ 1208 delcodeval dval; 1209 mathcodeval mval; 1210 dval = do_scan_extdef_del_code(extcode, true); 1211 mval.origin_value = 0; 1212 mval.class_value = dval.class_value; 1213 mval.family_value = dval.small_family_value; 1214 mval.character_value = dval.small_character_value; 1215 return mval; 1216} 1217 1218@ this has to match the inverse routine in the pascal code 1219 where the \.{\\Umathchardef} is executed 1220 1221@c 1222mathcodeval mathchar_from_integer(int value, int extcode) 1223{ 1224 mathcodeval mval; 1225 mval.origin_value = extcode; 1226 if (extcode == tex_mathcode) { 1227 mval.class_value = (value / 0x1000); 1228 mval.family_value = ((value % 0x1000) / 0x100); 1229 mval.character_value = (value % 0x100); 1230 } else { /* some xetexended xetex thing */ 1231 int mfam = (value / 0x200000) & 0x7FF; 1232 mval.class_value = mfam % 0x08; 1233 mval.family_value = mfam / 0x08; 1234 mval.character_value = value & 0x1FFFFF; 1235 } 1236 return mval; 1237} 1238 1239@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad 1240are broken down into subfields called |type| and either |math_list| or 1241|(math_fam,math_character)|. The job of |scan_math| is to figure out 1242what to place in one of these principal fields; it looks at the 1243subformula that comes next in the input, and places an encoding of 1244that subformula into a given word of |mem|. 1245 1246@c 1247#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) 1248 1249 1250int scan_math(pointer p, int mstyle) 1251{ 1252 /* label restart,reswitch,exit; */ 1253 mathcodeval mval = { 0, 0, 0, 0 }; 1254 assert(p != null); 1255 RESTART: 1256 get_next_nb_nr(); 1257 RESWITCH: 1258 switch (cur_cmd) { 1259 case letter_cmd: 1260 case other_char_cmd: 1261 case char_given_cmd: 1262 mval = get_math_code(cur_chr); 1263 if (mval.class_value == 8) { 1264 /* An active character that is an |outer_call| is allowed here */ 1265 cur_cs = active_to_cs(cur_chr, true); 1266 cur_cmd = eq_type(cur_cs); 1267 cur_chr = equiv(cur_cs); 1268 x_token(); 1269 back_input(); 1270 goto RESTART; 1271 } 1272 break; 1273 case char_num_cmd: 1274 scan_char_num(); 1275 cur_chr = cur_val; 1276 cur_cmd = char_given_cmd; 1277 goto RESWITCH; 1278 break; 1279 case math_char_num_cmd: 1280 if (cur_chr == 0) 1281 mval = scan_mathchar(tex_mathcode); 1282 else if (cur_chr == 1) 1283 mval = scan_mathchar(xetex_mathcode); 1284 else if (cur_chr == 2) 1285 mval = scan_mathchar(xetexnum_mathcode); 1286 else 1287 confusion("scan_math"); 1288 break; 1289 case math_given_cmd: 1290 mval = mathchar_from_integer(cur_chr, tex_mathcode); 1291 break; 1292 case xmath_given_cmd: 1293 mval = mathchar_from_integer(cur_chr, xetex_mathcode); 1294 break; 1295 case delim_num_cmd: 1296 if (cur_chr == 0) 1297 mval = scan_delimiter_as_mathchar(tex_mathcode); 1298 else if (cur_chr == 1) 1299 mval = scan_delimiter_as_mathchar(xetex_mathcode); 1300 else 1301 confusion("scan_math"); 1302 break; 1303 default: 1304 /* The pointer |p| is placed on |save_stack| while a complex subformula 1305 is being scanned. */ 1306 back_input(); 1307 scan_left_brace(); 1308 set_saved_record(0, saved_math, 0, p); 1309 incr(save_ptr); 1310 push_math(math_group, mstyle); 1311 return 1; 1312 } 1313 type(p) = math_char_node; 1314 math_character(p) = mval.character_value; 1315 if ((mval.class_value == var_code) && fam_in_range) 1316 math_fam(p) = cur_fam; 1317 else 1318 math_fam(p) = mval.family_value; 1319 return 0; 1320} 1321 1322 1323 1324@ The |set_math_char| procedure creates a new noad appropriate to a given 1325math code, and appends it to the current mlist. However, if the math code 1326is sufficiently large, the |cur_chr| is treated as an active character and 1327nothing is appended. 1328 1329@c 1330void set_math_char(mathcodeval mval) 1331{ 1332 pointer p; /* the new noad */ 1333 if (mval.class_value == 8) { 1334 /* An active character that is an |outer_call| is allowed here */ 1335 cur_cs = active_to_cs(cur_chr, true); 1336 cur_cmd = eq_type(cur_cs); 1337 cur_chr = equiv(cur_cs); 1338 x_token(); 1339 back_input(); 1340 } else { 1341 pointer q; 1342 p = new_noad(); 1343 q = new_node(math_char_node, 0); 1344 nucleus(p) = q; 1345 math_character(nucleus(p)) = mval.character_value; 1346 math_fam(nucleus(p)) = mval.family_value; 1347 if (mval.class_value == var_code) { 1348 if (fam_in_range) 1349 math_fam(nucleus(p)) = cur_fam; 1350 subtype(p) = ord_noad_type; 1351 } else { 1352 switch (mval.class_value) { 1353 /* *INDENT-OFF* */ 1354 case 0: subtype(p) = ord_noad_type; break; 1355 case 1: subtype(p) = op_noad_type_normal; break; 1356 case 2: subtype(p) = bin_noad_type; break; 1357 case 3: subtype(p) = rel_noad_type; break; 1358 case 4: subtype(p) = open_noad_type; break; 1359 case 5: subtype(p) = close_noad_type; break; 1360 case 6: subtype(p) = punct_noad_type; break; 1361 /* *INDENT-ON* */ 1362 } 1363 } 1364 vlink(tail) = p; 1365 tail = p; 1366 } 1367} 1368 1369 1370 1371@ The |math_char_in_text| procedure creates a new node representing a math char 1372in text code, and appends it to the current list. However, if the math code 1373is sufficiently large, the |cur_chr| is treated as an active character and 1374nothing is appended. 1375 1376@c 1377void math_char_in_text(mathcodeval mval) 1378{ 1379 pointer p; /* the new node */ 1380 if (mval.class_value == 8) { 1381 /* An active character that is an |outer_call| is allowed here */ 1382 cur_cs = active_to_cs(cur_chr, true); 1383 cur_cmd = eq_type(cur_cs); 1384 cur_chr = equiv(cur_cs); 1385 x_token(); 1386 back_input(); 1387 } else { 1388 p = new_char(fam_fnt(mval.family_value, text_size), 1389 mval.character_value); 1390 vlink(tail) = p; 1391 tail = p; 1392 } 1393} 1394 1395 1396@ @c 1397void math_math_comp(void) 1398{ 1399 pointer q; 1400 tail_append(new_noad()); 1401 subtype(tail) = (quarterword) cur_chr; 1402 q = new_node(math_char_node, 0); 1403 nucleus(tail) = q; 1404 if (cur_chr == over_noad_type) 1405 (void) scan_math(nucleus(tail), cramped_style(m_style)); 1406 else 1407 (void) scan_math(nucleus(tail), m_style); 1408} 1409 1410 1411@ @c 1412void math_limit_switch(void) 1413{ 1414 const char *hlp[] = { 1415 "I'm ignoring this misplaced \\limits or \\nolimits command.", 1416 NULL 1417 }; 1418 if (head != tail) { 1419 if (type(tail) == simple_noad && 1420 (subtype(tail) == op_noad_type_normal || 1421 subtype(tail) == op_noad_type_limits || 1422 subtype(tail) == op_noad_type_no_limits)) { 1423 subtype(tail) = (quarterword) cur_chr; 1424 return; 1425 } 1426 } 1427 tex_error("Limit controls must follow a math operator", hlp); 1428} 1429 1430 1431@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. 1432The first parameter of this procedure is the |mem| address where the 1433delimiter is to be placed; the second tells if this delimiter follows 1434\.{\\radical} or not. 1435 1436@c 1437static void scan_delimiter(pointer p, int r) 1438{ 1439 delcodeval dval = { 0, 0, 0, 0, 0, 0 }; 1440 if (r == tex_mathcode) { /* \.{\\radical} */ 1441 dval = do_scan_extdef_del_code(tex_mathcode, true); 1442 } else if (r == xetex_mathcode) { /* \.{\\Uradical} */ 1443 dval = do_scan_extdef_del_code(xetex_mathcode, false); 1444 } else if (r == no_mathcode) { 1445 get_next_nb_nr(); 1446 switch (cur_cmd) { 1447 case letter_cmd: 1448 case other_char_cmd: 1449 dval = get_del_code(cur_chr); 1450 break; 1451 case delim_num_cmd: 1452 if (cur_chr == 0) /* \.{\\delimiter} */ 1453 dval = do_scan_extdef_del_code(tex_mathcode, true); 1454 else if (cur_chr == 1) /* \.{\\Udelimiter} */ 1455 dval = do_scan_extdef_del_code(xetex_mathcode, true); 1456 else 1457 confusion("scan_delimiter1"); 1458 break; 1459 default: 1460 dval.small_family_value = -1; 1461 break; 1462 } 1463 } else { 1464 confusion("scan_delimiter2"); 1465 } 1466 if (p == null) 1467 return; 1468 if (dval.small_family_value < 0) { 1469 const char *hlp[] = { 1470 "I was expecting to see something like `(' or `\\{' or", 1471 "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", 1472 "should probably delete the `{' by typing `1' now, so that", 1473 "braces don't get unbalanced. Otherwise just proceed", 1474 "Acceptable delimiters are characters whose \\delcode is", 1475 "nonnegative, or you can use `\\delimiter <delimiter code>'.", 1476 NULL 1477 }; 1478 back_error("Missing delimiter (. inserted)", hlp); 1479 small_fam(p) = 0; 1480 small_char(p) = 0; 1481 large_fam(p) = 0; 1482 large_char(p) = 0; 1483 } else { 1484 small_fam(p) = dval.small_family_value; 1485 small_char(p) = dval.small_character_value; 1486 large_fam(p) = dval.large_family_value; 1487 large_char(p) = dval.large_character_value; 1488 } 1489 return; 1490} 1491 1492 1493@ @c 1494void math_radical(void) 1495{ 1496 halfword q; 1497 int chr_code = cur_chr; 1498 tail_append(new_node(radical_noad, chr_code)); 1499 q = new_node(delim_node, 0); 1500 left_delimiter(tail) = q; 1501 if (chr_code == 0) /* \.{\\radical} */ 1502 scan_delimiter(left_delimiter(tail), tex_mathcode); 1503 else if (chr_code == 1) /* \.{\\Uradical} */ 1504 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1505 else if (chr_code == 2) /* \.{\\Uroot} */ 1506 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1507 else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ 1508 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1509 else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ 1510 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1511 else if (chr_code == 5) /* \.{\\Udelimiterunder} */ 1512 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1513 else if (chr_code == 6) /* \.{\\Udelimiterover} */ 1514 scan_delimiter(left_delimiter(tail), xetex_mathcode); 1515 else 1516 confusion("math_radical"); 1517 if (chr_code == 2) { 1518 /* the trick with the |vlink(q)| is used by |scan_math| 1519 to decide whether it needs to go on */ 1520 q = new_node(math_char_node, 0); 1521 vlink(q) = tail; 1522 degree(tail) = q; 1523 if (!scan_math(degree(tail), sup_sup_style(m_style))) { 1524 vlink(degree(tail)) = null; 1525 q = new_node(math_char_node, 0); 1526 nucleus(tail) = q; 1527 (void) scan_math(nucleus(tail), cramped_style(m_style)); 1528 } 1529 } else { 1530 q = new_node(math_char_node, 0); 1531 nucleus(tail) = q; 1532 (void) scan_math(nucleus(tail), cramped_style(m_style)); 1533 } 1534} 1535 1536@ @c 1537void math_ac(void) 1538{ 1539 halfword q; 1540 mathcodeval t = { 0, 0, 0, 0 }, b = { 1541 0, 0, 0, 0}; 1542 if (cur_cmd == accent_cmd) { 1543 const char *hlp[] = { 1544 "I'm changing \\accent to \\mathaccent here; wish me luck.", 1545 "(Accents are not the same in formulas as they are in text.)", 1546 NULL 1547 }; 1548 tex_error("Please use \\mathaccent for accents in math mode", hlp); 1549 } 1550 tail_append(new_node(accent_noad, 0)); 1551 if (cur_chr == 0) { /* \.{\\mathaccent} */ 1552 t = scan_mathchar(tex_mathcode); 1553 } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ 1554 if (scan_keyword("fixed")) { 1555 subtype(tail) = 1; 1556 t = scan_mathchar(xetex_mathcode); 1557 } else if (scan_keyword("both")) { 1558 if (scan_keyword("fixed")) { 1559 subtype(tail) = 1; 1560 } 1561 t = scan_mathchar(xetex_mathcode); 1562 if (scan_keyword("fixed")) { 1563 subtype(tail) += 2; 1564 } 1565 b = scan_mathchar(xetex_mathcode); 1566 } else if (scan_keyword("bottom")) { 1567 if (scan_keyword("fixed")) { 1568 subtype(tail) = 2; 1569 } 1570 b = scan_mathchar(xetex_mathcode); 1571 } else { 1572 t = scan_mathchar(xetex_mathcode); 1573 } 1574 } else { 1575 confusion("math_ac"); 1576 } 1577 if (!(t.character_value == 0 && t.family_value == 0)) { 1578 q = new_node(math_char_node, 0); 1579 accent_chr(tail) = q; 1580 math_character(accent_chr(tail)) = t.character_value; 1581 if ((t.class_value == var_code) && fam_in_range) 1582 math_fam(accent_chr(tail)) = cur_fam; 1583 else 1584 math_fam(accent_chr(tail)) = t.family_value; 1585 } 1586 if (!(b.character_value == 0 && b.family_value == 0)) { 1587 q = new_node(math_char_node, 0); 1588 bot_accent_chr(tail) = q; 1589 math_character(bot_accent_chr(tail)) = b.character_value; 1590 if ((b.class_value == var_code) && fam_in_range) 1591 math_fam(bot_accent_chr(tail)) = cur_fam; 1592 else 1593 math_fam(bot_accent_chr(tail)) = b.family_value; 1594 } 1595 q = new_node(math_char_node, 0); 1596 nucleus(tail) = q; 1597 (void) scan_math(nucleus(tail), cramped_style(m_style)); 1598} 1599 1600@ @c 1601pointer math_vcenter_group(pointer p) 1602{ 1603 pointer q, r; 1604 q = new_noad(); 1605 subtype(q) = vcenter_noad_type; 1606 r = new_node(sub_box_node, 0); 1607 nucleus(q) = r; 1608 math_list(nucleus(q)) = p; 1609 return q; 1610} 1611 1612 1613@ The routine that scans the four mlists of a \.{\\mathchoice} is very 1614much like the routine that builds discretionary nodes. 1615 1616@c 1617void append_choices(void) 1618{ 1619 tail_append(new_choice()); 1620 incr(save_ptr); 1621 set_saved_record(-1, saved_choices, 0, 0); 1622 push_math(math_choice_group, display_style); 1623 scan_left_brace(); 1624} 1625 1626@ @c 1627void build_choices(void) 1628{ 1629 pointer p; /* the current mlist */ 1630 int prev_style; 1631 prev_style = m_style; 1632 unsave_math(); 1633 p = fin_mlist(null); 1634 assert(saved_type(-1) == saved_choices); 1635 switch (saved_value(-1)) { 1636 case 0: 1637 display_mlist(tail) = p; 1638 break; 1639 case 1: 1640 text_mlist(tail) = p; 1641 break; 1642 case 2: 1643 script_mlist(tail) = p; 1644 break; 1645 case 3: 1646 script_script_mlist(tail) = p; 1647 decr(save_ptr); 1648 return; 1649 break; 1650 } /* there are no other cases */ 1651 set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); 1652 push_math(math_choice_group, (prev_style + 2)); 1653 scan_left_brace(); 1654} 1655 1656 1657@ Subscripts and superscripts are attached to the previous nucleus by the 1658action procedure called |sub_sup|. 1659 1660@c 1661void sub_sup(void) 1662{ 1663 pointer q; 1664 if (tail == head || (!scripts_allowed(tail))) { 1665 tail_append(new_noad()); 1666 q = new_node(sub_mlist_node, 0); 1667 nucleus(tail) = q; 1668 } 1669 if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ 1670 if (supscr(tail) != null) { 1671 const char *hlp[] = { 1672 "I treat `x^1^2' essentially like `x^1{}^2'.", NULL 1673 }; 1674 tail_append(new_noad()); 1675 q = new_node(sub_mlist_node, 0); 1676 nucleus(tail) = q; 1677 tex_error("Double superscript", hlp); 1678 } 1679 q = new_node(math_char_node, 0); 1680 supscr(tail) = q; 1681 (void) scan_math(supscr(tail), sup_style(m_style)); 1682 } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { 1683 if (subscr(tail) != null) { 1684 const char *hlp[] = { 1685 "I treat `x_1_2' essentially like `x_1{}_2'.", NULL 1686 }; 1687 tail_append(new_noad()); 1688 q = new_node(sub_mlist_node, 0); 1689 nucleus(tail) = q; 1690 tex_error("Double subscript", hlp); 1691 } 1692 q = new_node(math_char_node, 0); 1693 subscr(tail) = q; 1694 (void) scan_math(subscr(tail), sub_style(m_style)); 1695 } 1696} 1697 1698 1699@ An operation like `\.{\\over}' causes the current mlist to go into a 1700state of suspended animation: |incompleat_noad| points to a |fraction_noad| 1701that contains the mlist-so-far as its numerator, while the denominator 1702is yet to come. Finally when the mlist is finished, the denominator will 1703go into the incompleat fraction noad, and that noad will become the 1704whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' 1705delimiters. 1706 1707@c 1708void math_fraction(void) 1709{ 1710 halfword c; /* the type of generalized fraction we are scanning */ 1711 pointer q; 1712 c = cur_chr; 1713 if (incompleat_noad != null) { 1714 const char *hlp[] = { 1715 "I'm ignoring this fraction specification, since I don't", 1716 "know whether a construction like `x \\over y \\over z'", 1717 "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", 1718 NULL 1719 }; 1720 if (c >= delimited_code) { 1721 scan_delimiter(null, no_mathcode); 1722 scan_delimiter(null, no_mathcode); 1723 } 1724 if ((c % delimited_code) == above_code) 1725 scan_normal_dimen(); 1726 tex_error("Ambiguous; you need another { and }", hlp); 1727 } else { 1728 incompleat_noad = new_node(fraction_noad, 0); 1729 numerator(incompleat_noad) = new_node(sub_mlist_node, 0); 1730 math_list(numerator(incompleat_noad)) = vlink(head); 1731 vlink(head) = null; 1732 tail = head; 1733 m_style = cramped_style(m_style); 1734 1735 if (c >= delimited_code) { 1736 q = new_node(delim_node, 0); 1737 left_delimiter(incompleat_noad) = q; 1738 q = new_node(delim_node, 0); 1739 right_delimiter(incompleat_noad) = q; 1740 scan_delimiter(left_delimiter(incompleat_noad), no_mathcode); 1741 scan_delimiter(right_delimiter(incompleat_noad), no_mathcode); 1742 } 1743 switch (c % delimited_code) { 1744 case above_code: 1745 scan_normal_dimen(); 1746 thickness(incompleat_noad) = cur_val; 1747 break; 1748 case over_code: 1749 thickness(incompleat_noad) = default_code; 1750 break; 1751 case atop_code: 1752 thickness(incompleat_noad) = 0; 1753 break; 1754 } /* there are no other cases */ 1755 } 1756} 1757 1758 1759 1760@ At the end of a math formula or subformula, the |fin_mlist| routine is 1761called upon to return a pointer to the newly completed mlist, and to 1762pop the nest back to the enclosing semantic level. The parameter to 1763|fin_mlist|, if not null, points to a |fence_noad| that ends the 1764current mlist; this |fence_noad| has not yet been appended. 1765 1766@c 1767pointer fin_mlist(pointer p) 1768{ 1769 pointer q; /* the mlist to return */ 1770 if (incompleat_noad != null) { 1771 if (denominator(incompleat_noad) != null) { 1772 type(denominator(incompleat_noad)) = sub_mlist_node; 1773 } else { 1774 q = new_node(sub_mlist_node, 0); 1775 denominator(incompleat_noad) = q; 1776 } 1777 math_list(denominator(incompleat_noad)) = vlink(head); 1778 if (p == null) { 1779 q = incompleat_noad; 1780 } else { 1781 q = math_list(numerator(incompleat_noad)); 1782 if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) 1783 || (delim_ptr == null)) 1784 confusion("right"); /* this can't happen */ 1785 math_list(numerator(incompleat_noad)) = vlink(delim_ptr); 1786 vlink(delim_ptr) = incompleat_noad; 1787 vlink(incompleat_noad) = p; 1788 } 1789 } else { 1790 vlink(tail) = p; 1791 q = vlink(head); 1792 } 1793 pop_nest(); 1794 return q; 1795} 1796 1797 1798@ Now at last we're ready to see what happens when a right brace occurs 1799in a math formula. Two special cases are simplified here: Braces are effectively 1800removed when they surround a single Ord without sub/superscripts, or when they 1801surround an accent that is the nucleus of an Ord atom. 1802 1803@c 1804void close_math_group(pointer p) 1805{ 1806 int old_style = m_style; 1807 unsave_math(); 1808 1809 decr(save_ptr); 1810 assert(saved_type(0) == saved_math); 1811 type(saved_value(0)) = sub_mlist_node; 1812 p = fin_mlist(null); 1813 math_list(saved_value(0)) = p; 1814 if (p != null) { 1815 if (vlink(p) == null) { 1816 if (type(p) == simple_noad && subtype(p) == ord_noad_type) { 1817 if (subscr(p) == null && supscr(p) == null) { 1818 type(saved_value(0)) = type(nucleus(p)); 1819 if (type(nucleus(p)) == math_char_node) { 1820 math_fam(saved_value(0)) = math_fam(nucleus(p)); 1821 math_character(saved_value(0)) = 1822 math_character(nucleus(p)); 1823 } else { 1824 math_list(saved_value(0)) = math_list(nucleus(p)); 1825 math_list(nucleus(p)) = null; 1826 } 1827 delete_attribute_ref(node_attr(saved_value(0))); 1828 node_attr(saved_value(0)) = node_attr(nucleus(p)); 1829 node_attr(nucleus(p)) = null; 1830 flush_node(p); 1831 } 1832 } else if (type(p) == accent_noad) { 1833 if (saved_value(0) == nucleus(tail)) { 1834 if (type(tail) == simple_noad 1835 && subtype(tail) == ord_noad_type) { 1836 pointer q = head; 1837 while (vlink(q) != tail) 1838 q = vlink(q); 1839 vlink(q) = p; 1840 nucleus(tail) = null; 1841 subscr(tail) = null; 1842 supscr(tail) = null; 1843 delete_attribute_ref(node_attr(p)); 1844 node_attr(p) = node_attr(tail); 1845 node_attr(tail) = null; 1846 flush_node(tail); 1847 tail = p; 1848 } 1849 } 1850 } 1851 } 1852 } 1853 if (vlink(saved_value(0)) > 0) { 1854 pointer q; 1855 q = new_node(math_char_node, 0); 1856 nucleus(vlink(saved_value(0))) = q; 1857 vlink(saved_value(0)) = null; 1858 saved_value(0) = q; 1859 (void) scan_math(saved_value(0), old_style); 1860 /* restart */ 1861 } 1862} 1863 1864 1865@ We have dealt with all constructions of math mode except `\.{\\left}' and 1866`\.{\\right}', so the picture is completed by the following sections of 1867the program. The |middle| feature of eTeX allows one ore several \.{\\middle} 1868delimiters to appear between \.{\\left} and \.{\\right}. 1869 1870@c 1871void math_left_right(void) 1872{ 1873 halfword t; /* |left_noad_side| .. |right_noad_side| */ 1874 pointer p; /* new noad */ 1875 pointer q; /* resulting mlist */ 1876 pointer r; /* temporary */ 1877 t = cur_chr; 1878 if ((t != left_noad_side) && (cur_group != math_left_group)) { 1879 if (cur_group == math_shift_group) { 1880 scan_delimiter(null, no_mathcode); 1881 if (t == middle_noad_side) { 1882 const char *hlp[] = { 1883 "I'm ignoring a \\middle that had no matching \\left.", 1884 NULL 1885 }; 1886 tex_error("Extra \\middle", hlp); 1887 } else { 1888 const char *hlp[] = { 1889 "I'm ignoring a \\right that had no matching \\left.", 1890 NULL 1891 }; 1892 tex_error("Extra \\right", hlp); 1893 } 1894 } else { 1895 off_save(); 1896 } 1897 } else { 1898 p = new_noad(); 1899 type(p) = fence_noad; 1900 subtype(p) = (quarterword) t; 1901 r = new_node(delim_node, 0); 1902 delimiter(p) = r; 1903 scan_delimiter(delimiter(p), no_mathcode); 1904 if (t == left_noad_side) { 1905 q = p; 1906 } else { 1907 q = fin_mlist(p); 1908 unsave_math(); 1909 } 1910 if (t != right_noad_side) { 1911 push_math(math_left_group, m_style); 1912 vlink(head) = q; 1913 tail = p; 1914 delim_ptr = p; 1915 } else { 1916 tail_append(new_noad()); 1917 subtype(tail) = inner_noad_type; 1918 r = new_node(sub_mlist_node, 0); 1919 nucleus(tail) = r; 1920 math_list(nucleus(tail)) = q; 1921 } 1922 } 1923} 1924 1925 1926@ \TeX\ gets to the following part of the program when 1927the first `\.\$' ending a display has been scanned. 1928 1929@c 1930static void check_second_math_shift(void) 1931{ 1932 get_x_token(); 1933 if (cur_cmd != math_shift_cmd) { 1934 const char *hlp[] = { 1935 "The `$' that I just saw supposedly matches a previous `$$'.", 1936 "So I shall assume that you typed `$$' both times.", 1937 NULL 1938 }; 1939 back_error("Display math should end with $$", hlp); 1940 } 1941} 1942 1943static void check_display_math_end(void) 1944{ 1945 if (cur_chr != cramped_display_style) { 1946 const char *hlp[] = { 1947 "I shall assume that you typed that.", 1948 NULL 1949 }; 1950 tex_error("Display math should end with \\Ustopdisplaymath", hlp); 1951 } 1952} 1953 1954static void check_inline_math_end(void) 1955{ 1956 if (cur_chr != cramped_text_style) { 1957 const char *hlp[] = { 1958 "I shall assume that you typed that.", 1959 NULL 1960 }; 1961 tex_error("Inline math should end with \\Ustopmath", hlp); 1962 } 1963} 1964 1965@ @c 1966static void resume_after_display(void) 1967{ 1968 if (cur_group != math_shift_group) 1969 confusion("display"); 1970 unsave_math(); 1971 prev_graf = prev_graf + 3; 1972 push_nest(); 1973 mode = hmode; 1974 space_factor = 1000; 1975 tail_append(make_local_par_node()); /* this needs to be intercepted in 1976 the display math start ! */ 1977 get_x_token(); 1978 if (cur_cmd != spacer_cmd) 1979 back_input(); 1980 if (nest_ptr == 1) { 1981 lua_node_filter_s(buildpage_filter_callback,lua_key_index(after_display)); 1982 build_page(); 1983 } 1984} 1985 1986 1987@ The fussiest part of math mode processing occurs when a displayed formula is 1988being centered and placed with an optional equation number. 1989 1990 1991At this time we are in vertical mode (or internal vertical mode). 1992 1993 |p| points to the mlist for the formula. 1994 |a| is either |null| or it points to a box containing the equation number. 1995 |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). 1996 1997 1998@c 1999static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) 2000{ 2001 pointer eq_box; /* box containing the equation */ 2002 scaled eq_w; /* width of the equation */ 2003 scaled line_w; /* width of the line */ 2004 scaled eqno_w; /* width of equation number */ 2005 scaled eqno_w2; /* width of equation number plus space to separate from equation */ 2006 scaled line_s; /* move the line right this much */ 2007 scaled d; /* displacement of equation in the line */ 2008 small_number g1, g2; /* glue parameter codes for before and after */ 2009 pointer r,s; /* kern nodes used to position the display */ 2010 pointer t; /* tail of adjustment list */ 2011 pointer pre_t; /* tail of pre-adjustment list */ 2012 boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ 2013 swap_dir = (int_par(pre_display_direction_code) < 0 ? true : false ); 2014 if (eqno_box != null && swap_dir) 2015 l = !l; 2016 2017 adjust_tail = adjust_head; 2018 pre_adjust_tail = pre_adjust_head; 2019 eq_box = hpack(p, 0, additional, -1); 2020 p = list_ptr(eq_box); 2021 t = adjust_tail; 2022 adjust_tail = null; 2023 pre_t = pre_adjust_tail; 2024 pre_adjust_tail = null; 2025 eq_w = width(eq_box); 2026 line_w = display_width; 2027 line_s = display_indent; 2028 if (eqno_box == null) { 2029 eqno_w = 0; 2030 eqno_w2 = 0; 2031 } else { 2032 eqno_w = width(eqno_box); 2033 eqno_w2 = eqno_w + get_math_quad(text_size); 2034 } 2035 if (eq_w + eqno_w2 > line_w) { 2036 /* The user can force the equation number to go on a separate line 2037 by causing its width to be zero. */ 2038 if ((eqno_w != 0) 2039 && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) 2040 || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) 2041 || (total_shrink[fill] != 0) 2042 || (total_shrink[filll] != 0))) { 2043 list_ptr(eq_box) = null; 2044 flush_node(eq_box); 2045 eq_box = hpack(p, line_w - eqno_w2, exactly, -1); 2046 } else { 2047 eqno_w = 0; 2048 if (eq_w > line_w) { 2049 list_ptr(eq_box) = null; 2050 flush_node(eq_box); 2051 eq_box = hpack(p, line_w, exactly, -1); 2052 } 2053 } 2054 eq_w = width(eq_box); 2055 } 2056 /* We try first to center the display without regard to the existence of 2057 the equation number. If that would make it too close (where ``too close'' 2058 means that the space between display and equation number is less than the 2059 width of the equation number), we either center it in the remaining space 2060 or move it as far from the equation number as possible. The latter alternative 2061 is taken only if the display begins with glue, since we assume that the 2062 user put glue there to control the spacing precisely. 2063 */ 2064 d = half(line_w - eq_w); 2065 if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ 2066 d = half(line_w - eq_w - eqno_w); 2067 if (p != null) 2068 if (!is_char_node(p)) 2069 if (type(p) == glue_node) 2070 d = 0; 2071 } 2072 2073 tail_append(new_penalty(int_par(pre_display_penalty_code))); 2074 if ((d + line_s <= pre_display_size) || l) { /* not enough clearance */ 2075 g1 = above_display_skip_code; 2076 g2 = below_display_skip_code; 2077 } else { 2078 g1 = above_display_short_skip_code; 2079 g2 = below_display_short_skip_code; 2080 } 2081 2082 /* If the equation number is set on a line by itself, either before or 2083 after the formula, we append an infinite penalty so that no page break will 2084 separate the display from its number; and we use the same size and 2085 displacement for all three potential lines of the display, even though 2086 `\.{\\parshape}' may specify them differently. 2087 */ 2088 if (eqno_box && l && (eqno_w == 0)) { /* \.{\\leqno} on a forced single line due to |width=0| */ 2089 /* it follows that |type(a)=hlist_node| */ 2090 shift_amount(eqno_box) = line_s; 2091 append_to_vlist(eqno_box); 2092 tail_append(new_penalty(inf_penalty)); 2093 } else { 2094 tail_append(new_param_glue(g1)); 2095 } 2096 2097 if (eqno_w != 0) { 2098 r = new_kern(line_w - eq_w - eqno_w - d); 2099 s = new_kern(width(r) + eqno_w); 2100 if (l) { 2101 if (swap_dir) { 2102 if (math_direction==dir_TLT) { 2103 /* TRT + TLT + \eqno, (swap_dir=true, math_direction=TLT, l=true) */ 2104 vlink(eqno_box) = r; 2105 vlink(r) = eq_box; 2106 vlink(eq_box) = s; 2107 eq_box = eqno_box; 2108 } else { 2109 /* TLT + TRT + \eqno, (swap_dir=true, math_direction=TRT, l=true) */ 2110 vlink(eqno_box) = r; 2111 vlink(r) = eq_box; 2112 vlink(eq_box) = s; 2113 eq_box = eqno_box; 2114 } 2115 } else { 2116 if (math_direction==dir_TLT) { 2117 /* TLT + TLT + \leqno, (swap_dir=false, math_direction=TLT, l=true) */ /* OK */ 2118 vlink(eqno_box) = r; 2119 vlink(r) = eq_box; 2120 vlink(eq_box) = s; 2121 eq_box = eqno_box; 2122 } else { 2123 /* TRT + TRT + \leqno, (swap_dir=false, math_direction=TRT, l=true) */ 2124 vlink(eqno_box) = r; 2125 vlink(r) = eq_box; 2126 vlink(eq_box) = s; 2127 eq_box = eqno_box; 2128 } 2129 } 2130 } else { 2131 if (swap_dir) { 2132 if (math_direction==dir_TLT) { 2133 /* TRT + TLT + \leqno, (swap_dir=true, math_direction=TLT, l=false) */ 2134 vlink(eq_box) = r; 2135 vlink(r) = eqno_box; 2136 } else { 2137 /* TLT + TRT + \leqno, (swap_dir=true, math_direction=TRT, l=false) */ 2138 vlink(eq_box) = r; 2139 vlink(r) = eqno_box; 2140 } 2141 } else { 2142 if (math_direction==dir_TLT) { 2143 /* TLT + TLT + \eqno, (swap_dir=false, math_direction=TLT, l=false) */ /* OK */ 2144 s = new_kern(d); 2145 vlink(s) = eq_box; 2146 vlink(eq_box) = r; 2147 vlink(r) = eqno_box; 2148 eq_box = s; 2149 } else { 2150 /* TRT + TRT + \eqno, (swap_dir=false, math_direction=TRT, l=false) */ 2151 vlink(s) = eq_box; 2152 vlink(eq_box) = r; 2153 vlink(r) = eqno_box; 2154 eq_box = s; 2155 } 2156 } 2157 } 2158 eq_box = hpack(eq_box, 0, additional, -1); 2159 shift_amount(eq_box) = line_s; 2160 } else { 2161 shift_amount(eq_box) = line_s + d; 2162 } 2163 append_to_vlist(eq_box); 2164 2165 if ((eqno_box != null) && (eqno_w == 0) && !l) { 2166 tail_append(new_penalty(inf_penalty)); 2167 shift_amount(eqno_box) = line_s; 2168 append_to_vlist(eqno_box); 2169 g2 = 0; 2170 } 2171 if (t != adjust_head) { /* migrating material comes after equation number */ 2172 vlink(tail) = vlink(adjust_head); 2173 tail = t; 2174 } 2175 if (pre_t != pre_adjust_head) { 2176 vlink(tail) = vlink(pre_adjust_head); 2177 tail = pre_t; 2178 } 2179 tail_append(new_penalty(int_par(post_display_penalty_code))); 2180 if (g2 > 0) 2181 tail_append(new_param_glue(g2)); 2182 2183 resume_after_display(); 2184} 2185 2186@ @c 2187void after_math(void) 2188{ 2189 int m; /* |mmode| or |-mmode| */ 2190 pointer p; /* the formula */ 2191 pointer a = null; /* box containing equation number */ 2192 boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ 2193 m = mode; 2194 p = fin_mlist(null); /* this pops the nest */ 2195 if (cur_cmd == math_shift_cs_cmd && 2196 (cur_chr == text_style || cur_chr == display_style)) { 2197 you_cant(); 2198 } 2199 if (mode == -m) { /* end of equation number */ 2200 if (cur_cmd == math_shift_cmd) { 2201 check_second_math_shift(); 2202 } else { 2203 check_display_math_end(); 2204 } 2205 run_mlist_to_hlist(p, text_style, false); 2206 a = hpack(vlink(temp_head), 0, additional, -1); 2207 unsave_math(); 2208 decr(save_ptr); /* now |cur_group=math_shift_group| */ 2209 assert(saved_type(0) == saved_eqno); 2210 if (saved_value(0) == 1) 2211 l = true; 2212 m = mode; 2213 p = fin_mlist(null); 2214 } 2215 if (m < 0) { 2216 /* The |unsave| is done after everything else here; hence an appearance of 2217 `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these 2218 particular \.\$'s. This is consistent with the conventions of 2219 `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the 2220 space above that display. 2221 */ 2222 if (cur_cmd == math_shift_cs_cmd) { 2223 check_inline_math_end(); 2224 } 2225 tail_append(new_math(math_surround, before)); 2226 if (dir_math_save) { 2227 tail_append(new_dir(math_direction)); 2228 } 2229 run_mlist_to_hlist(p, text_style, (mode > 0)); 2230 vlink(tail) = vlink(temp_head); 2231 while (vlink(tail) != null) 2232 tail = vlink(tail); 2233 if (dir_math_save) { 2234 tail_append(new_dir(math_direction - 64)); 2235 } 2236 dir_math_save = false; 2237 tail_append(new_math(math_surround, after)); 2238 space_factor = 1000; 2239 unsave_math(); 2240 } else { 2241 if (a == null) { 2242 if (cur_cmd == math_shift_cmd) { 2243 check_second_math_shift(); 2244 } else { 2245 check_display_math_end(); 2246 } 2247 } 2248 run_mlist_to_hlist(p, display_style, false); 2249 finish_displayed_math(l, a, vlink(temp_head)); 2250 } 2251} 2252 2253 2254@ When \.{\\halign} appears in a display, the alignment routines operate 2255essentially as they do in vertical mode. Then the following program is 2256activated, with |p| and |q| pointing to the beginning and end of the 2257resulting list, and with |aux_save| holding the |prev_depth| value. 2258 2259@c 2260void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) 2261{ 2262 do_assignments(); 2263 if (cur_cmd == math_shift_cmd) { 2264 check_second_math_shift(); 2265 } else { 2266 check_display_math_end(); 2267 } 2268 pop_nest(); 2269 tail_append(new_penalty(int_par(pre_display_penalty_code))); 2270 tail_append(new_param_glue(above_display_skip_code)); 2271 vlink(tail) = p; 2272 if (p != null) 2273 tail = q; 2274 tail_append(new_penalty(int_par(post_display_penalty_code))); 2275 tail_append(new_param_glue(below_display_skip_code)); 2276 cur_list.prev_depth_field = saved_prevdepth; 2277 resume_after_display(); 2278} 2279 2280@ Interface to \.{\\Umath} and \.{\\mathstyle} 2281 2282@c 2283void setup_math_style(void) 2284{ 2285 pointer q; 2286 tail_append(new_noad()); 2287 q = new_node(math_char_node, 0); 2288 nucleus(tail) = q; 2289 (void) scan_math(nucleus(tail), num_style(m_style)); 2290} 2291 2292 2293@ @c 2294void print_math_style(void) 2295{ 2296 if (abs(mode) == mmode) 2297 print_int(m_style); 2298 else 2299 print_int(-1); 2300} 2301