beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / tex / conditional.w
blobfa4e73b49cb024fc844c515b31b39f683a5bd13b
1 % conditional.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 @ @c
22 #include "ptexlib.h"
24 @ @c
25 #define box(A) eqtb[box_base+(A)].hh.rh
27 @* We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
29 @ Conditions can be inside conditions, and this nesting has a stack
30 that is independent of the |save_stack|.
32 Four global variables represent the top of the condition stack:
33 |cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
34 the largest code of a |fi_or_else| command that is syntactically legal;
35 |cur_if| is the name of the current type of conditional; and |if_line|
36 is the line number at which it began.
38 If no conditions are currently in progress, the condition stack has the
39 special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
40 Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
41 |link| fields of the first word contain |if_limit|, |cur_if|, and
42 |cond_ptr| at the next level, and the second word contains the
43 corresponding |if_line|.
46 halfword cond_ptr; /* top of the condition stack */
47 int if_limit; /* upper bound on |fi_or_else| codes */
48 int cur_if; /* type of conditional being worked on */
49 int if_line; /* line where that conditional began */
51 @ When we skip conditional text, we keep track of the line number
52 where skipping began, for use in error messages.
55 int skip_line; /* skipping began here */
57 @ Here is a procedure that ignores text until coming to an \.{\\or},
58 \.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$
59 nesting. After it has acted, |cur_chr| will indicate the token that
60 was found, but |cur_tok| will not be set (because this makes the
61 procedure run faster).
64 void pass_text(void)
66 int l = 0; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */
67 int save_scanner_status = scanner_status; /* |scanner_status| upon entry */
68 scanner_status = skipping;
69 skip_line = line;
70 while (1) {
71 get_next();
72 if (cur_cmd == fi_or_else_cmd) {
73 if (l == 0)
74 break;
75 if (cur_chr == fi_code)
76 decr(l);
77 } else if (cur_cmd == if_test_cmd) {
78 incr(l);
81 scanner_status = save_scanner_status;
82 if (int_par(tracing_ifs_code) > 0)
83 show_cur_cmd_chr();
86 @ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then
87 if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
88 condition has been evaluated, \.{\\relax} will be inserted.
89 For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
90 would otherwise require something after the `\.1'.
93 void push_condition_stack(void)
95 halfword p = new_node(if_node, 0);
96 vlink(p) = cond_ptr;
97 if_limit_type(p) = (quarterword) if_limit;
98 if_limit_subtype(p) = (quarterword) cur_if;
99 if_line_field(p) = if_line;
100 cond_ptr = p;
101 cur_if = cur_chr;
102 if_limit = if_code;
103 if_line = line;
106 void pop_condition_stack(void)
108 halfword p;
109 if (if_stack[in_open] == cond_ptr)
110 if_warning();
111 /* conditionals possibly not properly nested with files */
112 p = cond_ptr;
113 if_line = if_line_field(p);
114 cur_if = if_limit_subtype(p);
115 if_limit = if_limit_type(p);
116 cond_ptr = vlink(p);
117 flush_node(p);
120 @ Here's a procedure that changes the |if_limit| code corresponding to
121 a given value of |cond_ptr|.
124 void change_if_limit(int l, halfword p)
126 if (p == cond_ptr) {
127 /* that's the easy case */
128 if_limit = l;
129 } else {
130 halfword q = cond_ptr;
131 while (1) {
132 if (q == null)
133 confusion("if");
134 if (vlink(q) == p) {
135 if_limit_type(q) = (quarterword) l;
136 return;
138 q = vlink(q);
143 @ The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter}
144 \.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new
145 control sequence will be entered into the hash table (once all tokens
146 preceding the mandatory \.{\\endcsname} have been expanded).
149 static halfword last_tested_cs ;
151 static boolean test_for_cs(void)
153 boolean b = false; /*is the condition true? */
154 int m, s; /*to be tested against the second operand */
155 halfword q; /*for traversing token lists in \.{\\ifx} tests */
156 halfword n = get_avail();
157 halfword p = n; /*head of the list of characters */
158 while (1) {
159 get_x_token();
160 if (cur_cs != 0)
161 break;
162 store_new_token(cur_tok);
164 if (cur_cmd != end_cs_name_cmd) {
165 last_tested_cs = null_cs;
166 if (int_par(suppress_ifcsname_error_code)) {
167 do {
168 get_x_token();
169 } while (cur_cmd != end_cs_name_cmd);
170 flush_list(n);
171 return b;
172 } else {
173 complain_missing_csname();
176 /* Look up the characters of list |n| in the hash table, and set |cur_cs| */
177 m = first;
178 p = token_link(n);
179 while (p != null) {
180 if (m >= max_buf_stack) {
181 max_buf_stack = m + 4;
182 if (max_buf_stack >= buf_size)
183 check_buffer_overflow(max_buf_stack);
185 s = token_chr(token_info(p));
186 if (s <= 0x7F) {
187 buffer[m++] = (packed_ASCII_code) s;
188 } else if (s <= 0x7FF) {
189 buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40);
190 buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40);
191 } else if (s <= 0xFFFF) {
192 buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000);
193 buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40);
194 buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40);
195 } else {
196 buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000);
197 buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000);
198 buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40);
199 buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40);
201 p = token_link(p);
203 if (m > first) {
204 cur_cs = id_lookup(first, m - first); /* |no_new_control_sequence| is |true| */
205 } else if (m == first) {
206 cur_cs = null_cs; /* the list is empty */
208 b = (eq_type(cur_cs) != undefined_cs_cmd);
209 flush_list(n);
210 last_cs_name = cur_cs;
211 return b;
214 @ An active character will be treated as category 13 following
215 \.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}.
218 #define get_x_token_or_active_char() do { \
219 get_x_token(); \
220 if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \
221 if (is_active_cs(cs_text(cur_cs))) { \
222 cur_cmd=active_char_cmd; \
223 cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \
226 } while (0)
228 @ A condition is started when the |expand| procedure encounters
229 an |if_test| command; in that case |expand| reduces to |conditional|,
230 which is a recursive procedure.
231 @^recursion@>
234 void conditional(void)
236 boolean b = false; /*is the condition true? */
237 int r; /*relation to be evaluated */
238 int m, n; /*to be tested against the second operand */
239 halfword p, q; /*for traversing token lists in \.{\\ifx} tests */
240 int save_scanner_status; /*|scanner_status| upon entry */
241 halfword save_cond_ptr; /*|cond_ptr| corresponding to this conditional */
242 int this_if; /*type of this conditional */
243 boolean is_unless; /*was this if preceded by `\.{\\unless}' ? */
244 if ((int_par(tracing_ifs_code) > 0) && (int_par(tracing_commands_code) <= 1))
245 show_cur_cmd_chr();
246 push_condition_stack();
247 save_cond_ptr = cond_ptr;
248 is_unless = (cur_chr >= unless_code);
249 this_if = cur_chr % unless_code;
250 /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */
251 switch (this_if) {
252 case if_char_code:
253 case if_cat_code:
254 /* Test if two characters match */
255 get_x_token_or_active_char();
256 if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
257 /*not a character */
258 m = relax_cmd;
259 n = too_big_char;
260 } else {
261 m = cur_cmd;
262 n = cur_chr;
264 get_x_token_or_active_char();
265 if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
266 cur_cmd = relax_cmd;
267 cur_chr = too_big_char;
269 if (this_if == if_char_code)
270 b = (n == cur_chr);
271 else
272 b = (m == cur_cmd);
273 break;
274 case if_int_code:
275 case if_dim_code:
276 case if_abs_dim_code:
277 case if_abs_num_code:
278 /* Test relation between integers or dimensions */
279 /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII codes. */
280 if (this_if == if_int_code || this_if == if_abs_num_code)
281 scan_int();
282 else
283 scan_normal_dimen();
284 n = cur_val;
285 if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
286 negate(n);
287 /* Get the next non-blank non-call... */
288 do {
289 get_x_token();
290 } while (cur_cmd == spacer_cmd);
292 if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) {
293 r = cur_tok - other_token;
294 } else {
296 r = cur_tok - other_token;
297 if ((r < '<') || (r > '>')) {
298 print_err("Missing = inserted for ");
299 print_cmd_chr(if_test_cmd, this_if);
300 help1("I was expecting to see `<', `=', or `>'. Didn't.");
301 back_error();
302 r = '=';
304 if (this_if == if_int_code || this_if == if_abs_num_code)
305 scan_int();
306 else
307 scan_normal_dimen();
308 if ((cur_val < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
309 negate(cur_val);
310 switch (r) {
311 case '<':
312 b = (n < cur_val);
313 break;
314 case '=':
315 b = (n == cur_val);
316 break;
317 case '>':
318 b = (n > cur_val);
319 break;
320 default:
321 /* can't happen */
322 b = false;
323 break;
325 break;
326 case if_odd_code:
327 /* Test if an integer is odd */
328 scan_int();
329 b = odd(cur_val);
330 break;
331 case if_vmode_code:
332 b = (abs(cur_list.mode_field) == vmode);
333 break;
334 case if_hmode_code:
335 b = (abs(cur_list.mode_field) == hmode);
336 break;
337 case if_mmode_code:
338 b = (abs(cur_list.mode_field) == mmode);
339 break;
340 case if_inner_code:
341 b = (cur_list.mode_field < 0);
342 break;
344 case if_void_code:
345 case if_hbox_code:
346 case if_vbox_code:
347 scan_register_num();
348 p = box(cur_val);
349 if (this_if == if_void_code)
350 b = (p == null);
351 else if (p == null)
352 b = false;
353 else if (this_if == if_hbox_code)
354 b = (type(p) == hlist_node);
355 else
356 b = (type(p) == vlist_node);
357 break;
359 case if_void_code:
360 scan_register_num();
361 p = box(cur_val);
362 b = (p == null);
363 break;
364 case if_hbox_code:
365 scan_register_num();
366 p = box(cur_val);
367 b = (p != null) && (type(p) == hlist_node);
368 break;
369 case if_vbox_code:
370 scan_register_num();
371 p = box(cur_val);
372 b = (p != null) && (type(p) == vlist_node);
373 break;
374 case ifx_code:
376 Test if two tokens match
378 Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
379 or \\{outer} and the other isn't, even though the texts of the macros are
380 the same.
382 We need to reset |scanner_status|, since \.{\\outer} control sequences
383 are allowed, but we might be scanning a macro definition or preamble.
385 save_scanner_status = scanner_status;
386 scanner_status = normal;
387 get_next();
388 n = cur_cs;
389 p = cur_cmd;
390 q = cur_chr;
391 get_next();
392 if (cur_cmd != p) {
393 b = false;
394 } else if (cur_cmd < call_cmd) {
395 b = (cur_chr == q);
396 } else {
398 Test if two macro texts match
400 Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
401 different in examples like this:
403 $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
404 {}\\def\\a\{\\c\}&
405 {}\\def\\c\{\}\cr
406 {}\\def\\b\{\\d\}&
407 {}\\def\\d\{\}\cr}}$$
409 p = token_link(cur_chr);
410 /*omit reference counts */
411 q = token_link(equiv(n));
412 if (p == q) {
413 b = true;
414 } else {
415 while ((p != null) && (q != null)) {
416 if (token_info(p) != token_info(q)) {
417 p = null;
418 break;
419 } else {
420 p = token_link(p);
421 q = token_link(q);
424 b = ((p == null) && (q == null));
427 scanner_status = save_scanner_status;
428 break;
429 case if_eof_code:
430 scan_four_bit_int();
431 b = (read_open[cur_val] == closed);
432 break;
433 case if_true_code:
434 b = true;
435 break;
436 case if_false_code:
437 b = false;
438 break;
439 case if_case_code:
440 /* Select the appropriate case and |return| or |goto common_ending| */
441 scan_int();
442 /* |n| is the number of cases to pass */
443 n = cur_val;
444 if (int_par(tracing_commands_code) > 1) {
445 begin_diagnostic();
446 tprint("{case ");
447 print_int(n);
448 print_char('}');
449 end_diagnostic(false);
451 while (n != 0) {
452 pass_text();
453 if (cond_ptr == save_cond_ptr) {
454 if (cur_chr == or_code)
455 decr(n);
456 else
457 goto COMMON_ENDING;
458 } else if (cur_chr == fi_code) {
459 pop_condition_stack();
462 change_if_limit(or_code, save_cond_ptr);
463 /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */
464 return;
465 break;
466 case if_primitive_code:
467 save_scanner_status = scanner_status;
468 scanner_status = normal;
469 get_next();
470 scanner_status = save_scanner_status;
471 m = prim_lookup(cs_text(cur_cs));
472 b = ((cur_cmd != undefined_cs_cmd) &&
473 (m != undefined_primitive) &&
474 (cur_cmd == get_prim_eq_type(m)) &&
475 (cur_chr == get_prim_equiv(m)));
476 break;
477 case if_def_code:
479 The conditional \.{\\ifdefined} tests if a control sequence is defined.
480 We need to reset |scanner_status|, since \.{\\outer} control sequences
481 are allowed, but we might be scanning a macro definition or preamble.
483 save_scanner_status = scanner_status;
484 scanner_status = normal;
485 get_next();
486 b = (cur_cmd != undefined_cs_cmd);
487 scanner_status = save_scanner_status;
488 break;
489 case if_cs_code:
490 b = test_for_cs();
491 break;
492 case if_in_csname_code:
493 b = is_in_csname;
494 break;
495 case if_font_char_code:
497 The conditional \.{\\iffontchar} tests the existence of a character in
498 a font.
500 scan_font_ident();
501 n = cur_val;
502 scan_char_num();
503 b = char_exists(n, cur_val);
504 break;
505 default:
506 /* there are no other cases, but for -Wall: */
507 b = false;
509 if (is_unless)
510 b = !b;
511 if (int_par(tracing_commands_code) > 1) {
512 /* Display the value of |b| */
513 begin_diagnostic();
514 if (b)
515 tprint("{true}");
516 else
517 tprint("{false}");
518 end_diagnostic(false);
520 if (b) {
521 change_if_limit(else_code, save_cond_ptr);
522 /*wait for \.{\\else} or \.{\\fi} */
523 return;
526 Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|
528 In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
529 \.{\\else} that we come to after learning that the \.{\\if} is false is
530 not the \.{\\else} we're looking for. Hence the following curious
531 logic is needed.
533 while (1) {
534 pass_text();
535 if (cond_ptr == save_cond_ptr) {
536 if (cur_chr != or_code)
537 goto COMMON_ENDING;
538 print_err("Extra \\or");
539 help1("I'm ignoring this; it doesn't match any \\if.");
540 error();
541 } else if (cur_chr == fi_code) {
542 pop_condition_stack();
545 COMMON_ENDING:
546 if (cur_chr == fi_code) {
547 pop_condition_stack();
548 } else {
549 /*wait for \.{\\fi} */
550 if_limit = fi_code;