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
/>.
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
).
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
;
72 if
(cur_cmd
== fi_or_else_cmd
) {
75 if
(cur_chr
== fi_code
)
77 } else if
(cur_cmd
== if_test_cmd
) {
81 scanner_status
= save_scanner_status
;
82 if
(int_par
(tracing_ifs_code
) > 0)
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);
97 if_limit_type
(p
) = (quarterword
) if_limit
;
98 if_limit_subtype
(p
) = (quarterword
) cur_if
;
99 if_line_field
(p
) = if_line
;
106 void pop_condition_stack
(void
)
109 if
(if_stack
[in_open
] == cond_ptr
)
111 /* conditionals possibly not properly nested with files
*/
113 if_line
= if_line_field
(p
);
114 cur_if
= if_limit_subtype
(p
);
115 if_limit
= if_limit_type
(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
)
127 /* that's the easy case
*/
130 halfword q
= cond_ptr
;
135 if_limit_type
(q
) = (quarterword
) l
;
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
*/
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
)) {
169 } while
(cur_cmd
!= end_cs_name_cmd
);
173 complain_missing_csname
();
176 /* Look up the characters of list |n| in the hash table
, and set |cur_cs|
*/
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
));
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);
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);
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
);
210 last_cs_name
= cur_cs
;
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
{ \
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
)); \
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.
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))
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
*/
254 /* Test if two characters match
*/
255 get_x_token_or_active_char
();
256 if
((cur_cmd
> active_char_cmd
) ||
(cur_chr
> biggest_char
)) {
264 get_x_token_or_active_char
();
265 if
((cur_cmd
> active_char_cmd
) ||
(cur_chr
> biggest_char
)) {
267 cur_chr
= too_big_char
;
269 if
(this_if
== if_char_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
)
285 if
((n
< 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
287 /* Get the next non-blank non-call...
*/
290 } while
(cur_cmd
== spacer_cmd
);
292 if
((cur_tok
>= other_token
+ '
<'
) && (cur_tok <= other_token + '>')) {
293 r
= cur_tok
- other_token
;
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.");
304 if
(this_if
== if_int_code || this_if
== if_abs_num_code
)
308 if
((cur_val
< 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
327 /* Test if an integer is odd
*/
332 b
= (abs
(cur_list.mode_field
) == vmode
);
335 b
= (abs
(cur_list.mode_field
) == hmode
);
338 b
= (abs
(cur_list.mode_field
) == mmode
);
341 b
= (cur_list.mode_field
< 0);
349 if
(this_if
== if_void_code
)
353 else if
(this_if
== if_hbox_code
)
354 b
= (type
(p
) == hlist_node
);
356 b
= (type
(p
) == vlist_node
);
367 b
= (p
!= null
) && (type(p) == hlist_node);
372 b
= (p
!= null
) && (type(p) == vlist_node);
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
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
;
394 } else if
(cur_cmd
< call_cmd
) {
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
407 {}\\def\\d\
{\
}\cr
}}$$
409 p
= token_link
(cur_chr
);
410 /*omit reference counts
*/
411 q
= token_link
(equiv
(n
));
415 while
((p
!= null
) && (q != null)) {
416 if
(token_info
(p
) != token_info
(q
)) {
424 b
= ((p
== null
) && (q == null));
427 scanner_status
= save_scanner_status
;
431 b
= (read_open
[cur_val
] == closed
);
440 /* Select the appropriate case and |return| or |goto common_ending|
*/
442 /* |n| is the number of cases to pass
*/
444 if
(int_par
(tracing_commands_code
) > 1) {
449 end_diagnostic
(false
);
453 if
(cond_ptr
== save_cond_ptr
) {
454 if
(cur_chr
== or_code
)
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
} */
466 case if_primitive_code
:
467 save_scanner_status
= scanner_status
;
468 scanner_status
= normal
;
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
)));
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
;
486 b
= (cur_cmd
!= undefined_cs_cmd
);
487 scanner_status
= save_scanner_status
;
492 case if_in_csname_code
:
495 case if_font_char_code
:
497 The conditional \.
{\\iffontchar
} tests the existence of a character in
503 b
= char_exists
(n
, cur_val
);
506 /* there are no other cases
, but for
-Wall
: */
511 if
(int_par
(tracing_commands_code
) > 1) {
512 /* Display the value of |b|
*/
518 end_diagnostic
(false
);
521 change_if_limit
(else_code
, save_cond_ptr
);
522 /*wait for \.
{\\else
} or \.
{\\fi
} */
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
535 if
(cond_ptr
== save_cond_ptr
) {
536 if
(cur_chr
!= or_code
)
538 print_err
("Extra \\or");
539 help1
("I'm ignoring this; it doesn't match any \\if.");
541 } else if
(cur_chr
== fi_code
) {
542 pop_condition_stack
();
546 if
(cur_chr
== fi_code
) {
547 pop_condition_stack
();
549 /*wait for \.
{\\fi
} */