diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2010-04-04 13:55:46 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2010-04-04 13:55:46 +0000 |
commit | f372862c42d234710b6057449b46818e63181c90 (patch) | |
tree | a9ebec66dd31ac37aed442502ebd1b9fe76245ec /Build/source/texk/web2c/luatexdir/tex/conditional.w | |
parent | 2dad291ab056f30816e0aea61970f38033c2f6a5 (diff) |
import luatex 0.60.0 (with small local changes, see luatexdir/ChangeLog)
and do an autoreconf
git-svn-id: svn://tug.org/texlive/trunk@17680 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/conditional.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/conditional.w | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.w b/Build/source/texk/web2c/luatexdir/tex/conditional.w new file mode 100644 index 00000000000..dd9349ab5ef --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.w @@ -0,0 +1,539 @@ +% conditional.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: conditional.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/conditional.w $"; + +@ @c +#define box(A) eqtb[box_base+(A)].hh.rh + +@* We consider now the way \TeX\ handles various kinds of \.{\\if} commands. + +@ Conditions can be inside conditions, and this nesting has a stack +that is independent of the |save_stack|. + +Four global variables represent the top of the condition stack: +|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies +the largest code of a |fi_or_else| command that is syntactically legal; +|cur_if| is the name of the current type of conditional; and |if_line| +is the line number at which it began. + +If no conditions are currently in progress, the condition stack has the +special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. +Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and +|link| fields of the first word contain |if_limit|, |cur_if|, and +|cond_ptr| at the next level, and the second word contains the +corresponding |if_line|. + +@c +halfword cond_ptr; /* top of the condition stack */ +int if_limit; /* upper bound on |fi_or_else| codes */ +int cur_if; /* type of conditional being worked on */ +int if_line; /* line where that conditional began */ + + +@ When we skip conditional text, we keep track of the line number +where skipping began, for use in error messages. + +@c +int skip_line; /* skipping began here */ + + +@ Here is a procedure that ignores text until coming to an \.{\\or}, +\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ +nesting. After it has acted, |cur_chr| will indicate the token that +was found, but |cur_tok| will not be set (because this makes the +procedure run faster). + +@c +void pass_text(void) +{ + int l; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */ + int save_scanner_status; /* |scanner_status| upon entry */ + save_scanner_status = scanner_status; + scanner_status = skipping; + l = 0; + skip_line = line; + while (1) { + get_token_lua(); + if (cur_cmd == fi_or_else_cmd) { + if (l == 0) + break; + if (cur_chr == fi_code) + decr(l); + } else if (cur_cmd == if_test_cmd) { + incr(l); + } + } + scanner_status = save_scanner_status; + if (int_par(tracing_ifs_code) > 0) + show_cur_cmd_chr(); +} + + +@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then +if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} +condition has been evaluated, \.{\\relax} will be inserted. +For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}' +would otherwise require something after the `\.1'. + +@c +void push_condition_stack(void) +{ + halfword p = new_node(if_node, 0); + vlink(p) = cond_ptr; + if_limit_type(p) = (quarterword) if_limit; + if_limit_subtype(p) = (quarterword) cur_if; + if_line_field(p) = if_line; + cond_ptr = p; + cur_if = cur_chr; + if_limit = if_code; + if_line = line; +} + +void pop_condition_stack(void) +{ + halfword p; + if (if_stack[in_open] == cond_ptr) + if_warning(); + /* conditionals possibly not properly nested with files */ + p = cond_ptr; + if_line = if_line_field(p); + cur_if = if_limit_subtype(p); + if_limit = if_limit_type(p); + cond_ptr = vlink(p); + flush_node(p); +} + + +@ Here's a procedure that changes the |if_limit| code corresponding to +a given value of |cond_ptr|. + +@c +void change_if_limit(int l, halfword p) +{ + halfword q; + if (p == cond_ptr) { + if_limit = l; /* that's the easy case */ + } else { + q = cond_ptr; + while (1) { + if (q == null) + confusion("if"); + if (vlink(q) == p) { + if_limit_type(q) = (quarterword) l; + return; + } + q = vlink(q); + } + } +} + + +@ The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter} +\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new +control sequence will be entered into the hash table (once all tokens +preceding the mandatory \.{\\endcsname} have been expanded). + +@c +static boolean test_for_cs(void) +{ + boolean b; /*is the condition true? */ + int m, s; /*to be tested against the second operand */ + halfword n, p, q; /*for traversing token lists in \.{\\ifx} tests */ + n = get_avail(); + p = n; /*head of the list of characters */ + b = false; + while (1) { + get_x_token(); + if (cur_cs != 0) + break; + store_new_token(cur_tok); + } + if (cur_cmd != end_cs_name_cmd) { + if (int_par(suppress_ifcsname_error_code)) { + do { + get_x_token(); + } while (cur_cmd != end_cs_name_cmd); + flush_list(n); + return b; + } else { + complain_missing_csname(); + } + } + /* Look up the characters of list |n| in the hash table, and set |cur_cs| */ + m = first; + p = token_link(n); + while (p != null) { + if (m >= max_buf_stack) { + max_buf_stack = m + 4; + if (max_buf_stack == buf_size) + check_buffer_overflow(max_buf_stack); + } + s = token_chr(token_info(p)); + if (s <= 0x7F) { + buffer[m++] = (packed_ASCII_code) s; + } else if (s <= 0x7FF) { + buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40); + } else if (s <= 0xFFFF) { + buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40); + } else { + buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000); + buffer[m++] = + (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40); + buffer[m++] = + (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40); + } + p = token_link(p); + } + if (m > first) { + cur_cs = id_lookup(first, m - first); /*|no_new_control_sequence| is |true| */ + } else if (m == first) { + cur_cs = null_cs; /*the list is empty */ + } + b = (eq_type(cur_cs) != undefined_cs_cmd); + flush_list(n); + return b; +} + + +@ An active character will be treated as category 13 following +\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. + +@c +#define get_x_token_or_active_char() do { \ + get_x_token(); \ + if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \ + if (is_active_cs(cs_text(cur_cs))) { \ + cur_cmd=active_char_cmd; \ + cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \ + } \ + } \ + } while (0) + + + + + +@ A condition is started when the |expand| procedure encounters +an |if_test| command; in that case |expand| reduces to |conditional|, +which is a recursive procedure. +@^recursion@> + +@c +void conditional(void) +{ + boolean b; /*is the condition true? */ + int r; /*relation to be evaluated */ + int m, n; /*to be tested against the second operand */ + halfword p, q; /*for traversing token lists in \.{\\ifx} tests */ + int save_scanner_status; /*|scanner_status| upon entry */ + halfword save_cond_ptr; /*|cond_ptr| corresponding to this conditional */ + int this_if; /*type of this conditional */ + boolean is_unless; /*was this if preceded by `\.{\\unless}' ? */ + b = false; /*default is false, just in case */ + if (int_par(tracing_ifs_code) > 0) + if (int_par(tracing_commands_code) <= 1) + show_cur_cmd_chr(); + push_condition_stack(); + save_cond_ptr = cond_ptr; + is_unless = (cur_chr >= unless_code); + this_if = cur_chr % unless_code; + /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */ + switch (this_if) { + case if_char_code: + case if_cat_code: + /* Test if two characters match */ + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { /*not a character */ + m = relax_cmd; + n = too_big_char; + } else { + m = cur_cmd; + n = cur_chr; + } + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { + cur_cmd = relax_cmd; + cur_chr = too_big_char; + } + if (this_if == if_char_code) + b = (n == cur_chr); + else + b = (m == cur_cmd); + break; + case if_int_code: + case if_dim_code: + case if_abs_dim_code: + case if_abs_num_code: + /* Test relation between integers or dimensions */ + /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII + codes. */ + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + n = cur_val; + if (n < 0) + if (this_if == if_abs_dim_code || this_if == if_abs_num_code) + negate(n); + + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) { + r = cur_tok - other_token; + } else { + print_err("Missing = inserted for "); + print_cmd_chr(if_test_cmd, this_if); + help1("I was expecting to see `<', `=', or `>'. Didn't."); + back_error(); + r = '='; + } + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + + if (cur_val < 0) + if (this_if == if_abs_dim_code || this_if == if_abs_num_code) + negate(cur_val); + + switch (r) { + case '<': + b = (n < cur_val); + break; + case '=': + b = (n == cur_val); + break; + case '>': + b = (n > cur_val); + break; + default: + b = false; + break; /*can't happen */ + } + break; + case if_odd_code: + /* Test if an integer is odd */ + scan_int(); + b = odd(cur_val); + break; + case if_vmode_code: + b = (abs(cur_list.mode_field) == vmode); + break; + case if_hmode_code: + b = (abs(cur_list.mode_field) == hmode); + break; + case if_mmode_code: + b = (abs(cur_list.mode_field) == mmode); + break; + case if_inner_code: + b = (cur_list.mode_field < 0); + break; + case if_void_code: + case if_hbox_code: + case if_vbox_code: + /* Test box register status */ + scan_register_num(); + p = box(cur_val); + if (this_if == if_void_code) + b = (p == null); + else if (p == null) + b = false; + else if (this_if == if_hbox_code) + b = (type(p) == hlist_node); + else + b = (type(p) == vlist_node); + break; + case ifx_code: + /* Test if two tokens match */ + /* Note that `\.{\\ifx}' will declare two macros different if one is \\{long} + or \\{outer} and the other isn't, even though the texts of the macros are + the same. + + We need to reset |scanner_status|, since \.{\\outer} control sequences + are allowed, but we might be scanning a macro definition or preamble. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + n = cur_cs; + p = cur_cmd; + q = cur_chr; + get_token_lua(); + if (cur_cmd != p) { + b = false; + } else if (cur_cmd < call_cmd) { + b = (cur_chr == q); + } else { + /* Test if two macro texts match */ + /* Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are + different in examples like this: + $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr + {}\\def\\a\{\\c\}& + {}\\def\\c\{\}\cr + {}\\def\\b\{\\d\}& + {}\\def\\d\{\}\cr}}$$ */ + p = token_link(cur_chr); + q = token_link(equiv(n)); /*omit reference counts */ + if (p == q) { + b = true; + } else { + while ((p != null) && (q != null)) { + if (token_info(p) != token_info(q)) { + p = null; + } else { + p = token_link(p); + q = token_link(q); + } + } + b = ((p == null) && (q == null)); + } + } + scanner_status = save_scanner_status; + break; + case if_eof_code: + scan_four_bit_int_or_18(); + if (cur_val == 18) + b = !shellenabledp; + else + b = (read_open[cur_val] == closed); + break; + case if_true_code: + b = true; + break; + case if_false_code: + b = false; + break; + case if_case_code: + /* Select the appropriate case + and |return| or |goto common_ending| */ + scan_int(); + n = cur_val; /*|n| is the number of cases to pass */ + if (int_par(tracing_commands_code) > 1) { + begin_diagnostic(); + tprint("{case "); + print_int(n); + print_char('}'); + end_diagnostic(false); + } + while (n != 0) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr == or_code) + decr(n); + else + goto COMMON_ENDING; + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + change_if_limit(or_code, save_cond_ptr); + return; /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */ + break; + case if_primitive_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + scanner_status = save_scanner_status; + m = prim_lookup(cs_text(cur_cs)); + b = ((cur_cmd != undefined_cs_cmd) && + (m != undefined_primitive) && + (cur_cmd == get_prim_eq_type(m)) && + (cur_chr == get_prim_equiv(m))); + break; + case if_def_code: + /* The conditional \.{\\ifdefined} tests if a control sequence is defined. */ + /* We need to reset |scanner_status|, since \.{\\outer} control sequences + are allowed, but we might be scanning a macro definition or preamble. */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + b = (cur_cmd != undefined_cs_cmd); + scanner_status = save_scanner_status; + break; + case if_cs_code: + b = test_for_cs(); + break; + case if_in_csname_code: + b = is_in_csname; + break; + case if_font_char_code: + /* The conditional \.{\\iffontchar} tests the existence of a character in + a font. */ + scan_font_ident(); + n = cur_val; + scan_char_num(); + b = char_exists(n, cur_val); + break; + default: /* there are no other cases, but for -Wall: */ + b = false; + } + + if (is_unless) + b = !b; + if (int_par(tracing_commands_code) > 1) { + /* Display the value of |b| */ + begin_diagnostic(); + if (b) + tprint("{true}"); + else + tprint("{false}"); + end_diagnostic(false); + } + if (b) { + change_if_limit(else_code, save_cond_ptr); + return; /*wait for \.{\\else} or \.{\\fi} */ + } + /* Skip to \.{\\else} or \.{\\fi}, then |goto common_ending| */ + /* + In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first + \.{\\else} that we come to after learning that the \.{\\if} is false is + not the \.{\\else} we're looking for. Hence the following curious + logic is needed. + */ + while (1) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr != or_code) + goto COMMON_ENDING; + print_err("Extra \\or"); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + COMMON_ENDING: + if (cur_chr == fi_code) + pop_condition_stack(); + else + if_limit = fi_code; /*wait for \.{\\fi} */ +} |