1
4
5# include "luametatex.h"
6
7
16
17
41
42condition_state_info lmt_condition_state = {
43 .cond_ptr = null,
44 .cur_if = 0,
45 .cur_unless = 0,
46 .if_step = 0,
47 .if_limit = 0,
48 .if_line = 0,
49 .if_nesting = 0,
50 .if_unless = 0,
51 .skip_line = 0,
52 .chk_integer = 0,
53 .chk_dimension = 0,
54 .padding = 0,
55};
56
57
67
68static void tex_aux_pass_text(void)
69{
70 int level = 0;
71 int status = lmt_input_state.scanner_status;
72 lmt_input_state.scanner_status = scanner_is_skipping;
73 lmt_condition_state.skip_line = lmt_input_state.input_line;
74 while (1) {
75 tex_get_next();
76 if (cur_cmd == if_test_cmd) {
77 switch (cur_chr) {
78 case fi_code:
79 if (level == 0) {
80 lmt_input_state.scanner_status = status;
81 return;
82 } else {
83 --level;
84 break;
85 }
86 case else_code:
87 case or_code:
88 if (level == 0) {
89 lmt_input_state.scanner_status = status;
90 return;
91 } else {
92 break;
93 }
94 case or_else_code:
95 case or_unless_code:
96 tex_get_next_non_spacer();
97
98 break;
99 default:
100 ++level;
101 break;
102 }
103 }
104 }
105}
106
107
111
112static int tex_aux_pass_text_x(int tracing_ifs, int tracing_commands)
113{
114 int level = 0;
115 int status = lmt_input_state.scanner_status;
116 lmt_input_state.scanner_status = scanner_is_skipping;
117 lmt_condition_state.skip_line = lmt_input_state.input_line;
118 while (1) {
119 tex_get_next();
120 if (cur_cmd == if_test_cmd) {
121 switch (cur_chr) {
122 case if_code:
123 ++level;
124 break;
125 case fi_code:
126 if (level == 0) {
127 lmt_input_state.scanner_status = status;
128 return 0;
129 } else {
130 --level;
131 break;
132 }
133 case else_code:
134 case or_code:
135 if (level == 0) {
136 lmt_input_state.scanner_status = status;
137 return 0;
138 } else {
139 break;
140 }
141 case or_else_code:
142 case or_unless_code:
143 if (level == 0) {
144 int unless = cur_chr == or_unless_code;
145 if (tracing_commands > 1) {
146 tex_begin_diagnostic();
147 tex_print_str(unless ? "{orunless}" : "{orelse}");
148 tex_end_diagnostic();
149 } else if (tracing_ifs) {
150 tex_show_cmd_chr(cur_cmd, cur_chr);
151 }
152 tex_get_next_non_spacer();
153 if (lmt_condition_state.if_limit == if_code) {
154 if (cur_cmd == if_test_cmd && cur_chr >= first_real_if_test_code) {
155
156 } else {
157 tex_handle_error(
158 normal_error_type,
159 unless ? "No condition after \\orunless" : "No condition after \\orelse",
160 "I'd expected a proper if test command."
161 );
162 }
163 lmt_input_state.scanner_status = status;
164 return unless;
165 }
166 } else {
167 --level;
168 }
169 break;
170 default:
171 ++level;
172 break;
173 }
174 }
175 }
176}
177
178
190
191static void tex_aux_if_warning(void)
192{
193
194 bool warning = false;
195 int index = lmt_input_state.in_stack_data.ptr;
196 lmt_input_state.base_ptr = lmt_input_state.input_stack_data.ptr;
197
198 lmt_input_state.input_stack[lmt_input_state.base_ptr] = lmt_input_state.cur_input;
199 while (lmt_input_state.in_stack[index].if_ptr == lmt_condition_state.cond_ptr) {
200
201 if (tracing_nesting_par > 0) {
202 while ((lmt_input_state.input_stack[lmt_input_state.base_ptr].state == token_list_state) || (lmt_input_state.input_stack[lmt_input_state.base_ptr].index > index)) {
203 --lmt_input_state.base_ptr;
204 }
205 if (lmt_input_state.input_stack[lmt_input_state.base_ptr].name > 17) {
206 warning = true;
207 }
208 }
209 lmt_input_state.in_stack[index].if_ptr = node_next(lmt_condition_state.cond_ptr);
210 --index;
211 }
212 if (warning) {
213 tex_begin_diagnostic();
214 tex_print_format("[conditional: end of %C%L of a different file]", if_test_cmd, lmt_condition_state.cur_if, lmt_condition_state.if_line);
215 tex_end_diagnostic();
216 if (tracing_nesting_par > 1) {
217 tex_show_context();
218 }
219 if (lmt_error_state.history == spotless) {
220 lmt_error_state.history = warning_issued;
221 }
222 }
223}
224
225
229
230static void tex_aux_push_condition_stack(int code, int unless)
231{
232 halfword p = tex_get_node(if_node_size);
233 node_type(p) = if_node;
234 node_subtype(p) = 0;
235 node_next(p) = lmt_condition_state.cond_ptr;
236 if_limit_type(p) = (quarterword) lmt_condition_state.if_limit;
237 if_limit_subtype(p) = (quarterword) lmt_condition_state.cur_if;
238 if_limit_step(p) = (singleword) lmt_condition_state.cur_unless;
239 if_limit_unless(p) = (singleword) lmt_condition_state.if_unless;
240 if_limit_stepunless(p) = (singleword) lmt_condition_state.if_unless;
241 if_limit_line(p) = lmt_condition_state.if_line;
242 lmt_condition_state.cond_ptr = p;
243 lmt_condition_state.cur_if = cur_chr;
244 lmt_condition_state.cur_unless = unless;
245 lmt_condition_state.if_step = code;
246 lmt_condition_state.if_limit = if_code;
247 lmt_condition_state.if_line = lmt_input_state.input_line;
248 ++lmt_condition_state.if_nesting;
249}
250
251static void tex_aux_pop_condition_stack(void)
252{
253 halfword p;
254 if (lmt_input_state.in_stack[lmt_input_state.in_stack_data.ptr].if_ptr == lmt_condition_state.cond_ptr) {
255
259 tex_aux_if_warning();
260 }
261 p = lmt_condition_state.cond_ptr;
262 --lmt_condition_state.if_nesting;
263 lmt_condition_state.if_line = if_limit_line(p);
264 lmt_condition_state.cur_if = if_limit_subtype(p);
265 lmt_condition_state.cur_unless = if_limit_unless(p);
266 lmt_condition_state.if_step = if_limit_step(p);
267 lmt_condition_state.if_unless = if_limit_stepunless(p);
268 lmt_condition_state.if_limit = if_limit_type(p);
269 lmt_condition_state.cond_ptr = node_next(p);
270 tex_free_node(p, if_node_size);
271}
272
273
279
280
284
285inline static void tex_aux_change_if_limit(int l, halfword p)
286{
287 if (p == lmt_condition_state.cond_ptr) {
288 lmt_condition_state.if_limit = l;
289 } else {
290 halfword q = lmt_condition_state.cond_ptr;
291 while (q) {
292 if (node_next(q) == p) {
293 if_limit_type(q) = (quarterword) l;
294 return;
295 } else {
296 q = node_next(q);
297 }
298 }
299 tex_confusion("if");
300 }
301}
302
303
314
315
316
317
323
324static void tex_aux_get_x_token_or_active_char(void)
325{
326 tex_get_x_token();
327
328 if (cur_cmd == relax_cmd && cur_chr == no_expand_relax_code && tex_is_active_cs(cs_text(cur_cs))) {
329 cur_cmd = active_char_cmd;
330 cur_chr = active_cs_value(cs_text(cur_tok - cs_token_flag));
331 }
332}
333
334
340
341static void tex_aux_missing_equal_error(int code)
342{
343 tex_handle_error(back_error_type, "Missing = inserted for %C", if_test_cmd, code,
344 "I was expecting to see '<', '=', or '>'. Didn't."
345 );
346}
347
348
363
364static void tex_aux_show_if_state(halfword code, halfword case_value)
365{
366 tex_begin_diagnostic();
367 switch (code) {
368 case if_chk_int_code : tex_print_format("{chknum %i}", case_value); break;
369 case if_val_int_code : tex_print_format("{numval %i}", case_value); break;
370 case if_cmp_int_code : tex_print_format("{cmpnum %i}", case_value); break;
371 case if_chk_dim_code : tex_print_format("{chkdim %i}", case_value); break;
372 case if_val_dim_code : tex_print_format("{dimval %i}", case_value); break;
373 case if_cmp_dim_code : tex_print_format("{cmpdim %i}", case_value); break;
374 case if_case_code : tex_print_format("{case %i}", case_value); break;
375 case if_math_parameter_code: tex_print_format("{mathparameter %i}", case_value); break;
376 case if_math_style_code : tex_print_format("{mathstyle %i}", case_value); break;
377 case if_arguments_code : tex_print_format("{arguments %i}", case_value); break;
378 case if_parameters_code : tex_print_format("{parameter %i}", case_value); break;
379 case if_parameter_code : tex_print_format("{parameters %i}", case_value); break;
380 default : tex_print_format("{todo %i}", case_value); break;
381 }
382 tex_end_diagnostic();
383}
384
385
386
387inline static halfword tex_aux_grab_toks(int expand, int expandlist, int *head)
388{
389 halfword p = null;
390 if (expand) {
391 do {
392 tex_get_x_token();
393 } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
394 } else {
395 do {
396 tex_get_token();
397 } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
398 }
399 switch (cur_cmd) {
400 case left_brace_cmd:
401 p = expandlist ? tex_scan_toks_expand(1, NULL, 0, 0) : tex_scan_toks_normal(1, NULL);
402 *head = p;
403 break;
404 case internal_toks_cmd:
405 case register_toks_cmd:
406 p = eq_value(cur_chr);
407 break;
408 case register_cmd:
409
410 if (cur_chr == token_val_level) {
411 halfword n = tex_scan_toks_register_number();
412 p = eq_value(register_toks_location(n));
413 break;
414 } else {
415 goto DEFAULT;
416 }
417 case cs_name_cmd:
418 if (cur_chr == last_named_cs_code) {
419 if (lmt_scanner_state.last_cs_name != null_cs) {
420 p = eq_value(lmt_scanner_state.last_cs_name);
421 }
422 break;
423 } else {
424
425 }
426 case call_cmd:
427 case protected_call_cmd:
428 case semi_protected_call_cmd:
429 case constant_call_cmd:
430 case tolerant_call_cmd:
431 case tolerant_protected_call_cmd:
432 case tolerant_semi_protected_call_cmd:
433 p = eq_value(cur_cs);
434 break;
435 default:
436 DEFAULT:
437 {
438 halfword n;
439 tex_back_input(cur_tok);
440 n = tex_scan_toks_register_number();
441 p = eq_value(register_toks_location(n));
442 break;
443 }
444 }
445
446 return p ? token_link(p) : null;
447}
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477typedef enum comparisons {
478
479 comparison_equal = 0,
480 comparison_less = 1,
481 comparison_greater = 2,
482 comparison_not_equal = 3,
483 comparison_not_less = 4,
484 comparison_not_greater = 5,
485 comparison_element = 6,
486 comparison_not_element = 7,
487} comparisons;
488
489typedef enum values {
490
491 value_less = 1,
492 value_equal = 2,
493 value_greater = 3,
494 value_error = 4,
495} values;
496
497typedef enum checks {
498
499 check_okay = 1,
500 check_error = 2,
501} checks;
502
503typedef enum parameterstates {
504 parameter_zero = 0,
505 parameter_set = 1,
506 parameter_unset = 2,
507} parameterstates;
508
509inline static halfword tex_aux_scan_comparison(int code)
510{
511 bool negate = false;
512 while (1) {
513 tex_get_x_token();
514 switch (cur_cmd) {
515 case letter_cmd:
516 case other_char_cmd:
517 switch (cur_chr) {
518
519 case '=' : return negate ? comparison_not_equal : comparison_equal;
520 case '<' : return negate ? comparison_not_less : comparison_less;
521 case '>' : return negate ? comparison_not_greater : comparison_greater;
522
523 case '!' : negate = ! negate ; continue;
524
525 case 0x2208: return negate ? comparison_not_element : comparison_element;
526 case 0x2209: return negate ? comparison_element : comparison_not_element;
527 case 0x2260: return negate ? comparison_equal : comparison_not_equal;
528 case 0x2264: return negate ? comparison_greater : comparison_not_greater;
529 case 0x2265: return negate ? comparison_less : comparison_not_less;
530 case 0x2270: return negate ? comparison_greater : comparison_not_greater;
531 case 0x2271: return negate ? comparison_less : comparison_not_less;
532 }
533 case spacer_cmd:
534 continue;
535 default:
536 tex_aux_missing_equal_error(code);
537 return 0;
538 }
539 }
540}
541
542inline static void tex_aux_check_strict(int *result)
543{
544 tex_get_x_token();
545 switch (cur_cmd) {
546 case relax_cmd:
547 case spacer_cmd:
548 case if_test_cmd:
549 break;
550 default:
551 *result = check_error;
552 break;
553 }
554 tex_back_input(cur_tok);
555}
556
557void tex_conditional_if(halfword code, int unless)
558{
559
560 int result = 0;
561
562 halfword save_cond_ptr;
563
564 int tracing_ifs = tracing_ifs_par > 0;
565 int tracing_commands = tracing_commands_par;
566 int tracing_both = tracing_ifs && (tracing_commands <= 1);
567 if (tracing_both) {
568 tex_show_cmd_chr(cur_cmd, cur_chr);
569 }
570 tex_aux_push_condition_stack(code, unless);
571 save_cond_ptr = lmt_condition_state.cond_ptr;
572
573 HERE:
574
575 lmt_condition_state.if_step = code;
576 lmt_condition_state.if_unless = unless;
577 switch (code) {
578 case if_char_code:
579 case if_cat_code:
580
581 {
582 halfword n, m;
583 tex_aux_get_x_token_or_active_char();
584 if ((cur_cmd > active_char_cmd) || (cur_chr > max_character_code)) {
585
586 m = relax_cmd;
587 n = relax_code;
588 } else {
589 m = cur_cmd;
590 n = cur_chr;
591 }
592 tex_aux_get_x_token_or_active_char();
593 if ((cur_cmd > active_char_cmd) || (cur_chr > max_character_code)) {
594 cur_cmd = relax_cmd;
595 cur_chr = relax_code;
596 }
597 result = code == if_char_code ? (n == cur_chr) : (m == cur_cmd);
598 }
599 goto RESULT;
600 case if_int_code:
601 case if_abs_int_code:
602 {
603 halfword n1 = tex_scan_integer(0, NULL);
604 halfword cp = tex_aux_scan_comparison(code);
605 halfword n2 = tex_scan_integer(0, NULL);
606 if (code == if_abs_int_code) {
607 if (n1 < 0) {
608 n1 = -n1;
609 }
610 if (n2 < 0) {
611 n2 = -n2;
612 }
613 }
614 switch (cp) {
615 case comparison_equal : result = (n1 == n2); break;
616 case comparison_less : result = (n1 < n2); break;
617 case comparison_greater : result = (n1 > n2); break;
618 case comparison_not_equal : result = (n1 != n2); break;
619 case comparison_not_less : result = (n1 >= n2); break;
620 case comparison_not_greater: result = (n1 <= n2); break;
621 case comparison_element : result = (n1 & n2) == n1; break;
622 case comparison_not_element: result = (n1 & n2) != n1; break;
623 }
624 }
625 goto RESULT;
626 case if_zero_int_code:
627 result = tex_scan_integer(0, NULL) == 0;
628 goto RESULT;
629 case if_interval_int_code:
630 {
631 scaled n0 = tex_scan_integer(0, NULL);
632 scaled n1 = tex_scan_integer(0, NULL);
633 scaled n2 = tex_scan_integer(0, NULL);
634 result = n1 - n2;
635 result = result == 0 ? 1 : (result > 0 ? result <= n0 : -result <= n0);
636 }
637 goto RESULT;
638 case if_posit_code:
639 case if_abs_posit_code:
640 {
641 halfword n1 = tex_scan_posit(0);
642 halfword cp = tex_aux_scan_comparison(code);
643 halfword n2 = tex_scan_posit(0);
644 if (code == if_abs_posit_code) {
645 tex_posit zero = tex_integer_to_posit(0);
646 if (tex_posit_lt(n1,zero.v)) {
647 n1 = tex_posit_neg(n1);
648 }
649 if (tex_posit_lt(n2,zero.v)) {
650 n2 = tex_posit_neg(n2);
651 }
652 }
653 switch (cp) {
654 case comparison_equal : result = tex_posit_eq(n1,n2); break;
655 case comparison_less : result = tex_posit_lt(n1,n2); break;
656 case comparison_greater : result = tex_posit_gt(n1,n2); break;
657 case comparison_not_equal : result = tex_posit_ne(n1,n2); break;
658 case comparison_not_less : result = tex_posit_gt(n1,n2); break;
659 case comparison_not_greater: result = tex_posit_lt(n1,n2); break;
660 case comparison_element : result = tex_posit_eq(tex_integer_to_posit(tex_posit_to_integer(n1) & tex_posit_to_integer(n2)).v,n1); break;
661 case comparison_not_element: result = tex_posit_ne(tex_integer_to_posit(tex_posit_to_integer(n1) & tex_posit_to_integer(n2)).v,n1); break;
662 }
663 }
664 goto RESULT;
665 case if_zero_posit_code:
666 result = tex_posit_eq_zero(tex_scan_posit(0));
667 goto RESULT;
668 case if_interval_posit_code:
669 {
670 halfword n0 = tex_scan_posit(0);
671 halfword n1 = tex_scan_posit(0);
672 halfword n2 = tex_scan_posit(0);
673 result = tex_posit_sub(n1, n2);
674 result = tex_posit_eq_zero(result) ? 1 : (tex_posit_gt_zero(result) ? tex_posit_le(result, n0) : tex_posit_le(tex_posit_neg(result), n0));
675 }
676 goto RESULT;
677 case if_dim_code:
678 case if_abs_dim_code:
679 {
680 scaled n1 = tex_scan_dimension(0, 0, 0, 0, NULL);
681 halfword cp = tex_aux_scan_comparison(code);
682 scaled n2 = tex_scan_dimension(0, 0, 0, 0, NULL);
683 if (code == if_abs_dim_code) {
684 if (n1 < 0) {
685 n1 = -n1;
686 }
687 if (n2 < 0) {
688 n2 = -n2;
689 }
690 }
691 switch (cp) {
692 case comparison_equal : result = (n1 == n2); break;
693 case comparison_less : result = (n1 < n2); break;
694 case comparison_greater : result = (n1 > n2); break;
695 case comparison_not_equal : result = (n1 != n2); break;
696 case comparison_not_less : result = (n1 >= n2); break;
697 case comparison_not_greater: result = (n1 <= n2); break;
698 case comparison_element : result = (n1/65536 & n2/65536) == n1/65536; break;
699 case comparison_not_element: result = (n1/65536 & n2/65536) != n1/65536; break;
700 }
701 }
702 goto RESULT;
703 case if_zero_dim_code:
704 result = tex_scan_dimension(0, 0, 0, 0, NULL) == 0;
705 goto RESULT;
706 case if_interval_dim_code:
707 {
708 scaled n0 = tex_scan_dimension(0, 0, 0, 0, NULL);
709 scaled n1 = tex_scan_dimension(0, 0, 0, 0, NULL);
710 scaled n2 = tex_scan_dimension(0, 0, 0, 0, NULL);
711 result = n1 - n2;
712 result = result == 0 ? 1 : (result > 0 ? result <= n0 : -result <= n0);
713 }
714 goto RESULT;
715 case if_odd_code:
716 result = odd(tex_scan_integer(0, NULL));
717 goto RESULT;
718 case if_vmode_code:
719 result = is_v_mode(cur_list.mode);
720 goto RESULT;
721 case if_hmode_code:
722 result = is_h_mode(cur_list.mode);
723 goto RESULT;
724 case if_mmode_code:
725 result = is_m_mode(cur_list.mode);
726 goto RESULT;
727 case if_inner_code:
728 result = cur_list.mode < nomode;
729 goto RESULT;
730 case if_void_code:
731 {
732 halfword n = tex_scan_box_register_number();
733 result = box_register(n) == null;
734 }
735 goto RESULT;
736 case if_hbox_code:
737 {
738 halfword n = tex_scan_box_register_number();
739 halfword p = box_register(n);
740 result = p && (node_type(p) == hlist_node);
741 }
742 goto RESULT;
743 case if_vbox_code:
744 {
745 halfword n = tex_scan_box_register_number();
746 halfword p = box_register(n);
747 result = p && (node_type(p) == vlist_node);
748 }
749 goto RESULT;
750 case if_tok_code:
751 case if_cstok_code:
752 {
753 halfword pp = null;
754 halfword qq = null;
755 halfword p, q;
756 int expand = code == if_tok_code;
757 int save_scanner_status = lmt_input_state.scanner_status;
758 lmt_input_state.scanner_status = scanner_is_normal;
759 p = tex_aux_grab_toks(expand, 1, &pp);
760 q = tex_aux_grab_toks(expand, 1, &qq);
761 if (p == q) {
762
763 result = 1;
764 } else {
765 while (p && q) {
766 if (token_info(p) != token_info(q)) {
767
768
769 result = 0;
770 goto IFTOKDONE;
771 } else {
772 p = token_link(p);
773 q = token_link(q);
774 }
775 }
776 result = (! p) && (! q);
777 }
778 IFTOKDONE:
779 if (pp) {
780 tex_flush_token_list(pp);
781 }
782 if (qq) {
783 tex_flush_token_list(qq);
784 }
785 lmt_input_state.scanner_status = save_scanner_status;
786 }
787 goto RESULT;
788 case if_last_named_cs_code:
789 case if_x_code:
790 {
791
802
803 halfword p, q;
804 int save_scanner_status = lmt_input_state.scanner_status;
805 lmt_input_state.scanner_status = scanner_is_normal;
806 if (code == if_x_code) {
807 tex_get_next();
808
809 p = cur_cmd;
810 q = cur_chr;
811 } else {
812
813 p = eq_type(lmt_scanner_state.last_cs_name);
814 q = eq_value(lmt_scanner_state.last_cs_name);
815 }
816 tex_get_next();
817 if ((p == constant_call_cmd && cur_cmd == call_cmd) || (p == call_cmd && cur_cmd == constant_call_cmd)) {
818
819 goto SOMECALLCMD;
820 } else if (cur_cmd != p) {
821 result = 0;
822 } else if (cur_cmd < call_cmd) {
823 result = cur_chr == q;
824 } else {
825 SOMECALLCMD:
826
838 p = token_link(cur_chr);
839
840
841 q = token_link(q);
842 if (p == q) {
843 result = 1;
844
848 } else {
849 while (p && q) {
850 if (token_info(p) != token_info(q)) {
851
852
853 result = 0;
854 goto IFXDONE;
855 } else {
856 p = token_link(p);
857 q = token_link(q);
858 }
859 }
860 result = (! p) && (! q);
861 }
862 }
863 IFXDONE:
864 lmt_input_state.scanner_status = save_scanner_status;
865 }
866 goto RESULT;
867 case if_true_code:
868 result = 1;
869 goto RESULT;
870 case if_false_code:
871 result = 0;
872 goto RESULT;
873 case if_chk_int_code:
874 {
875 lmt_error_state.intercept = 1;
876 lmt_error_state.last_intercept = 0;
877 lmt_condition_state.chk_integer = 0;
878 tex_scan_integer_validate();
879 result = lmt_error_state.last_intercept ? check_error : check_okay;
880 lmt_error_state.intercept = 0;
881 lmt_error_state.last_intercept = 0;
882 goto CASE;
883 }
884 case if_chk_integer_code:
885 {
886 lmt_error_state.intercept = 1;
887 lmt_error_state.last_intercept = 0;
888 lmt_condition_state.chk_integer = tex_scan_integer(0, NULL);
889 result = lmt_error_state.last_intercept ? check_error : check_okay;
890 if (result == check_okay) {
891 tex_aux_check_strict(&result);
892 }
893 lmt_error_state.intercept = 0;
894 lmt_error_state.last_intercept = 0;
895 goto CASE;
896 }
897 case if_val_int_code:
898 {
899 lmt_error_state.intercept = 1;
900 lmt_error_state.last_intercept = 0;
901 lmt_condition_state.chk_integer = tex_scan_integer(0, NULL);
902 result = lmt_error_state.last_intercept ? value_error : (lmt_condition_state.chk_integer < 0) ? value_less : (lmt_condition_state.chk_integer > 0) ? value_greater : value_equal;
903 lmt_error_state.intercept = 0;
904 lmt_error_state.last_intercept = 0;
905 goto CASE;
906 }
907 case if_cmp_int_code:
908 {
909 halfword n1 = tex_scan_integer(0, NULL);
910 halfword n2 = tex_scan_integer(0, NULL);
911 result = (n1 < n2) ? 0 : (n1 > n2) ? 2 : 1;
912 goto CASE;
913 }
914 case if_chk_dim_code:
915 {
916 lmt_error_state.intercept = 1;
917 lmt_error_state.last_intercept = 0;
918 lmt_condition_state.chk_dimension = 0;
919 tex_scan_dimension_validate();
920 result = lmt_error_state.last_intercept ? check_error : check_okay;
921 lmt_error_state.intercept = 0;
922 lmt_error_state.last_intercept = 0;
923 goto CASE;
924 }
925 case if_chk_dimension_code:
926 {
927 lmt_error_state.intercept = 1;
928 lmt_error_state.last_intercept = 0;
929 lmt_condition_state.chk_dimension = tex_scan_dimension(0, 0, 0, 0, NULL);
930 result = lmt_error_state.last_intercept ? check_error : check_okay;
931 if (result == check_okay) {
932 tex_aux_check_strict(&result);
933 }
934 lmt_error_state.intercept = 0;
935 lmt_error_state.last_intercept = 0;
936 goto CASE;
937 }
938 case if_val_dim_code:
939 {
940 lmt_error_state.intercept = 1;
941 lmt_error_state.last_intercept = 0;
942 lmt_condition_state.chk_dimension = tex_scan_dimension(0, 0, 0, 0, NULL);
943 result = lmt_error_state.last_intercept ? value_error : (lmt_condition_state.chk_dimension < 0) ? value_less : (lmt_condition_state.chk_dimension > 0) ? value_greater : value_equal;
944 lmt_error_state.intercept = 0;
945 lmt_error_state.last_intercept = 0;
946 goto CASE;
947 }
948 case if_cmp_dim_code:
949 {
950 scaled n1 = tex_scan_dimension(0, 0, 0, 0, NULL);
951 scaled n2 = tex_scan_dimension(0, 0, 0, 0, NULL);
952 result = (n1 < n2) ? 0 : (n1 > n2) ? 2 : 1;
953 goto CASE;
954 }
955 case if_case_code:
956
957 result = tex_scan_integer(0, NULL);
958 goto CASE;
959 case if_defined_code:
960
965 {
966 int save_scanner_status = lmt_input_state.scanner_status;
967 lmt_input_state.scanner_status = scanner_is_normal;
968 tex_get_next();
969 result = cur_cmd != undefined_cs_cmd;
970 lmt_input_state.scanner_status = save_scanner_status;
971 goto RESULT;
972 }
973 case if_csname_code:
974 result = tex_is_valid_csname();
975 goto RESULT;
976 case if_in_csname_code:
977
978 result = lmt_expand_state.cs_name_level;
979 goto RESULT;
980 case if_font_char_code:
981
982 {
983 halfword fnt = tex_scan_font_identifier(NULL);
984 halfword chr = tex_scan_char_number(0);
985 result = tex_char_exists(fnt, chr);
986 }
987 goto RESULT;
988 case if_condition_code:
989
990 goto RESULT;
991 case if_flags_code:
992 {
993 halfword cs;
994 singleword flag;
995 tex_get_r_token();
996 cs = cur_cs;
997 flag = eq_flag(cs);
998
999 tex_get_token();
1000 if (cur_cmd == prefix_cmd) {
1001 switch (cur_chr) {
1002
1003 case frozen_code : result = is_frozen (flag); break;
1004 case permanent_code : result = is_permanent(flag); break;
1005 case immutable_code : result = is_immutable(flag); break;
1006
1007 case mutable_code : result = is_mutable (flag); break;
1008 case noaligned_code : result = is_noaligned(flag); break;
1009 case instance_code : result = is_instance (flag); break;
1010 case untraced_code : result = is_untraced (flag); break;
1011
1012 case global_code : result = eq_level(cs) == level_one;; break;
1013 case tolerant_code : result = is_tolerant_cmd(eq_type(cs)); break;
1014 case protected_code : result = is_protected_cmd(eq_type(cs)); break;
1015 case semiprotected_code : result = is_semi_protected_cmd(eq_type(cs)); break;
1016 case constant_code : result = is_constant_cmd(eq_type(cs)); break;
1017 }
1018 } else {
1019 int fl;
1020 tex_back_input(cur_tok);
1021 fl = tex_scan_integer(1, NULL);
1022 result = (flag & fl) == fl;
1023 if (! result) {
1024 if (is_protected(fl)) {
1025 result = is_protected_cmd(eq_type(cs));
1026 } else if (is_semiprotected(fl)) {
1027 result = is_semi_protected_cmd(eq_type(cs));
1028 } else if (is_tolerant(fl)) {
1029 result = is_tolerant_cmd(eq_type(cs));
1030 } else if (is_global(fl)) {
1031 result = eq_level(cs) == level_one;
1032 }
1033 }
1034 }
1035 goto RESULT;
1036 }
1037 case if_empty_code:
1038 {
1039 tex_get_token();
1040 EMPTY_CHECK_AGAIN:
1041 switch (cur_cmd) {
1042 case call_cmd:
1043 case constant_call_cmd:
1044 result = ! token_link(cur_chr);
1045 break;
1046 case internal_toks_reference_cmd:
1047 case register_toks_reference_cmd:
1048 result = ! token_link(cur_chr);
1049 break;
1050 case register_cmd:
1051
1052 if (cur_chr == token_val_level) {
1053 halfword n = tex_scan_toks_register_number();
1054 halfword p = eq_value(register_toks_location(n));
1055 result = ! p || ! token_link(p);
1056 } else {
1057 result = 0;
1058 }
1059 break;
1060 case internal_toks_cmd:
1061 case register_toks_cmd:
1062 {
1063 halfword p = eq_value(cur_chr);
1064 result = ! p || ! token_link(p);
1065 }
1066 break;
1067 case left_brace_cmd:
1068 {
1069 halfword h = tex_scan_toks_expand(1, NULL, 0, 0);
1070 result = token_link(h) == null;
1071 tex_flush_token_list(h);
1072 }
1073 break;
1074 case cs_name_cmd:
1075 if (cur_chr == last_named_cs_code && lmt_scanner_state.last_cs_name != null_cs) {
1076 cur_cmd = eq_type(lmt_scanner_state.last_cs_name);
1077 cur_chr = eq_value(lmt_scanner_state.last_cs_name);
1078 goto EMPTY_CHECK_AGAIN;
1079 }
1080
1081 default:
1082 result = 0;
1083 }
1084 goto RESULT;
1085 }
1086 case if_relax_code:
1087 {
1088 tex_get_token();
1089 result = cur_cmd == relax_cmd;
1090 goto RESULT;
1091 }
1092 case if_boolean_code:
1093 result = tex_scan_integer(0, NULL) ? 1 : 0;
1094 goto RESULT;
1095 case if_numexpression_code:
1096 result = tex_scanned_expression(integer_val_level) ? 1 : 0;
1097 goto RESULT;
1098 case if_dimexpression_code:
1099 result = tex_scanned_expression(dimension_val_level) ? 1 : 0;
1100 goto RESULT;
1101 case if_math_parameter_code:
1102
1106 {
1107 do {
1108 tex_get_x_token();
1109 } while (cur_cmd == spacer_cmd);
1110 if (cur_cmd == math_parameter_cmd) {
1111 int code = cur_chr;
1112 int style = tex_scan_math_style_identifier(0, 0);
1113 result = tex_get_math_parameter(style, code, NULL);
1114 if (result == max_dimension) {
1115 result = parameter_unset;
1116 } else if (result) {
1117 result = parameter_set;
1118 } else {
1119 result = parameter_zero;
1120 }
1121 } else {
1122 tex_normal_error("mathparameter", "a valid parameter expected");
1123 result = parameter_zero;
1124 }
1125 goto CASE;
1126 }
1127 case if_math_style_code:
1128 result = tex_current_math_style();
1129 goto CASE;
1130 case if_arguments_code:
1131 result = lmt_expand_state.arguments;
1132 goto CASE;
1133 case if_parameters_code:
1134
1138 result = tex_get_parameter_count();
1139 goto CASE;
1140 case if_parameter_code:
1141 {
1142
1147 if (lmt_input_state.cur_input.loc) {
1148 halfword t = token_info(lmt_input_state.cur_input.loc);
1149 if (t < cs_token_flag && token_cmd(t) == parameter_reference_cmd) {
1150 lmt_input_state.cur_input.loc = token_link(lmt_input_state.cur_input.loc);
1151 result = lmt_input_state.parameter_stack[lmt_input_state.cur_input.parameter_start + token_chr(t) - 1] != null ? 1 : 2;
1152 } else {
1153
1157 tex_get_token();
1158 result = cur_cmd == if_test_cmd ? 2 : 1;
1159 }
1160 }
1161 goto CASE;
1162 }
1163 case if_has_tok_code:
1164 {
1165 halfword qq = null;
1166 halfword p, q;
1167 int save_scanner_status = lmt_input_state.scanner_status;
1168 lmt_input_state.scanner_status = scanner_is_normal;
1169 p = tex_get_token();
1170 q = tex_aux_grab_toks(0, 0, &qq);
1171 if (p == q) {
1172 result = 1;
1173 } else {
1174 result = 0;
1175 while (q) {
1176 if (p == token_info(q)) {
1177 result = 1;
1178 break;
1179 } else {
1180 q = token_link(q);
1181 }
1182 }
1183 }
1184 if (qq) {
1185 tex_flush_token_list(qq);
1186 }
1187 lmt_input_state.scanner_status = save_scanner_status;
1188 goto RESULT;
1189 }
1190 case if_has_toks_code:
1191 case if_has_xtoks_code:
1192 {
1193 halfword pp = null;
1194 halfword p;
1195 int expand = code == if_has_xtoks_code;
1196 int save_scanner_status = lmt_input_state.scanner_status;
1197 lmt_input_state.scanner_status = scanner_is_normal;
1198 p = tex_aux_grab_toks(expand, expand, &pp);
1199 if (p) {
1200 halfword qq = null;
1201 halfword q = tex_aux_grab_toks(expand, expand, &qq);
1202 if (p == q) {
1203 result = 1;
1204 } else {
1205 int qh = q;
1206 int ph = p;
1207 result = 0;
1208 while (p && q) {
1209 halfword pt = token_info(p);
1210 halfword qt = token_info(q);
1211 AGAIN:
1212 if (pt == qt) {
1213 p = token_link(p);
1214 q = token_link(q);
1215 } else if (token_cmd(pt) == ignore_cmd && token_cmd(qt) >= ignore_cmd && token_cmd(qt) <= other_char_cmd) {
1216 p = token_link(p);
1217 if (token_chr(pt) == token_chr(qt)) {
1218 q = token_link(q);
1219 } else {
1220 pt = token_info(p);
1221 goto AGAIN;
1222 }
1223 } else {
1224 p = ph;
1225 q = token_link(qh);
1226 qh = q;
1227 }
1228 if (! p) {
1229 result = 1;
1230 break;
1231 }
1232 }
1233 }
1234 if (qq) {
1235 tex_flush_token_list(qq);
1236 }
1237 }
1238 if (pp) {
1239 tex_flush_token_list(pp);
1240 }
1241 lmt_input_state.scanner_status = save_scanner_status;
1242 goto RESULT;
1243 }
1244 case if_has_char_code:
1245 {
1246 halfword tok;
1247 halfword qq = null;
1248 halfword q;
1249 int save_scanner_status = lmt_input_state.scanner_status;
1250 lmt_input_state.scanner_status = scanner_is_normal;
1251 tok = tex_get_token();
1252 q = tex_aux_grab_toks(0, 0, &qq);
1253 if (q) {
1254 int nesting = 0;
1255 result = 0;
1256 while (q) {
1257 if (! nesting && token_info(q) == tok) {
1258 result = 1;
1259 break;
1260 } else if (token_cmd(token_info(q)) == left_brace_cmd) {
1261 nesting += 1;
1262 } else if (token_cmd(token_info(q)) == right_brace_cmd) {
1263 nesting -= 1;
1264 }
1265 q = token_link(q);
1266 }
1267 }
1268 if (qq) {
1269 tex_flush_token_list(qq);
1270 }
1271 lmt_input_state.scanner_status = save_scanner_status;
1272 goto RESULT;
1273 }
1274 case if_insert_code:
1275 {
1276
1277 result = ! tex_insert_is_void(tex_scan_integer(0, NULL));
1278 goto RESULT;
1279 }
1280 case if_in_alignment_code:
1281 {
1282 result = tex_in_alignment();
1283 goto RESULT;
1284 }
1285
1286
1287
1288
1289
1290
1291
1292 default:
1293 {
1294 int category;
1295 strnumber u = tex_save_cur_string();
1296 int save_scanner_status = lmt_input_state.scanner_status;
1297 lmt_input_state.scanner_status = scanner_is_normal;
1298 lmt_token_state.luacstrings = 0;
1299 category = lmt_function_call_by_category(code - last_if_test_code, 0, &result);
1300 tex_restore_cur_string(u);
1301 lmt_input_state.scanner_status = save_scanner_status;
1302 if (lmt_token_state.luacstrings > 0) {
1303 tex_lua_string_start();
1304 }
1305 switch (category) {
1306 case lua_value_integer_code:
1307 case lua_value_cardinal_code:
1308 case lua_value_dimension_code:
1309 goto CASE;
1310 case lua_value_boolean_code:
1311 goto RESULT;
1312 case lua_value_conditional_code:
1313
1314 tex_back_input(token_val(if_test_cmd, if_condition_code));
1315 tex_aux_pop_condition_stack();
1316 return;
1317 default:
1318 result = 0;
1319 goto RESULT;
1320 }
1321 }
1322 }
1323 CASE:
1324
1328 if (tracing_commands > 1) {
1329 tex_aux_show_if_state(code, result);
1330 }
1331 while (result) {
1332 unless = tex_aux_pass_text_x(tracing_ifs, tracing_commands);
1333 if (tracing_both) {
1334 tex_show_cmd_chr(cur_cmd, cur_chr);
1335 }
1336 if (lmt_condition_state.cond_ptr == save_cond_ptr) {
1337 if (cur_chr >= first_real_if_test_code) {
1338
1343 if (cur_chr == if_condition_code) {
1344
1345 tex_aux_pop_condition_stack();
1346 return;
1347 } else {
1348 code = cur_chr;
1349 goto HERE;
1350 }
1351 } else if (cur_chr == or_code) {
1352 --result;
1353 } else {
1354 goto COMMON_ENDING;
1355 }
1356 } else if (cur_chr == fi_code) {
1357 tex_aux_pop_condition_stack();
1358 }
1359 }
1360 tex_aux_change_if_limit(or_code, save_cond_ptr);
1361
1362 return;
1363 RESULT:
1364 if (unless) {
1365 result = ! result;
1366 }
1367 if (tracing_commands > 1) {
1368
1369 tex_begin_diagnostic();
1370 tex_print_str(result ? "{true}" : "{false}");
1371 tex_end_diagnostic();
1372 }
1373 if (result) {
1374 tex_aux_change_if_limit(else_code, save_cond_ptr);
1375
1376 return;
1377 } else {
1378
1383 while (1) {
1384 unless = tex_aux_pass_text_x(tracing_ifs, tracing_commands);
1385 if (tracing_both) {
1386 tex_show_cmd_chr(cur_cmd, cur_chr);
1387 }
1388 if (lmt_condition_state.cond_ptr == save_cond_ptr) {
1389
1390 if (cur_chr >= first_real_if_test_code) {
1391 if (cur_chr == if_condition_code) {
1392
1393 tex_aux_pop_condition_stack();
1394 return;
1395 } else {
1396 code = cur_chr;
1397 goto HERE;
1398 }
1399 } else if (cur_chr != or_code) {
1400 goto COMMON_ENDING;
1401 } else {
1402 tex_handle_error(
1403 normal_error_type,
1404 "Extra \\or",
1405 "I'm ignoring this; it doesn't match any \\if."
1406 );
1407 }
1408 } else if (cur_chr == fi_code) {
1409 tex_aux_pop_condition_stack();
1410 }
1411 }
1412 }
1413 COMMON_ENDING:
1414 if (cur_chr == fi_code) {
1415 tex_aux_pop_condition_stack();
1416 } else {
1417
1418
1419 lmt_condition_state.if_limit = fi_code;
1420 }
1421}
1422
1423
1428
1429void tex_conditional_fi_or_else(void)
1430{
1431 int tracing_ifs = tracing_ifs_par > 0;
1432 if (tracing_ifs && tracing_commands_par <= 1) {
1433 tex_show_cmd_chr(if_test_cmd, cur_chr);
1434 }
1435 if (cur_chr == or_else_code || cur_chr == or_unless_code) {
1436 tex_get_next_non_spacer();
1437 } else if (cur_chr > lmt_condition_state.if_limit) {
1438 if (lmt_condition_state.if_limit == if_code) {
1439
1440 tex_insert_relax_and_cur_cs();
1441 } else {
1442 tex_handle_error(normal_error_type,
1443 "Extra %C",
1444 if_test_cmd, cur_chr,
1445 "I'm ignoring this; it doesn't match any \\if."
1446 );
1447 }
1448
1449 return;
1450 }
1451
1452 while (! (cur_cmd == if_test_cmd && cur_chr == fi_code)) {
1453 tex_aux_pass_text();
1454 if (tracing_ifs) {
1455 tex_show_cmd_chr(cur_cmd, cur_chr);
1456 }
1457 }
1458 tex_aux_pop_condition_stack();
1459}
1460
1461
1468
1469void tex_conditional_unless(void)
1470{
1471 tex_get_token();
1472 if (cur_cmd == if_test_cmd) {
1473 if (tracing_commands_par > 1) {
1474 tex_show_cmd_chr(cur_cmd, cur_chr);
1475 }
1476 if (cur_chr != if_condition_code) {;
1477 tex_conditional_if(cur_chr, 1);
1478 }
1479 } else {
1480 tex_handle_error(back_error_type,
1481 "You can't use '\\unless' before '%C'",
1482 cur_cmd, cur_chr,
1483 "Continue, and I'll forget that it ever happened."
1484 );
1485 }
1486}
1487
1488void tex_show_ifs(void)
1489{
1490 if (lmt_condition_state.cond_ptr) {
1491
1492 int n = 0;
1493 {
1494
1495 halfword p = lmt_condition_state.cond_ptr;
1496 do {
1497 ++n;
1498 p = node_next(p);
1499 } while (p);
1500 }
1501
1502 {
1503 halfword cond_ptr = lmt_condition_state.cond_ptr;
1504 int cur_if = lmt_condition_state.cur_if;
1505 int cur_unless = lmt_condition_state.cur_unless;
1506 int if_step = lmt_condition_state.if_step;
1507 int if_unless = lmt_condition_state.if_unless;
1508 int if_line = lmt_condition_state.if_line;
1509 int if_limit = lmt_condition_state.if_limit;
1510 do {
1511 if (cur_unless) {
1512 if (if_line) {
1513 tex_print_format("[conditional: level %i, current %C %C, limit %C, %sstep %C, line %i]",
1514 n,
1515 expand_after_cmd, expand_unless_code,
1516 if_test_cmd, cur_if,
1517 if_test_cmd, if_limit,
1518 if_unless ? "unless " : "",
1519 if_test_cmd, if_step,
1520 if_line
1521 );
1522 } else {
1523 tex_print_format("[conditional: level %i, current %C %C, limit %C, %sstep %C]",
1524 n,
1525 expand_after_cmd, expand_unless_code,
1526 if_test_cmd, cur_if,
1527 if_test_cmd, if_limit,
1528 if_unless ? "unless " : "",
1529 if_test_cmd, if_step
1530 );
1531 }
1532 } else {
1533 if (if_line) {
1534 tex_print_format("[conditional: level %i, current %C, limit %C, %sstep %C, line %i]",
1535 n,
1536 if_test_cmd, cur_if,
1537 if_test_cmd, if_limit,
1538 if_unless ? "unless " : "",
1539 if_test_cmd, if_step,
1540 if_line
1541 );
1542 } else {
1543 tex_print_format("[conditional: level %i, current %C, limit %C, %sstep %C]",
1544 n,
1545 if_test_cmd, cur_if,
1546 if_test_cmd, if_limit,
1547 if_unless ? "unless " : "",
1548 if_test_cmd, if_step
1549 );
1550 }
1551 }
1552 --n;
1553 cur_if = if_limit_subtype(cond_ptr);
1554 cur_unless = if_limit_unless(cond_ptr);;
1555 if_step = if_limit_step(cond_ptr);;
1556 if_unless = if_limit_stepunless(cond_ptr);;
1557 if_line = if_limit_line(cond_ptr);;
1558 if_limit = if_limit_type(cond_ptr);;
1559 cond_ptr = node_next(cond_ptr);
1560 if (cond_ptr) {
1561 tex_print_levels();
1562 }
1563 } while (cond_ptr);
1564 }
1565 } else {
1566 tex_print_str("[conditional: none active]");
1567 }
1568}
1569
1570
1575
1576 |