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