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