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 (_, Cmd::Relax) | (K::Vertical | K::Math, Cmd::Spacer) => Next::BigSwitch,
274 (K::Math, Cmd::Boundary) => Next::BigSwitch,
275 (_, Cmd::IgnoreSpaces) => {
276 if self.cur_chr == 0 {
277 self.get_nonblank_noncall();
278 Next::Reswitch
279 } else {
280 Next::BigSwitch
282 }
283 }
284 (K::Vertical, Cmd::Stop) => {
285 if self.its_all_over() {
286 Next::Return
287 } else {
288 Next::BigSwitch
289 }
290 }
291 (K::Vertical, Cmd::Vmove)
292 | (K::Horizontal | K::Math, Cmd::Hmove)
293 | (_, Cmd::LastItem)
294 | (_, Cmd::MacParam) => {
295 self.report_illegal_case();
296 Next::BigSwitch
297 }
298 (_, c) if c > Cmd::MAX_NON_PREFIXED => {
299 self.prefixed_command();
300 Next::BigSwitch
301 }
302 (_, Cmd::BeginGroup) => {
303 self.new_save_level(GroupCode::SemiSimple);
304 self.reset_local_counts(true);
305 Next::BigSwitch
306 }
307 (_, Cmd::EndGroup) => {
308 if self.save.cur_group == GroupCode::SemiSimple {
309 self.end_group_text_dirs();
310 self.unsave();
311 } else {
312 self.off_save();
313 }
314 Next::BigSwitch
315 }
316 (_, Cmd::LeftBrace) if kind != K::Math => {
317 self.new_save_level(GroupCode::Simple);
318 self.reset_local_counts(true);
319 Next::BigSwitch
320 }
321 (_, Cmd::RightBrace) => {
322 self.handle_right_brace();
323 Next::BigSwitch
324 }
325 (_, Cmd::AfterAssignment) => {
326 self.get_token();
327 self.after_token = self.cur_tok;
328 Next::BigSwitch
329 }
330 (_, Cmd::AfterGroup) => {
331 self.get_token();
332 self.save_for_after(self.cur_tok);
333 Next::BigSwitch
334 }
335 (_, Cmd::InStream) => {
336 self.open_or_close_in();
337 Next::BigSwitch
338 }
339 (_, Cmd::Message) => {
340 self.issue_message();
341 Next::BigSwitch
342 }
343 (_, Cmd::CaseShift) => {
344 self.shift_case();
345 Next::BigSwitch
346 }
347 (_, Cmd::Xray) => {
348 self.show_whatever();
349 Next::BigSwitch
350 }
351 (_, Cmd::Extension)
352 if self.cur_chr == crate::misc::ext::END_LOCAL_CONTROL && self.local_level > 0 =>
353 {
354 Next::Return
355 }
356 (_, Cmd::Extension) => {
357 self.do_extension();
358 Next::BigSwitch
359 }
360 (_, Cmd::Normal) => {
361 self.do_normal();
362 Next::BigSwitch
363 }
364 (_, Cmd::LuaFunctionCall | Cmd::LuaBytecodeCall | Cmd::LuaCall) => {
365 let n = if self.cur_cmd == Cmd::LuaFunctionCall
366 || self.cur_cmd == Cmd::LuaBytecodeCall
367 {
368 self.scan_int();
369 self.cur_val
370 } else {
371 self.cur_chr as i32
372 };
373 if self.cur_cmd == Cmd::LuaBytecodeCall {
374 self.call_lua_bytecode(n);
375 } else {
376 self.call_lua_function(n);
377 }
378 Next::BigSwitch
379 }
380 (_, Cmd::PartokenName) => {
381 self.partoken_name();
382 Next::BigSwitch
383 }
384 _ => {
385 self.typeset_command();
386 Next::BigSwitch
387 }
388 }
389 }
390
391 pub fn report_illegal_case(&mut self) {
394 self.you_cant();
395 self.help(&[
396 "Sorry, but I'm not programmed to handle this case;",
397 "I'll just pretend that you didn't ask for it.",
398 "If you're in the wrong mode, you might be able to",
399 "return to the right one by typing `I}' or `I$' or `I\\par'.",
400 ]);
401 self.error();
402 }
403
404 pub fn you_cant(&mut self) {
406 self.print_err("You can't use `");
407 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
408 self.printer.print_str("' in ");
409 self.print_mode(self.mode);
410 }
411
412 pub fn privileged(&mut self) -> bool {
415 if self.mode.is_privileged() {
416 true
417 } else {
418 self.report_illegal_case();
419 false
420 }
421 }
422
423 pub fn insert_dollar_sign(&mut self) {
425 self.back_input();
426 self.cur_tok = Token::char(Cmd::MathShift, '$' as u32);
427 self.print_err("Missing $ inserted");
428 self.help(&[
429 "I've inserted a begin-math/end-math symbol since I think",
430 "you left one out. Proceed, with fingers crossed.",
431 ]);
432 self.ins_error();
433 }
434
435 fn its_all_over(&mut self) -> bool {
438 if self.privileged() {
439 let cl = self.cur_list();
440 if self.page_is_empty() && cl.head == cl.tail && self.dead_cycles == 0 {
441 return true;
442 }
443 self.back_input();
444 let b = self.nodes.new_null_box(true);
445 let hsize = self.eqtb.dimen(pg::assign_dimen::HSIZE);
446 if let Some(bd) = self.nodes.box_data_mut(b) {
447 bd.width = hsize;
448 }
449 self.tail_append(b);
450 let g = self.nodes.new_glue(
451 crate::glue::Glue {
452 stretch: crate::glue::Glue::fixed(0).stretch + retex_arith::UNITY,
453 stretch_order: crate::glue::FILL,
454 ..crate::glue::Glue::ZERO
455 },
456 0,
457 );
458 self.tail_append(g);
459 let p = self.nodes.new_penalty(-0o10000000000, 0);
460 self.tail_append(p);
461 self.build_page();
462 }
463 false
464 }
465
466 pub fn handle_right_brace(&mut self) {
468 match self.save.cur_group {
469 GroupCode::Simple => {
470 self.end_group_text_dirs();
471 self.unsave();
472 }
473 GroupCode::BottomLevel => {
474 self.print_err("Too many }'s");
475 self.help(&[
476 "You've closed more groups than you opened.",
477 "Such booboos are generally harmless, so keep going.",
478 ]);
479 self.error();
480 }
481 GroupCode::SemiSimple | GroupCode::MathShift | GroupCode::MathLeft => {
482 self.extra_right_brace()
483 }
484 _ => self.handle_right_brace_typesetting(),
485 }
486 }
487
488 fn extra_right_brace(&mut self) {
489 self.print_err("Extra }, or forgotten ");
490 match self.save.cur_group {
491 GroupCode::SemiSimple => self.print_esc_str("endgroup"),
492 GroupCode::MathShift => self.printer.print_char('$' as u32),
493 _ => self.print_esc_str("right"),
494 }
495 self.help(&[
496 "I've deleted a group-closing symbol because it seems to be",
497 "spurious, as in `$x}$'. But perhaps the } is legitimate and",
498 "you forgot something else, as in `\\hbox{$x}'. In such cases",
499 "the way to recover is to insert both the forgotten and the",
500 "deleted material, e.g., by typing `I$}'.",
501 ]);
502 self.error();
503 self.input.align_state += 1;
504 }
505
506 pub fn off_save(&mut self) {
508 if self.save.cur_group == GroupCode::BottomLevel {
509 self.print_err("Extra ");
511 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
512 self.help(&["Things are pretty mixed up, but I think the worst is over."]);
513 self.error();
514 return;
515 }
516 self.back_input();
517 let mut list: Vec<Token> = Vec::new();
518 self.print_err("Missing ");
519 match self.save.cur_group {
520 GroupCode::SemiSimple => {
521 list.push(Token::cs(self.cs.frozen_end_group));
522 self.print_esc_str("endgroup");
523 }
524 GroupCode::MathShift => {
525 list.push(Token::char(Cmd::MathShift, '$' as u32));
526 self.printer.print_char('$' as u32);
527 }
528 GroupCode::MathLeft => {
529 list.push(Token::cs(self.cs.frozen_right));
530 list.push(Token::other('.' as u32));
531 self.print_esc_str("right.");
532 }
533 _ => {
534 list.push(Token::RIGHT_BRACE);
535 self.printer.print_char('}' as u32);
536 }
537 }
538 self.printer.print_str(" inserted");
539 self.ins_list(Rc::from(list));
540 self.help(&[
541 "I've inserted something that you may have forgotten.",
542 "(See the <inserted text> above.)",
543 "With luck, this will get me unwedged. But if you",
544 "really didn't forget anything, try typing `2' now; then",
545 "my insertion and my current dilemma will both disappear.",
546 ]);
547 self.error();
548 }
549
550 fn issue_message(&mut self) {
552 let c = self.cur_chr;
553 let toks = self.scan_toks_expanded();
554 let s = self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), 10_000_000));
555 if c == 0 {
556 if self.printer.term_offset + s.len() > self.printer.max_print_line.saturating_sub(2) {
557 self.printer.print_ln();
558 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
559 self.printer.print_char(' ' as u32);
560 }
561 self.printer.print_bytes(&s);
562 self.printer.update_terminal();
563 } else {
564 self.print_err("");
565 self.printer.print_bytes(&s);
566 let err_help = self.eqtb.toks_param(pg::assign_toks::ERRHELP);
567 if !err_help.is_empty() {
568 self.err.use_err_help = true;
569 } else if self.long_help_seen {
570 self.help(&["(That was another \\errmessage.)"]);
571 } else {
572 if self.err.interaction < crate::error::Interaction::ErrorStop {
573 self.long_help_seen = true;
574 }
575 self.help(&[
576 "This error message was generated by an \\errmessage",
577 "command, so I can't give any explicit help.",
578 "Pretend that you're Hercule Poirot: Examine all clues,",
579 "and deduce the truth by order and method.",
580 ]);
581 }
582 self.error();
583 self.err.use_err_help = false;
584 }
585 }
586
587 fn shift_case(&mut self) {
589 let b = self.cur_chr;
590 let toks = self.scan_general_text();
591 let upper = b == pg::def_char_code::UCCODE + UC_LC_OFFSET;
592 let mut out: Vec<Token> = Vec::with_capacity(toks.len());
593 for &t in toks.iter() {
594 if t.is_cs() {
595 if let crate::hash::CsKind::Active(c) = *self.hash.kind(t.cs_id()) {
596 let m = self.case_map(c, upper);
597 if m != 0 {
598 out.push(Token::cs(self.hash.active(m)));
599 continue;
600 }
601 }
602 out.push(t);
603 } else {
604 let c = t.chr();
605 let m = self.case_map(c, upper);
606 if m != 0 {
607 out.push(Token::char(t.cmd(), m));
608 } else {
609 out.push(t);
610 }
611 }
612 }
613 self.back_list(Rc::from(out));
614 }
615
616 fn case_map(&self, c: u32, upper: bool) -> u32 {
617 (if upper {
618 self.eqtb.uc.get(c)
619 } else {
620 self.eqtb.lc.get(c)
621 }) as u32
622 }
623
624 pub fn final_cleanup(&mut self) {
626 let c = self.cur_chr;
627 if c != 1 {
628 self.eqtb.int_par[pg::assign_int::NEWLINECHAR as usize].val = -1;
629 self.printer.new_line_char = -1;
630 }
631 if self.job_name.is_none() {
632 self.open_log_file();
633 }
634 while self.input.stack.len() > 1 {
635 match self.input.stack.last() {
636 Some(Level::Tokens(_)) => self.end_token_list(),
637 _ => self.end_file_reading(),
638 }
639 }
640 while self.input.open_parens > 0 {
641 self.printer.print_char(')' as u32);
643 self.input.open_parens -= 1;
644 }
645 if self.save.cur_level > crate::eqtb::LEVEL_ONE {
646 self.printer.print_nl("(");
647 self.print_esc_str("end occurred ");
648 self.printer.print_str("inside a group at level ");
649 self.printer
650 .print_int(i64::from(self.save.cur_level - crate::eqtb::LEVEL_ONE));
651 self.printer.print_char(')' as u32);
652 self.show_save_groups();
653 }
654 while !self.cond.stack.is_empty() {
655 self.printer.print_nl("(");
656 self.print_esc_str("end occurred ");
657 self.printer.print_str("when ");
658 self.print_cmd_chr(Cmd::IfTest, self.cond.cur_if);
659 if self.cond.if_line != 0 {
660 self.printer.print_str(" on line ");
661 self.printer.print_int(self.cond.if_line.into());
662 }
663 self.printer.print_str(" was incomplete)");
664 self.pop_cond();
665 }
666 if self.err.history != crate::error::History::Spotless
667 && (self.err.history == crate::error::History::WarningIssued
668 || self.err.interaction < crate::error::Interaction::ErrorStop)
669 && self.printer.selector == crate::print::Selector::TermAndLog
670 {
671 self.printer.selector = crate::print::Selector::TermOnly;
672 self.printer
673 .print_nl("(see the transcript file for additional information)");
674 self.printer.selector = crate::print::Selector::TermAndLog;
675 }
676 if c == 1 {
677 if self.ini_version {
678 self.dump_requested = true;
679 return;
680 }
681 self.printer
682 .print_nl("(\\dump is performed only by INITEX)");
683 }
684 }
685
686 fn partoken_name(&mut self) {
689 self.get_token();
690 if self.cur_cs != NO_CS {
691 self.par_token_cs = self.cur_cs;
692 }
693 }
694}
695
696pub const UC_LC_OFFSET: u32 = 333_280;
699
700pub enum Next {
702 BigSwitch,
704 Reswitch,
706 Return,
708}