1use std::rc::Rc;
9
10use crate::Engine;
11use crate::cmd::Cmd;
12use crate::hash::NO_CS;
13use crate::input::{Level, TokKind};
14use crate::nodes::NodeRef;
15use crate::primitives_gen as pg;
16use crate::save::GroupCode;
17use crate::token::Token;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Mode {
22 None,
24 Vertical,
26 InternalVertical,
28 Horizontal,
30 RestrictedHorizontal,
32 DisplayMath,
34 Math,
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum ModeKind {
41 None,
43 Vertical,
45 Horizontal,
47 Math,
49}
50
51impl Mode {
52 pub fn abs_kind(self) -> ModeKind {
54 match self {
55 Mode::None => ModeKind::None,
56 Mode::Vertical | Mode::InternalVertical => ModeKind::Vertical,
57 Mode::Horizontal | Mode::RestrictedHorizontal => ModeKind::Horizontal,
58 Mode::DisplayMath | Mode::Math => ModeKind::Math,
59 }
60 }
61
62 pub fn is_inner(self) -> bool {
64 matches!(
65 self,
66 Mode::InternalVertical | Mode::RestrictedHorizontal | Mode::Math
67 )
68 }
69
70 pub fn is_privileged(self) -> bool {
72 matches!(self, Mode::Vertical | Mode::Horizontal | Mode::DisplayMath)
73 }
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub enum BoxContext {
79 Shift(i32),
82 SetBox(u32),
84 GlobalSetBox(u32),
86 Shipout,
88 Leaders(u16),
90 Lua,
92}
93
94#[derive(Clone, Debug)]
96pub struct ListState {
97 pub mode: Mode,
99 pub head: NodeRef,
101 pub tail: NodeRef,
103 pub prev_graf: i32,
105 pub mode_line: i32,
108 pub prev_depth: i32,
110 pub space_factor: i32,
112 pub clang: i32,
114 pub incompleat_noad: NodeRef,
116 pub math_style: i32,
118 pub delim_ptr: NodeRef,
121}
122
123pub const IGNORE_DEPTH: i32 = -65_536_000;
126
127impl Engine {
128 pub fn cur_list(&self) -> &ListState {
130 self.nest.last().expect("nest is never empty")
131 }
132
133 pub fn cur_list_mut(&mut self) -> &mut ListState {
135 self.nest.last_mut().expect("nest is never empty")
136 }
137
138 pub fn push_nest(&mut self, mode: Mode) {
140 let head = self.nodes.new_temp();
141 let cl = ListState {
142 mode,
143 head,
144 tail: head,
145 prev_graf: 0,
146 mode_line: self.input.line(),
147 prev_depth: IGNORE_DEPTH,
148 space_factor: 1000,
149 clang: 0,
150 incompleat_noad: NodeRef::NULL,
151 math_style: 0,
152 delim_ptr: NodeRef::NULL,
153 };
154 self.nest.push(cl);
155 self.mode = mode;
156 }
157
158 pub fn pop_nest(&mut self) {
160 if let Some(cl) = self.nest.pop() {
161 self.nodes.free(cl.head);
162 }
163 self.mode = self.cur_list().mode;
164 }
165
166 pub fn tail_append(&mut self, p: NodeRef) {
168 let tail = self.cur_list().tail;
169 self.nodes.link(tail, p);
170 self.cur_list_mut().tail = p;
171 }
172
173 pub fn print_mode(&mut self, m: Mode) {
175 let s = match m {
176 Mode::Vertical => "vertical mode",
177 Mode::Horizontal => "horizontal mode",
178 Mode::DisplayMath => "display math mode",
179 Mode::None => "no mode",
180 Mode::InternalVertical => "internal vertical mode",
181 Mode::RestrictedHorizontal => "restricted horizontal mode",
182 Mode::Math => "math mode",
183 };
184 self.printer.print_str(s);
185 }
186
187 pub fn main_control(&mut self) {
190 let every_job = self.eqtb.toks_param(pg::assign_toks::EVERYJOB).clone();
191 if !every_job.is_empty() {
192 self.begin_token_list(every_job, TokKind::EveryJobText);
193 }
194 self.main_loop();
195 }
196
197 pub fn run_local(&mut self, list: Option<crate::token::TokenList>) {
201 let end: crate::token::TokenList = Rc::from(vec![Token::cs(self.cs.end_local)]);
202 let depth = self.input.stack.len();
204 self.begin_token_list(end, TokKind::Inserted);
205 if let Some(l) = list {
206 self.begin_token_list(l, TokKind::Inserted);
207 }
208 let _ = depth;
209 self.local_level += 1;
210 self.main_loop();
211 self.local_level -= 1;
212 }
213
214 pub fn push_local_marker(&mut self) {
218 let end: crate::token::TokenList = Rc::from(vec![Token::cs(self.cs.end_local)]);
219 self.begin_token_list(end, TokKind::Inserted);
220 }
221
222 pub fn scan_list(&mut self) -> NodeRef {
226 self.lua_scanned_box = None;
227 let level = self.save.cur_level;
228 self.scan_box(BoxContext::Lua);
229 if self.lua_scanned_box.is_none() && self.save.cur_level > level {
230 let waiting = std::mem::replace(&mut self.lua_scan_waiting, true);
231 self.run_local_loop();
232 self.lua_scan_waiting = waiting;
233 }
234 self.lua_scanned_box.take().unwrap_or(NodeRef::NULL)
235 }
236
237 pub fn run_local_loop(&mut self) {
239 self.local_level += 1;
240 self.main_loop();
241 self.local_level -= 1;
242 }
243
244 #[inline]
247 pub fn check_double_free(&mut self) {
248 if let Some((t, p)) = self.nodes.double_free {
249 let name = crate::nodes::type_name(t);
250 self.luatex_fatal(
251 "nodes",
252 &format!("attempt to double-free {name} node {p}, ignored"),
253 );
254 }
255 }
256
257 fn main_loop(&mut self) {
258 loop {
259 self.get_x_token();
261 loop {
262 if self.eqtb.int(pg::assign_int::TRACINGCOMMANDS) > 0 {
264 self.show_cur_cmd_chr();
265 }
266 match self.dispatch() {
267 Next::BigSwitch => {
268 self.check_double_free();
269 break;
270 }
271 Next::Reswitch => continue,
272 Next::Return => return,
273 }
274 }
275 }
276 }
277
278 fn dispatch(&mut self) -> Next {
280 use ModeKind as K;
281 let kind = self.mode.abs_kind();
282 let cmd = self.cur_cmd;
283 match (kind, cmd) {
284 (K::Horizontal, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) => {
285 self.main_loop_char();
286 Next::Reswitch
287 }
288 (K::Horizontal, Cmd::CharNum) => {
289 self.scan_char_num();
290 self.cur_chr = self.cur_val as u32;
291 self.main_loop_char();
292 Next::Reswitch
293 }
294 (K::Horizontal, Cmd::Boundary) => {
295 self.append_boundary();
296 Next::BigSwitch
297 }
298 (K::Horizontal, Cmd::Spacer) => {
299 self.app_space_main();
300 Next::BigSwitch
301 }
302 (K::Horizontal | K::Math, Cmd::ExSpace) => {
303 self.app_explicit_space();
304 Next::BigSwitch
305 }
306 (K::Vertical, Cmd::Spacer) if self.local_level > 0 => {
310 self.append_normal_space();
311 Next::BigSwitch
312 }
313 (_, Cmd::Relax) | (K::Vertical | K::Math, Cmd::Spacer) => Next::BigSwitch,
314 (K::Math, Cmd::Boundary) => Next::BigSwitch,
315 (_, Cmd::IgnoreSpaces) => {
316 if self.cur_chr == 0 {
317 self.get_nonblank_noncall();
318 Next::Reswitch
319 } else {
320 Next::BigSwitch
322 }
323 }
324 (K::Vertical, Cmd::Stop) => {
325 if self.its_all_over() {
326 Next::Return
327 } else {
328 Next::BigSwitch
329 }
330 }
331 (K::Vertical, Cmd::Vmove)
332 | (K::Horizontal | K::Math, Cmd::Hmove)
333 | (_, Cmd::LastItem)
334 | (_, Cmd::MacParam) => {
335 self.report_illegal_case();
336 Next::BigSwitch
337 }
338 (_, c) if c > Cmd::MAX_NON_PREFIXED => {
339 self.prefixed_command();
340 Next::BigSwitch
341 }
342 (_, Cmd::BeginGroup) => {
343 self.new_save_level(GroupCode::SemiSimple);
344 self.reset_local_counts(true);
345 Next::BigSwitch
346 }
347 (_, Cmd::EndGroup) => {
348 if self.save.cur_group == GroupCode::SemiSimple {
349 self.end_group_text_dirs();
350 self.unsave();
351 } else {
352 self.off_save();
353 }
354 Next::BigSwitch
355 }
356 (_, Cmd::LeftBrace) if kind != K::Math => {
357 self.new_save_level(GroupCode::Simple);
358 self.reset_local_counts(true);
359 Next::BigSwitch
360 }
361 (_, Cmd::RightBrace) => {
362 self.handle_right_brace();
363 Next::BigSwitch
364 }
365 (_, Cmd::AfterAssignment) => {
366 self.get_token();
367 self.after_token = self.cur_tok;
368 Next::BigSwitch
369 }
370 (_, Cmd::AfterGroup) => {
371 self.get_token();
372 self.save_for_after(self.cur_tok);
373 Next::BigSwitch
374 }
375 (_, Cmd::InStream) => {
376 self.open_or_close_in();
377 Next::BigSwitch
378 }
379 (_, Cmd::Message) => {
380 self.issue_message();
381 Next::BigSwitch
382 }
383 (_, Cmd::CaseShift) => {
384 self.shift_case();
385 Next::BigSwitch
386 }
387 (_, Cmd::Xray) => {
388 self.show_whatever();
389 Next::BigSwitch
390 }
391 (_, Cmd::Extension)
392 if self.cur_chr == crate::misc::ext::END_LOCAL_CONTROL && self.local_level > 0 =>
393 {
394 Next::Return
395 }
396 (_, Cmd::Extension) => {
397 self.do_extension();
398 Next::BigSwitch
399 }
400 (_, Cmd::Normal) => {
401 self.do_normal();
402 Next::BigSwitch
403 }
404 (_, Cmd::LuaFunctionCall | Cmd::LuaBytecodeCall | Cmd::LuaCall) => {
405 let n = if self.cur_cmd == Cmd::LuaFunctionCall
406 || self.cur_cmd == Cmd::LuaBytecodeCall
407 {
408 self.scan_int();
409 self.cur_val
410 } else {
411 self.cur_chr as i32
412 };
413 if self.cur_cmd == Cmd::LuaBytecodeCall {
414 self.call_lua_bytecode(n);
415 } else {
416 self.call_lua_function(n);
417 }
418 Next::BigSwitch
419 }
420 (_, Cmd::PartokenName) => {
421 self.partoken_name();
422 Next::BigSwitch
423 }
424 _ => {
425 self.typeset_command();
426 Next::BigSwitch
427 }
428 }
429 }
430
431 pub fn report_illegal_case(&mut self) {
434 self.you_cant();
435 self.help(&[
436 "Sorry, but I'm not programmed to handle this case;",
437 "I'll just pretend that you didn't ask for it.",
438 "If you're in the wrong mode, you might be able to",
439 "return to the right one by typing `I}' or `I$' or `I\\par'.",
440 ]);
441 self.error();
442 }
443
444 pub fn you_cant(&mut self) {
446 self.print_err("You can't use `");
447 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
448 self.printer.print_str("' in ");
449 self.print_mode(self.mode);
450 }
451
452 pub fn privileged(&mut self) -> bool {
455 if self.mode.is_privileged() {
456 true
457 } else {
458 self.report_illegal_case();
459 false
460 }
461 }
462
463 pub fn insert_dollar_sign(&mut self) {
465 self.back_input();
466 self.cur_tok = Token::char(Cmd::MathShift, '$' as u32);
467 self.print_err("Missing $ inserted");
468 self.help(&[
469 "I've inserted a begin-math/end-math symbol since I think",
470 "you left one out. Proceed, with fingers crossed.",
471 ]);
472 self.ins_error();
473 }
474
475 fn its_all_over(&mut self) -> bool {
478 if self.privileged() {
479 let cl = self.cur_list();
480 if self.page_is_empty() && cl.head == cl.tail && self.dead_cycles == 0 {
481 return true;
482 }
483 self.back_input();
484 let b = self.nodes.new_null_box(true);
485 let hsize = self.eqtb.dimen(pg::assign_dimen::HSIZE);
486 if let Some(bd) = self.nodes.box_data_mut(b) {
487 bd.width = hsize;
488 }
489 self.tail_append(b);
490 let g = self.nodes.new_glue(
491 crate::glue::Glue {
492 stretch: crate::glue::Glue::fixed(0).stretch + retex_arith::UNITY,
493 stretch_order: crate::glue::FILL,
494 ..crate::glue::Glue::ZERO
495 },
496 0,
497 );
498 self.tail_append(g);
499 let p = self.nodes.new_penalty(-0o10000000000, 0);
500 self.tail_append(p);
501 self.build_page_from("end");
502 }
503 false
504 }
505
506 pub fn handle_right_brace(&mut self) {
508 match self.save.cur_group {
509 GroupCode::Simple => {
510 self.end_group_text_dirs();
511 self.unsave();
512 }
513 GroupCode::BottomLevel => {
514 self.print_err("Too many }'s");
515 self.help(&[
516 "You've closed more groups than you opened.",
517 "Such booboos are generally harmless, so keep going.",
518 ]);
519 self.error();
520 }
521 GroupCode::SemiSimple | GroupCode::MathShift | GroupCode::MathLeft => {
522 self.extra_right_brace()
523 }
524 _ => self.handle_right_brace_typesetting(),
525 }
526 }
527
528 fn extra_right_brace(&mut self) {
529 self.print_err("Extra }, or forgotten ");
530 match self.save.cur_group {
531 GroupCode::SemiSimple => self.print_esc_str("endgroup"),
532 GroupCode::MathShift => self.printer.print_char('$' as u32),
533 _ => self.print_esc_str("right"),
534 }
535 self.help(&[
536 "I've deleted a group-closing symbol because it seems to be",
537 "spurious, as in `$x}$'. But perhaps the } is legitimate and",
538 "you forgot something else, as in `\\hbox{$x}'. In such cases",
539 "the way to recover is to insert both the forgotten and the",
540 "deleted material, e.g., by typing `I$}'.",
541 ]);
542 self.error();
543 self.input.align_state += 1;
544 }
545
546 pub fn off_save(&mut self) {
548 if self.save.cur_group == GroupCode::BottomLevel {
549 self.print_err("Extra ");
551 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
552 self.help(&["Things are pretty mixed up, but I think the worst is over."]);
553 self.error();
554 return;
555 }
556 self.back_input();
557 let mut list: Vec<Token> = Vec::new();
558 self.print_err("Missing ");
559 match self.save.cur_group {
560 GroupCode::SemiSimple => {
561 list.push(Token::cs(self.cs.frozen_end_group));
562 self.print_esc_str("endgroup");
563 }
564 GroupCode::MathShift => {
565 list.push(Token::char(Cmd::MathShift, '$' as u32));
566 self.printer.print_char('$' as u32);
567 }
568 GroupCode::MathLeft => {
569 list.push(Token::cs(self.cs.frozen_right));
570 list.push(Token::other('.' as u32));
571 self.print_esc_str("right.");
572 }
573 _ => {
574 list.push(Token::RIGHT_BRACE);
575 self.printer.print_char('}' as u32);
576 }
577 }
578 self.printer.print_str(" inserted");
579 self.ins_list(Rc::from(list));
580 self.help(&[
581 "I've inserted something that you may have forgotten.",
582 "(See the <inserted text> above.)",
583 "With luck, this will get me unwedged. But if you",
584 "really didn't forget anything, try typing `2' now; then",
585 "my insertion and my current dilemma will both disappear.",
586 ]);
587 self.error();
588 }
589
590 fn issue_message(&mut self) {
592 let c = self.cur_chr;
593 let toks = self.scan_toks_expanded();
594 let s = self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), 10_000_000));
595 if c == 0 {
596 if self.printer.term_offset + s.len() > self.printer.max_print_line.saturating_sub(2) {
597 self.printer.print_ln();
598 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
599 self.printer.print_char(' ' as u32);
600 }
601 self.printer.print_bytes(&s);
602 self.printer.update_terminal();
603 } else {
604 self.print_err("");
605 self.printer.print_bytes(&s);
606 let err_help = self.eqtb.toks_param(pg::assign_toks::ERRHELP);
607 if !err_help.is_empty() {
608 self.err.use_err_help = true;
609 } else if self.long_help_seen {
610 self.help(&["(That was another \\errmessage.)"]);
611 } else {
612 if self.err.interaction < crate::error::Interaction::ErrorStop {
613 self.long_help_seen = true;
614 }
615 self.help(&[
616 "This error message was generated by an \\errmessage",
617 "command, so I can't give any explicit help.",
618 "Pretend that you're Hercule Poirot: Examine all clues,",
619 "and deduce the truth by order and method.",
620 ]);
621 }
622 self.error();
623 self.err.use_err_help = false;
624 }
625 }
626
627 fn shift_case(&mut self) {
629 let b = self.cur_chr;
630 let toks = self.scan_general_text();
631 let upper = b == pg::def_char_code::UCCODE + UC_LC_OFFSET;
632 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
633 for &t in toks.iter() {
634 if t.is_cs() {
635 if let crate::hash::CsKind::Active(c) = *self.hash.kind(t.cs_id()) {
636 let m = self.case_map(c, upper);
637 if m != 0 {
638 out.push(Token::cs(self.hash.active(m)));
639 continue;
640 }
641 }
642 out.push(t);
643 } else {
644 let c = t.chr();
645 let m = self.case_map(c, upper);
646 if m != 0 {
647 out.push(Token::char(t.cmd(), m));
648 } else {
649 out.push(t);
650 }
651 }
652 }
653 self.back_list(Rc::from(out));
654 }
655
656 fn case_map(&self, c: u32, upper: bool) -> u32 {
657 (if upper {
658 self.eqtb.uc.get(c)
659 } else {
660 self.eqtb.lc.get(c)
661 }) as u32
662 }
663
664 pub fn final_cleanup(&mut self) {
666 let c = self.cur_chr;
667 if c != 1 {
668 self.eqtb.int_par[pg::assign_int::NEWLINECHAR as usize].val = -1;
669 self.printer.new_line_char = -1;
670 }
671 if self.job_name.is_none() {
672 self.open_log_file();
673 }
674 while self.input.stack.len() > 1 {
675 match self.input.stack.last() {
676 Some(Level::Tokens(_)) => self.end_token_list(),
677 _ => self.end_file_reading(),
678 }
679 }
680 while self.input.open_parens > 0 {
681 self.printer.print_char(')' as u32);
683 self.input.open_parens -= 1;
684 }
685 if self.save.cur_level > crate::eqtb::LEVEL_ONE {
686 self.printer.print_nl("(");
687 self.print_esc_str("end occurred ");
688 self.printer.print_str("inside a group at level ");
689 self.printer
690 .print_int(i64::from(self.save.cur_level - crate::eqtb::LEVEL_ONE));
691 self.printer.print_char(')' as u32);
692 self.show_save_groups();
693 }
694 while !self.cond.stack.is_empty() {
695 self.printer.print_nl("(");
696 self.print_esc_str("end occurred ");
697 self.printer.print_str("when ");
698 self.print_cmd_chr(Cmd::IfTest, self.cond.cur_if);
699 if self.cond.if_line != 0 {
700 self.printer.print_str(" on line ");
701 self.printer.print_int(self.cond.if_line.into());
702 }
703 self.printer.print_str(" was incomplete)");
704 self.pop_cond();
705 }
706 if self.err.history != crate::error::History::Spotless
707 && (self.err.history == crate::error::History::WarningIssued
708 || self.err.interaction < crate::error::Interaction::ErrorStop)
709 && self.printer.selector == crate::print::Selector::TermAndLog
710 {
711 self.printer.selector = crate::print::Selector::TermOnly;
712 self.printer
713 .print_nl("(see the transcript file for additional information)");
714 self.printer.selector = crate::print::Selector::TermAndLog;
715 }
716 if c == 1 {
717 if self.ini_version {
718 self.dump_requested = true;
719 return;
720 }
721 self.printer
722 .print_nl("(\\dump is performed only by INITEX)");
723 }
724 }
725
726 fn partoken_name(&mut self) {
729 self.get_token();
730 if self.cur_cs != NO_CS {
731 self.par_token_cs = self.cur_cs;
732 }
733 }
734}
735
736pub const UC_LC_OFFSET: u32 = 333_280;
739
740pub enum Next {
742 BigSwitch,
744 Reswitch,
746 Return,
748}