fix getsup (HH)
[luatex.git] / source / texk / web2c / luatexdir / tex / align.w
blobdd0ed10338e4eb68d02cb79cd870473415a93c41
1 % align.w
3 % Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
5 % This file is part of LuaTeX.
7 % LuaTeX is free software; you can redistribute it and/or modify it under
8 % the terms of the GNU General Public License as published by the Free
9 % Software Foundation; either version 2 of the License, or (at your
10 % option) any later version.
12 % LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13 % ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 % FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 % License for more details.
17 % You should have received a copy of the GNU General Public License along
18 % with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
20 \def\<#1>{$#1$}
22 @ @c
25 #include "ptexlib.h"
27 @ @c
28 void fin_align(void);
29 void init_row(void);
30 void init_col(void);
32 #define noDEBUG
34 @ It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
35 they cut across so many of the control structures of \TeX.
37 Therefore the present page is probably not the best place for a beginner to
38 start reading this program; it is better to master everything else first.
40 Let us focus our thoughts on an example of what the input might be, in order
41 to get some idea about how the alignment miracle happens. The example doesn't
42 do anything useful, but it is sufficiently general to indicate all of the
43 special cases that must be dealt with; please do not be disturbed by its
44 apparent complexity and meaninglessness.
45 $$\vbox{\halign{\.{#}\hfil\cr
46 {}\\tabskip 2pt plus 3pt\cr
47 {}\\halign to 300pt\{u1\#v1\&\cr
48 \hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
49 \hskip 50pt u3\#v3\\cr\cr
50 \hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
51 \hskip 25pt \\noalign\{\\vskip 3pt\}\cr
52 \hskip 25pt b1\\span b2\\cr\cr
53 \hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
54 Here's what happens:
56 \yskip
57 (0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
58 places the 300pt dimension onto the |save_stack|, and an |align_group|
59 code is placed above it. This will make it possible to complete the alignment
60 when the matching `\.\}' is found.
62 (1) The preamble is scanned next. Macros in the preamble are not expanded,
63 @^preamble@>
64 except as part of a tabskip specification. For example, if \.{u2} had been
65 a macro in the preamble above, it would have been expanded, since \TeX\
66 must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
67 is constructed based on the user's preamble; in our case it contains the
68 following seven items:
69 $$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
70 {}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
71 {}\\alignrecord, width $-\infty$&preamble info for column 1\cr
72 {}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
73 {}\\alignrecord, width $-\infty$&preamble info for column 2\cr
74 {}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
75 {}\\alignrecord, width $-\infty$&preamble info for column 3\cr
76 {}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
77 These ``alignrecord'' entries have the same size as an |unset_node|,
78 since they will later be converted into such nodes. These alignrecord
79 nodes have no |depth| field; this is split into |u_part| and |v_part|,
80 and they point to token lists for the templates of the alignment. For
81 example, the |u_part| field in the first alignrecord points to the
82 token list `\.{u1}', i.e., the template preceding the `\.\#' for
83 column~1. Furthermore, They have a |span_ptr| instead of a |node_attr|
84 field, and these |span_ptr| fields are initially set to the value
85 |end_span|, for reasons explained below.
87 (2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
88 It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
89 be read again, and the template `\.{u1}' is fed to the scanner. Just
90 before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
91 Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
92 {\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
93 token, indicating the end of a column. At this point an |unset_node| is
94 created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
95 The natural width of this unset node replaces the |width| field of the
96 alignrecord for column~1; in general, the alignrecords will record the
97 maximum natural width that has occurred so far in a given column.
99 (3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
100 are now bypassed. Again \TeX\ goes into restricted horizontal mode and
101 makes an |unset_node| from the resulting hlist; but this time the
102 hlist contains simply `\.{a2}'. The natural width of the new unset box
103 is remembered in the |width| field of the alignrecord for column~2.
105 (4) A third |unset_node| is created for column 3, using essentially the
106 mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
107 v3}'. The vertical rule in this case has running dimensions that will later
108 extend to the height and depth of the whole first row, since each |unset_node|
109 in a row will eventually inherit the height and depth of its enclosing box.
111 (5) The first row has now ended; it is made into a single unset box
112 comprising the following seven items:
113 $$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
114 {}\\glue 2pt plus 3pt\cr
115 {}\\unsetbox for 1 column: u1a1v1\cr
116 {}\\glue 2pt plus 3pt\cr
117 {}\\unsetbox for 1 column: a2\cr
118 {}\\glue 1pt plus 1fil\cr
119 {}\\unsetbox for 1 column: u3\\vrule v3\cr
120 {}\\glue 1pt plus 1fil\cr}}$$
121 The width of this unset row is unimportant, but it has the correct height
122 and depth, so the correct baselineskip glue will be computed as the row
123 is inserted into a vertical list.
125 (6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
126 additional material (in this case \.{\\vskip 3pt}) to the vertical list.
127 While processing this material, \TeX\ will be in internal vertical
128 mode, and |no_align_group| will be on |save_stack|.
130 (7) The next row produces an unset box that looks like this:
131 $$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
132 {}\\glue 2pt plus 3pt\cr
133 {}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
134 {}\\glue 1pt plus 1fil\cr
135 {}\\unsetbox for 1 column: {\rm(empty)}\cr
136 {}\\glue 1pt plus 1fil\cr}}$$
137 The natural width of the unset box that spans columns 1~and~2 is stored
138 in a ``span node,'' which we will explain later; the |span_ptr| field of the
139 alignrecord for column~1 now points to the new span node, and the |span_ptr|
140 of the span node points to |end_span|.
142 (8) The final row produces the unset box
143 $$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
144 {}\\glue 2pt plus 3pt\cr
145 {}\\unsetbox for 1 column: {\rm(empty)}\cr
146 {}\\glue 2pt plus 3pt\cr
147 {}\\unsetbox for 2 columns: u2c2v2\cr
148 {}\\glue 1pt plus 1fil\cr}}$$
149 A new span node is attached to the alignrecord for column 2.
151 (9) The last step is to compute the true column widths and to change all the
152 unset boxes to hboxes, appending the whole works to the vertical list that
153 encloses the \.{\\halign}. The rules for deciding on the final widths of
154 each unset column box will be explained below.
156 \yskip\noindent
157 Note that as \.{\\halign} is being processed, we fearlessly give up control
158 to the rest of \TeX. At critical junctures, an alignment routine is
159 called upon to step in and do some little action, but most of the time
160 these routines just lurk in the background. It's something like
161 post-hypnotic suggestion.
163 @ We have mentioned that alignrecords contain no |height| or |depth| fields.
164 Their |glue_sign| and |glue_order| are pre-empted as well, since it
165 is necessary to store information about what to do when a template ends.
166 This information is called the |extra_info| field.
169 /* could be in texnodes.h, but documented here*/
171 #define u_part(A) vlink((A)+depth_offset) /* pointer to \<u_j> token list */
172 #define v_part(A) vinfo((A)+depth_offset) /* pointer to \<v_j> token list */
173 #define span_ptr(A) vinfo((A)+1) /* column spanning list */
174 #define extra_info(A) vinfo((A)+list_offset) /* info to remember during template */
176 @ Alignments can occur within alignments, so a small stack is used to access
177 the alignrecord information. At each level we have a |preamble| pointer,
178 indicating the beginning of the preamble list; a |cur_align| pointer,
179 indicating the current position in the preamble list; a |cur_span| pointer,
180 indicating the value of |cur_align| at the beginning of a sequence of
181 spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
182 an alignrecord that should be copied next if the current list is extended;
183 and the |align_state| variable, which indicates the nesting of braces so
184 that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
185 There also are pointers |cur_head| and |cur_tail| to the head and tail
186 of a list of adjustments being moved out from horizontal mode to
187 vertical~mode, and alike |cur_pre_head| and |cur_pre_tail| for pre-adjust
188 lists.
190 The current values of these nine quantities appear in global variables;
191 when they have to be pushed down, they are stored in 6-word nodes, and
192 |align_ptr| points to the topmost such node.
195 /* could be in texnodes.h but documented here*/
197 #define preamble vlink(align_head) /* the current preamble list */
199 pointer cur_align = null; /* current position in preamble list */
200 pointer cur_span = null; /* start of currently spanned columns in preamble list */
201 pointer cur_loop = null; /* place to copy when extending a periodic preamble */
202 pointer align_ptr = null; /* most recently pushed-down alignment stack node */
203 pointer cur_head = null, cur_tail = null; /* adjustment list pointers */
204 pointer cur_pre_head = null, cur_pre_tail = null; /* pre-adjustment list pointers */
206 /* The |align_state| and |preamble| variables are initialized elsewhere. */
208 @ Alignment stack maintenance is handled by a pair of trivial routines
209 called |push_alignment| and |pop_alignment|.
211 (HH:) It makes not much sense to add support for an \.{attr} keyword to
212 \.{\\halign} and \.{\\valign} because then we need to decide if we tag
213 rows or cells or both or come up with \.{cellattr} and \.{rowattr} and
214 such. But then it even makes sense to have explicit commands (in addition
215 to the seperator) to tags individual cells. Too muss hassle for now and the
216 advantages are not that large.
219 static void push_alignment(void)
221 pointer p; /* the new alignment stack node */
222 p = new_node(align_stack_node, 0);
223 vinfo(p + 1) = align_ptr;
224 vlink(p + 1) = cur_align;
225 vinfo(p + 2) = preamble;
226 vlink(p + 2) = cur_span;
227 vinfo(p + 3) = cur_loop;
228 vlink(p + 3) = align_state;
229 vinfo(p + 4) = cur_head;
230 vlink(p + 4) = cur_tail;
231 vinfo(p + 5) = cur_pre_head;
232 vlink(p + 5) = cur_pre_tail;
233 align_ptr = p;
234 cur_head = new_node(temp_node, 0);
235 cur_pre_head = new_node(temp_node, 0);
238 static void pop_alignment(void)
240 pointer p; /* the top alignment stack node */
241 flush_node(cur_head);
242 flush_node(cur_pre_head);
243 p = align_ptr;
244 cur_pre_tail = vlink(p + 5);
245 cur_pre_head = vinfo(p + 5);
246 cur_tail = vlink(p + 4);
247 cur_head = vinfo(p + 4);
248 align_state = vlink(p + 3);
249 cur_loop = vinfo(p + 3);
250 cur_span = vlink(p + 2);
251 preamble = vinfo(p + 2);
252 cur_align = vlink(p + 1);
253 align_ptr = vinfo(p + 1);
254 flush_node(p);
258 @ \TeX\ has eight procedures that govern alignments: |init_align| and
259 |fin_align| are used at the very beginning and the very end; |init_row| and
260 |fin_row| are used at the beginning and end of individual rows; |init_span|
261 is used at the beginning of a sequence of spanned columns (possibly involving
262 only one column); |init_col| and |fin_col| are used at the beginning and
263 end of individual columns; and |align_peek| is used after \.{\\cr} to see
264 whether the next item is \.{\\noalign}.
266 We shall consider these routines in the order they are first used during
267 the course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
268 |init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
271 @ The preamble is copied directly, except that \.{\\tabskip} causes a change
272 to the tabskip glue, thereby possibly expanding macros that immediately
273 follow it. An appearance of \.{\\span} also causes such an expansion.
275 Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}'
276 token survives in the preamble and the `\.{\\tabskip}' defines new
277 tabskip glue (locally).
280 static void get_preamble_token(void)
282 RESTART:
283 get_token();
284 while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) {
285 get_token(); /* this token will be expanded once */
286 if (cur_cmd > max_command_cmd) {
287 expand();
288 get_token();
291 if (cur_cmd == endv_cmd)
292 fatal_error("(interwoven alignment preambles are not allowed)");
293 if ((cur_cmd == assign_glue_cmd)
294 && (cur_chr == glue_base + tab_skip_code)) {
295 scan_optional_equals();
296 scan_glue(glue_val_level);
297 if (global_defs_par > 0)
298 geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
299 else
300 eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
301 goto RESTART;
307 @ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
308 mode, \TeX\ calls |init_align|, whose task is to get everything off to a
309 good start. This mostly involves scanning the preamble and putting its
310 information into the preamble list.
311 @^preamble@>
314 void init_align(void)
316 /* label done, done1, done2, continue; */
317 pointer save_cs_ptr; /* |warning_index| value for error messages */
318 pointer p, r; /* for short-term temporary use */
319 save_cs_ptr = cur_cs; /* \.{\\halign} or \.{\\valign}, usually */
320 push_alignment();
321 align_state = -1000000; /* enter a new alignment level */
323 /* When \.{\\halign} is used as a displayed formula, there should be
324 no other pieces of mlists present. */
326 if ((cur_list.mode_field == mmode)
327 && ((cur_list.tail_field != cur_list.head_field)
328 || (incompleat_noad_par != null))) {
329 const char *hlp[] =
330 { "Displays can use special alignments (like \\eqalignno)",
331 "only if nothing but the alignment itself is between $$'s.",
332 "So I've deleted the formulas that preceded this alignment.",
333 NULL
335 tex_error("Improper \\halign inside $$'s", hlp);
336 flush_math();
338 push_nest(); /* enter a new semantic level */
339 /* In vertical modes, |prev_depth| already has the correct value. But
340 if we are in |mmode| (displayed formula mode), we reach out to the
341 enclosing vertical mode for the |prev_depth| value that produces the
342 correct baseline calculations. */
343 if (cur_list.mode_field == mmode) {
344 cur_list.mode_field = -vmode;
345 prev_depth_par = nest[nest_ptr - 2].prev_depth_field;
346 } else if (cur_list.mode_field > 0) {
347 cur_list.mode_field = -(cur_list.mode_field);
349 scan_spec(align_group);
350 /* Scan the preamble */
351 preamble = null;
352 cur_align = align_head;
353 cur_loop = null;
354 scanner_status = aligning;
355 warning_index = save_cs_ptr;
356 align_state = -1000000;
357 /* at this point, |cur_cmd=left_brace| */
358 while (true) {
359 /* Append the current tabskip glue to the preamble list */
360 r = new_param_glue(tab_skip_code);
361 vlink(cur_align) = r;
362 cur_align = vlink(cur_align);
364 if (cur_cmd == car_ret_cmd)
365 break; /* \.{\\cr} ends the preamble */
367 /* Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| */
368 /* Scan the template \<u_j>, putting the resulting token list in |hold_token_head| */
369 /* Spaces are eliminated from the beginning of a template. */
371 p = hold_token_head;
372 token_link(p) = null;
373 while (1) {
374 get_preamble_token();
375 if (cur_cmd == mac_param_cmd)
376 break;
377 if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
378 && (align_state == -1000000)) {
379 if ((p == hold_token_head) && (cur_loop == null)
380 && (cur_cmd == tab_mark_cmd)) {
381 cur_loop = cur_align;
382 } else {
383 const char *hlp[] =
384 { "There should be exactly one # between &'s, when an",
385 "\\halign or \\valign is being set up. In this case you had",
386 "none, so I've put one in; maybe that will work.",
387 NULL
389 back_input();
390 tex_error("Missing # inserted in alignment preamble", hlp);
391 break;
393 } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) {
394 r = get_avail();
395 token_link(p) = r;
396 p = token_link(p);
397 token_info(p) = cur_tok;
400 r = new_node(align_record_node, 0);
401 vlink(cur_align) = r;
402 cur_align = vlink(cur_align); /* a new alignrecord */
403 span_ptr(cur_align) = end_span;
404 width(cur_align) = null_flag;
405 u_part(cur_align) = token_link(hold_token_head);
406 /* Scan the template \<v_j>, putting the resulting token list in |hold_token_head| */
408 p = hold_token_head;
409 token_link(p) = null;
410 while (1) {
411 CONTINUE:
412 get_preamble_token();
413 if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
414 && (align_state == -1000000))
415 break;
416 if (cur_cmd == mac_param_cmd) {
417 const char *hlp[] =
418 { "There should be exactly one # between &'s, when an",
419 "\\halign or \\valign is being set up. In this case you had",
420 "more than one, so I'm ignoring all but the first.",
421 NULL
423 tex_error("Only one # is allowed per tab", hlp);
424 goto CONTINUE;
426 r = get_avail();
427 token_link(p) = r;
428 p = token_link(p);
429 token_info(p) = cur_tok;
431 r = get_avail();
432 token_link(p) = r;
433 p = token_link(p);
434 token_info(p) = end_template_token; /* put \.{\\endtemplate} at the end */
436 v_part(cur_align) = token_link(hold_token_head);
438 scanner_status = normal;
440 new_save_level(align_group);
441 if (every_cr_par != null)
442 begin_token_list(every_cr_par, every_cr_text);
443 align_peek(); /* look for \.{\\noalign} or \.{\\omit} */
447 @ The tricky part about alignments is getting the templates into the
448 scanner at the right time, and recovering control when a row or column
449 is finished.
451 We usually begin a row after each \.{\\cr} has been sensed, unless that
452 \.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
453 the alignment. The |align_peek| routine is used to look ahead and do
454 the right thing; it either gets a new row started, or gets a \.{\\noalign}
455 started, or finishes off the alignment.
458 void align_peek(void)
460 RESTART:
461 align_state = 1000000;
462 do {
463 get_x_or_protected();
464 } while (cur_cmd == spacer_cmd);
465 if (cur_cmd == no_align_cmd) {
466 scan_left_brace();
467 new_save_level(no_align_group);
468 if (cur_list.mode_field == -vmode)
469 normal_paragraph();
470 } else if (cur_cmd == right_brace_cmd) {
471 fin_align();
472 } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) {
473 goto RESTART; /* ignore \.{\\crcr} */
474 } else {
475 init_row(); /* start a new row */
476 init_col(); /* start a new column and replace what we peeked at */
481 @ The parameter to |init_span| is a pointer to the alignrecord where the
482 next column or group of columns will begin. A new semantic level is
483 entered, so that the columns will generate a list for subsequent packaging.
486 static void init_span(pointer p)
488 push_nest();
489 if (cur_list.mode_field == -hmode) {
490 space_factor_par = 1000;
491 } else {
492 prev_depth_par = ignore_depth;
493 normal_paragraph();
495 cur_span = p;
499 @ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
500 we enter a new semantic level, copy the first tabskip glue, and change
501 from internal vertical mode to restricted horizontal mode or vice versa.
502 The |space_factor| and |prev_depth| are not used on this semantic level,
503 but we clear them to zero just to be tidy.
506 void init_row(void)
508 push_nest();
509 cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field;
510 if (cur_list.mode_field == -hmode)
511 space_factor_par = 0;
512 else
513 prev_depth_par = 0;
514 tail_append(new_glue(preamble));
515 subtype(cur_list.tail_field) = tab_skip_code + 1;
516 cur_align = vlink(preamble);
517 cur_tail = cur_head;
518 cur_pre_tail = cur_pre_head;
519 init_span(cur_align);
523 @ When a column begins, we assume that |cur_cmd| is either |omit| or else
524 the current token should be put back into the input until the \<u_j>
525 template has been scanned. (Note that |cur_cmd| might be |tab_mark| or
526 |car_ret|.) We also assume that |align_state| is approximately 1000000 at
527 this time. We remain in the same mode, and start the template if it is
528 called for.
531 void init_col(void)
533 extra_info(cur_align) = cur_cmd;
534 if (cur_cmd == omit_cmd)
535 align_state = 0;
536 else {
537 back_input();
538 begin_token_list(u_part(cur_align), u_template);
539 } /* now |align_state=1000000| */
543 @ The scanner sets |align_state| to zero when the \<u_j> template ends. When
544 a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|,
545 the scanner activates the following code, which fires up the \<v_j> template.
546 We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
547 |span_code|, or a character code, depending on how the column text has ended.
549 This part of the program had better not be activated when the preamble
550 to another alignment is being scanned, or when no alignment preamble is active.
553 void insert_vj_template(void)
555 if ((scanner_status == aligning) || (cur_align == null))
556 fatal_error("(interwoven alignment preambles are not allowed)");
557 cur_cmd = extra_info(cur_align);
558 extra_info(cur_align) = cur_chr;
559 if (cur_cmd == omit_cmd)
560 begin_token_list(omit_template, v_template);
561 else
562 begin_token_list(v_part(cur_align), v_template);
563 align_state = 1000000;
566 /* Determine the stretch order */
567 #define determine_stretch_order() do { \
568 if (total_stretch[filll]!=0) o=filll; \
569 else if (total_stretch[fill]!=0) o=fill; \
570 else if (total_stretch[fil]!=0) o=fil; \
571 else if (total_stretch[sfi]!=0) o=sfi; \
572 else o=normal; \
573 } while (0)
576 /* Determine the shrink order */
577 #define determine_shrink_order() do { \
578 if (total_shrink[filll]!=0) o=filll; \
579 else if (total_shrink[fill]!=0) o=fill; \
580 else if (total_shrink[fil]!=0) o=fil; \
581 else if (total_shrink[sfi]!=0) o=sfi; \
582 else o=normal; \
583 } while (0)
587 @ When the |endv| command at the end of a \<v_j> template comes through the
588 scanner, things really start to happen; and it is the |fin_col| routine
589 that makes them happen. This routine returns |true| if a row as well as a
590 column has been finished.
593 boolean fin_col(void)
595 pointer p; /* the alignrecord after the current one */
596 pointer q, r; /* temporary pointers for list manipulation */
597 pointer s; /* a new span node */
598 pointer u; /* a new unset box */
599 scaled w; /* natural width */
600 unsigned char o; /* order of infinity */
601 halfword n; /* span counter */
602 if (cur_align == null)
603 confusion("endv");
604 q = vlink(cur_align);
605 if (q == null)
606 confusion("endv");
607 if (align_state < 500000)
608 fatal_error("(interwoven alignment preambles are not allowed)");
609 p = vlink(q);
610 /* If the preamble list has been traversed, check that the row has ended */
611 if ((p == null) && (extra_info(cur_align) < cr_code)) {
612 if (cur_loop != null) {
613 /* Lengthen the preamble periodically */
614 r = new_node(align_record_node, 0);
615 vlink(q) = r;
616 p = vlink(q); /* a new alignrecord */
617 span_ptr(p) = end_span;
618 width(p) = null_flag;
619 cur_loop = vlink(cur_loop);
621 /* Copy the templates from node |cur_loop| into node |p| */
622 q = hold_token_head;
623 r = u_part(cur_loop);
624 while (r != null) {
625 s = get_avail();
626 token_link(q) = s;
627 q = token_link(q);
628 token_info(q) = token_info(r);
629 r = token_link(r);
631 token_link(q) = null;
632 u_part(p) = token_link(hold_token_head);
633 q = hold_token_head;
634 r = v_part(cur_loop);
635 while (r != null) {
636 s = get_avail();
637 token_link(q) = s;
638 q = token_link(q);
639 token_info(q) = token_info(r);
640 r = token_link(r);
642 token_link(q) = null;
643 v_part(p) = token_link(hold_token_head);
645 cur_loop = vlink(cur_loop);
646 r = new_glue(cur_loop);
647 vlink(p) = r;
648 } else {
649 const char *hlp[] =
650 { "You have given more \\span or & marks than there were",
651 "in the preamble to the \\halign or \\valign now in progress.",
652 "So I'll assume that you meant to type \\cr instead.",
653 NULL
655 extra_info(cur_align) = cr_code;
656 tex_error("Extra alignment tab has been changed to \\cr", hlp);
659 if (extra_info(cur_align) != span_code) {
660 unsave();
661 new_save_level(align_group);
662 /* Package an unset box for the current column and record its width */
663 if (cur_list.mode_field == -hmode) {
664 adjust_tail = cur_tail;
665 pre_adjust_tail = cur_pre_tail;
666 u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
667 additional, align_set_group, -1, 0, 0);
668 w = width(u);
669 cur_tail = adjust_tail;
670 adjust_tail = null;
671 cur_pre_tail = pre_adjust_tail;
672 pre_adjust_tail = null;
673 } else {
674 u = filtered_vpackage(vlink(cur_list.head_field),
675 0, additional, 0, align_set_group, -1, 0, 0);
676 w = height(u);
678 n = min_quarterword; /* this represents a span count of 1 */
679 if (cur_span != cur_align) {
680 /* Update width entry for spanned columns */
681 q = cur_span;
682 do {
683 incr(n);
684 q = vlink(vlink(q));
685 } while (q != cur_align);
686 if (n > max_quarterword)
687 confusion("too many spans"); /* this can happen, but won't */
688 q = cur_span;
689 while (span_span(span_ptr(q)) < n) {
690 q = span_ptr(q);
692 if (span_span(span_ptr(q)) > n) {
693 s = new_span_node(span_ptr(q), n, w);
694 span_ptr(q) = s;
695 } else if (width(span_ptr(q)) < w) {
696 width(span_ptr(q)) = w;
699 } else if (w > width(cur_align)) {
700 width(cur_align) = w;
702 type(u) = unset_node;
703 span_count(u) = (quarterword) n;
704 determine_stretch_order();
705 glue_order(u) = o;
706 glue_stretch(u) = total_stretch[o];
707 determine_shrink_order();
708 glue_sign(u) = o;
709 glue_shrink(u) = total_shrink[o];
710 pop_nest();
711 vlink(cur_list.tail_field) = u;
712 cur_list.tail_field = u;
714 /* Copy the tabskip glue between columns */
715 tail_append(new_glue(vlink(cur_align)));
716 subtype(cur_list.tail_field) = tab_skip_code + 1;
718 if (extra_info(cur_align) >= cr_code) {
719 return true;
721 init_span(p);
723 align_state = 1000000;
724 do {
725 get_x_or_protected();
726 } while (cur_cmd == spacer_cmd);
727 cur_align = p;
728 init_col();
729 return false;
734 @ A span node is a 3-word record containing |width|, |span_span|, and
735 |span_ptr| fields. The |span_span| field indicates the number of
736 spanned columns; the |span_ptr| field points to a span node for the same
737 starting column, having a greater extent of spanning, or to
738 |end_span|, which has the largest possible |span_span| field; the |width|
739 field holds the largest natural width corresponding to a particular
740 set of spanned columns.
742 A list of the maximum widths so far, for spanned columns starting at a
743 given column, begins with the |span_ptr| field of the alignrecord for
744 that column. The code has to make sure that there is room for
745 |span_ptr| in both the alignrecord and the span nodes, which is why
746 |span_ptr| replaces |node_attr|.
747 @^data structure assumptions@>
749 The |new_span_node| function is defined in |texnodes.c|.
752 #ifndef span_span
753 # define span_span(A) vlink((A)+1) /* that is normally |alink| */
754 #endif
757 @ At the end of a row, we append an unset box to the current vlist (for
758 \.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
759 contains the unset boxes for the columns, separated by the tabskip glue.
760 Everything will be set later.
763 void fin_row(void)
765 pointer p; /* the new unset box */
766 if (cur_list.mode_field == -hmode) {
767 p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
768 additional, fin_row_group, -1, 0, 0);
769 pop_nest();
770 if (cur_pre_head != cur_pre_tail)
771 append_list(cur_pre_head, cur_pre_tail);
772 append_to_vlist(p,lua_key_index(alignment));
773 if (cur_head != cur_tail)
774 append_list(cur_head, cur_tail);
775 } else {
776 p = filtered_vpackage(vlink(cur_list.head_field),
777 0, additional, max_depth_par, fin_row_group, -1, 0, 0);
778 pop_nest();
779 vlink(cur_list.tail_field) = p;
780 cur_list.tail_field = p;
781 space_factor_par = 1000;
783 type(p) = unset_node;
784 glue_stretch(p) = 0;
785 if (every_cr_par != null)
786 begin_token_list(every_cr_par, every_cr_text);
787 align_peek();
788 /* note that |glue_shrink(p)=0| since |glue_shrink==shift_amount| */
792 @ Finally, we will reach the end of the alignment, and we can breathe a
793 sigh of relief that memory hasn't overflowed. All the unset boxes will now be
794 set so that the columns line up, taking due account of spanned columns.
797 void fin_align(void)
799 pointer p, q, r, s, u, rr; /* registers for the list operations */
800 scaled t, w; /* width of column */
801 scaled o; /* shift offset for unset boxes */
802 halfword n; /* matching span amount */
803 scaled rule_save; /* temporary storage for |overfull_rule| */
804 halfword pd; /* temporary storage for |prev_depth| */
805 halfword ng; /* temporary storage for |new_glue| */
806 if (cur_group != align_group)
807 confusion("align1");
808 unsave(); /* that |align_group| was for individual entries */
809 if (cur_group != align_group)
810 confusion("align0");
811 unsave(); /* that |align_group| was for the whole alignment */
812 if (nest[nest_ptr - 1].mode_field == mmode)
813 o = display_indent_par;
814 else
815 o = 0;
816 /* Go through the preamble list, determining the column widths and
817 * changing the alignrecords to dummy unset boxes
820 /* It's time now to dismantle the preamble list and to compute the column
821 widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
822 that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
823 contains $w_{ii}$ in its |width| field, and there is also a linked list of
824 the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
825 these span nodes contain the value $j-i+|min_quarterword|$ in their
826 |link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
827 we regard as $-\infty$.
829 The final column widths are defined by the formula
830 $$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$
831 where $t_k$ is the natural width of the tabskip glue between columns
832 $k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
833 |1<=i<=j| (i.e., if every entry that involved column~|j| also involved
834 column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
835 column~|j|.
837 \TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
838 Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
839 Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
840 for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
841 value is changed to zero and so is the next tabskip.
843 q = vlink(preamble);
844 do {
845 flush_list(u_part(q));
846 flush_list(v_part(q));
847 p = vlink(vlink(q));
848 if (width(q) == null_flag) {
849 /* Nullify |width(q)| and the tabskip glue following this column */
850 width(q) = 0;
851 r = vlink(q);
852 reset_glue_to_zero(r); /* is a lready copy */
854 if (span_ptr(q) != end_span) {
855 /* Merge the widths in the span nodes of |q| with those of |p|,
856 destroying the span nodes of |q| */
858 Merging of two span-node lists is a typical exercise in the manipulation of
859 linearly linked data structures. The essential invariant in the following
860 |repeat| loop is that we want to dispense with node |r|, in |q|'s list,
861 and |u| is its successor; all nodes of |p|'s list up to and including |s|
862 have been processed, and the successor of |s| matches |r| or precedes |r|
863 or follows |r|, according as |link(r)=n| or |link(r)>n| or |link(r)<n|.
865 t = width(q) + width(vlink(q));
866 r = span_ptr(q);
867 s = end_span;
868 span_ptr(s) = p;
869 n = min_quarterword + 1;
870 do {
871 width(r) = width(r) - t;
872 u = span_ptr(r);
873 while (span_span(r) > n) {
874 s = span_ptr(s);
875 n = span_span(span_ptr(s)) + 1;
877 if (span_span(r) < n) {
878 span_ptr(r) = span_ptr(s);
879 span_ptr(s) = r;
880 decr(span_span(r));
881 s = r;
882 } else {
883 if (width(r) > width(span_ptr(s)))
884 width(span_ptr(s)) = width(r);
885 flush_node(r);
887 r = u;
888 } while (r != end_span);
890 type(q) = unset_node;
891 span_count(q) = min_quarterword;
892 height(q) = 0;
893 depth(q) = 0;
894 glue_order(q) = normal;
895 glue_sign(q) = normal;
896 glue_stretch(q) = 0;
897 glue_shrink(q) = 0;
898 q = p;
899 } while (q != null);
901 /* Package the preamble list, to determine the actual tabskip glue amounts,
902 and let |p| point to this prototype box */
903 /* Now the preamble list has been converted to a list of alternating unset
904 boxes and tabskip glue, where the box widths are equal to the final
905 column sizes. In case of \.{\\valign}, we change the widths to heights,
906 so that a correct error message will be produced if the alignment is
907 overfull or underfull.
910 decr(save_ptr);
911 pack_begin_line = -cur_list.ml_field;
912 if (cur_list.mode_field == -vmode) {
913 rule_save = overfull_rule_par;
914 overfull_rule_par = 0; /* prevent rule from being packaged */
915 p = hpack(preamble, saved_value(0), saved_level(0), -1);
916 overfull_rule_par = rule_save;
917 } else {
918 q = vlink(preamble);
919 do {
920 height(q) = width(q);
921 width(q) = 0;
922 q = vlink(vlink(q));
923 } while (q != null);
924 p = filtered_vpackage(preamble,
925 saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0);
926 q = vlink(preamble);
927 do {
928 width(q) = height(q);
929 height(q) = 0;
930 q = vlink(vlink(q));
931 } while (q != null);
933 pack_begin_line = 0;
935 /* Set the glue in all the unset boxes of the current list */
936 q = vlink(cur_list.head_field);
937 s = cur_list.head_field;
938 while (q != null) {
939 if (!is_char_node(q)) {
940 if (type(q) == unset_node) {
941 /* Set the unset box |q| and the unset boxes in it */
942 /* The unset box |q| represents a row that contains one or more unset boxes,
943 depending on how soon \.{\\cr} occurred in that row. */
945 if (cur_list.mode_field == -vmode) {
946 type(q) = hlist_node;
947 subtype(q) = align_row_list;
948 width(q) = width(p);
949 } else {
950 type(q) = vlist_node;
951 subtype(q) = align_row_list;
952 height(q) = height(p);
954 glue_order(q) = glue_order(p);
955 glue_sign(q) = glue_sign(p);
956 glue_set(q) = glue_set(p);
957 shift_amount(q) = o;
958 r = vlink(list_ptr(q));
959 assert (type(r) == unset_node);
960 s = vlink(list_ptr(p));
961 do {
962 /* Set the glue in node |r| and change it from an unset node */
963 /* A box made from spanned columns will be followed by tabskip glue nodes and
964 by empty boxes as if there were no spanning. This permits perfect alignment
965 of subsequent entries, and it prevents values that depend on floating point
966 arithmetic from entering into the dimensions of any boxes.
968 n = span_count(r);
969 t = width(s);
970 w = t;
971 u = hold_head;
972 while (n > min_quarterword) {
973 decr(n);
974 /* Append tabskip glue and an empty box to list |u|,
975 and update |s| and |t| as the prototype nodes are passed */
977 s = vlink(s);
978 ng = new_glue(s);
979 vlink(u) = ng;
980 u = vlink(u);
981 subtype(u) = tab_skip_code + 1;
982 t = t + width(s);
983 if (glue_sign(p) == stretching) {
984 if (stretch_order(s) == glue_order(p))
985 t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s)));
986 } else if (glue_sign(p) == shrinking) {
987 if (shrink_order(s) == glue_order(p))
988 t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s)));
990 s = vlink(s);
991 rr = new_null_box();
992 vlink(u) = rr;
993 u = vlink(u);
994 t = t + width(s);
995 subtype(u) = align_cell_list;
996 if (cur_list.mode_field == -vmode) {
997 width(u) = width(s);
998 } else {
999 type(u) = vlist_node;
1000 height(u) = width(s);
1004 if (cur_list.mode_field == -vmode) {
1005 /* Make the unset node |r| into an |hlist_node| of width |w|,
1006 setting the glue as if the width were |t| */
1008 height(r) = height(q);
1009 depth(r) = depth(q);
1010 if (t == width(r)) {
1011 glue_sign(r) = normal;
1012 glue_order(r) = normal;
1013 set_glue_ratio_zero(glue_set(r));
1014 } else if (t > width(r)) {
1015 glue_sign(r) = stretching;
1016 if (glue_stretch(r) == 0)
1017 set_glue_ratio_zero(glue_set(r));
1018 else
1019 glue_set(r) =
1020 unfloat((double) (t - width(r)) /
1021 glue_stretch(r));
1022 } else {
1023 glue_order(r) = glue_sign(r);
1024 glue_sign(r) = shrinking;
1025 if (glue_shrink(r) == 0)
1026 set_glue_ratio_zero(glue_set(r));
1027 else if ((glue_order(r) == normal)
1028 && (width(r) - t > glue_shrink(r)))
1029 set_glue_ratio_one(glue_set(r));
1030 else
1031 glue_set(r) =
1032 unfloat((double) (width(r) - t) /
1033 glue_shrink(r));
1035 width(r) = w;
1036 type(r) = hlist_node;
1037 subtype(r) = align_cell_list;
1039 } else {
1040 /* Make the unset node |r| into a |vlist_node| of height |w|,
1041 setting the glue as if the height were |t| */
1043 width(r) = width(q);
1044 if (t == height(r)) {
1045 glue_sign(r) = normal;
1046 glue_order(r) = normal;
1047 set_glue_ratio_zero(glue_set(r));
1048 } else if (t > height(r)) {
1049 glue_sign(r) = stretching;
1050 if (glue_stretch(r) == 0)
1051 set_glue_ratio_zero(glue_set(r));
1052 else
1053 glue_set(r) =
1054 unfloat((t - height(r)) / glue_stretch(r));
1055 } else {
1056 glue_order(r) = glue_sign(r);
1057 glue_sign(r) = shrinking;
1058 if (glue_shrink(r) == 0)
1059 set_glue_ratio_zero(glue_set(r));
1060 else if ((glue_order(r) == normal)
1061 && (height(r) - t > glue_shrink(r)))
1062 set_glue_ratio_one(glue_set(r));
1063 else
1064 glue_set(r) =
1065 unfloat((height(r) - t) / glue_shrink(r));
1067 height(r) = w;
1068 type(r) = vlist_node;
1069 subtype(r) = align_cell_list;
1072 /* subtype(r) = 0; */
1073 shift_amount(r) = 0;
1074 if (u != hold_head) { /* append blank boxes to account for spanned nodes */
1075 vlink(u) = vlink(r);
1076 vlink(r) = vlink(hold_head);
1077 r = u;
1080 r = vlink(vlink(r));
1081 s = vlink(vlink(s));
1082 } while (r != null);
1084 } else if (type(q) == rule_node) {
1085 /* Make the running dimensions in rule |q| extend to the
1086 boundaries of the alignment */
1087 if (is_running(width(q)))
1088 width(q) = width(p);
1089 if (is_running(height(q)))
1090 height(q) = height(p);
1091 if (is_running(depth(q)))
1092 depth(q) = depth(p);
1093 if (o != 0) {
1094 r = vlink(q);
1095 vlink(q) = null;
1096 q = hpack(q, 0, additional, -1);
1097 shift_amount(q) = o;
1098 subtype(q) = align_cell_list;
1099 vlink(q) = r;
1100 vlink(s) = q;
1104 s = q;
1105 q = vlink(q);
1107 flush_node_list(p);
1108 pop_alignment();
1109 /* Insert the current list into its environment */
1110 /* We now have a completed alignment, in the list that starts at |cur_list.head_field|
1111 and ends at |cur_list.tail_field|. This list will be merged with the one that encloses
1112 it. (In case the enclosing mode is |mmode|, for displayed formulas,
1113 we will need to insert glue before and after the display; that part of the
1114 program will be deferred until we're more familiar with such operations.)
1116 pd = prev_depth_par;
1117 p = vlink(cur_list.head_field);
1118 q = cur_list.tail_field;
1119 pop_nest();
1120 if (cur_list.mode_field == mmode) {
1121 finish_display_alignment(p, q, pd);
1122 } else {
1123 prev_depth_par = pd; /* aux:=aux_save; */
1124 vlink(cur_list.tail_field) = p;
1125 if (p != null)
1126 cur_list.tail_field = q;
1127 if (cur_list.mode_field == vmode) {
1128 if (!output_active)
1129 lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment));
1130 build_page();
1135 @ The token list |omit_template| just referred to is a constant token
1136 list that contains the special control sequence \.{\\endtemplate} only.
1139 void initialize_alignments(void)
1141 token_info(omit_template) = end_template_token; /* |link(omit_template)=null| */
1142 span_span(end_span) = max_quarterword + 1;
1143 span_ptr(end_span) = null;