1use std::rc::Rc;
11
12use crate::Engine;
13use crate::cmd::Cmd;
14use crate::hash::{CsId, NO_CS, NULL_CS};
15use crate::token::{Token, TokenList};
16use crate::utf8x;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum LexState {
21 MidLine,
23 SkipBlanks,
25 NewLine,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum TokKind {
32 Parameter,
34 UTemplate,
36 VTemplate,
38 BackedUp,
40 BackedUpChar,
42 Inserted,
44 Macro,
46 OutputText,
48 EveryParText,
50 EveryMathText,
52 EveryDisplayText,
54 EveryHboxText,
56 EveryVboxText,
58 EveryJobText,
60 EveryCrText,
62 MarkText,
64 EveryEofText,
66 WriteText,
68 LocalText,
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub enum CatMode {
75 Current,
77 Table(u32),
79 String,
81 Single(u8),
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub enum LuaLineKind {
89 Print,
91 Sprint,
93}
94
95#[derive(Clone, Debug)]
97pub enum LuaItem {
98 Text {
100 text: Vec<u8>,
102 kind: LuaLineKind,
104 catmode: CatMode,
106 },
107 Tokens(Vec<Token>),
109}
110
111#[derive(Clone, Debug)]
113pub enum LineSource {
114 File {
116 data: Rc<[u8]>,
118 pos: usize,
120 },
121 Terminal,
123 Read(u32),
125 Lines {
127 lines: std::collections::VecDeque<Vec<u8>>,
129 catcodes: Option<u32>,
131 pseudo: bool,
133 },
134 Lua(std::collections::VecDeque<LuaItem>),
136}
137
138#[derive(Clone, Debug)]
140pub struct TextLevel {
141 pub state: LexState,
143 pub buf: Vec<u32>,
146 pub loc: usize,
148 pub source: LineSource,
150 pub line: i32,
152 pub name: Vec<u8>,
154 pub is_input_file: bool,
156 pub eof_seen: bool,
158 pub grp_boundary: usize,
160 pub if_depth: usize,
162 pub catmode: CatMode,
164 pub sync: (u32, i32),
167}
168
169#[derive(Clone, Debug)]
171pub struct TokLevel {
172 pub list: TokenList,
174 pub loc: usize,
176 pub kind: TokKind,
178 pub param_start: usize,
180 pub name: CsId,
182}
183
184#[derive(Clone, Debug)]
186pub enum Level {
187 Text(Box<TextLevel>),
189 Tokens(TokLevel),
191}
192
193#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
195pub enum ScannerStatus {
196 Normal,
198 Skipping,
200 Defining,
202 Matching,
204 Aligning,
206 Absorbing,
208}
209
210const INPUT_STACK_SIZE: usize = 10_000;
213
214pub const MAX_IN_OPEN: usize = 15;
217
218fn counts_as_open(source: &LineSource) -> bool {
222 match source {
223 LineSource::File { .. } | LineSource::Read(_) => true,
224 LineSource::Lines { pseudo, .. } => *pseudo,
225 _ => false,
226 }
227}
228
229const SPARE_TOKENS: usize = 16;
231
232#[derive(Debug)]
234pub struct InputState {
235 pub stack: Vec<Level>,
237 pub param_stack: Vec<TokenList>,
239 pub align_state: i32,
241 pub scanner_status: ScannerStatus,
243 pub warning_index: CsId,
245 pub open_parens: usize,
247 pub force_eof: bool,
249 pub last_line: i32,
251 pub in_open: usize,
254 pub spare_tokens: Vec<TokenList>,
257}
258
259impl Default for InputState {
260 fn default() -> Self {
261 InputState {
262 stack: Vec::new(),
263 param_stack: Vec::new(),
264 align_state: 1_000_000,
265 scanner_status: ScannerStatus::Normal,
266 warning_index: NO_CS,
267 open_parens: 0,
268 force_eof: false,
269 last_line: 0,
270 in_open: 0,
271 spare_tokens: Vec::new(),
272 }
273 }
274}
275
276pub const NO_EXPAND_FLAG: u32 = 0x11_0001;
279
280impl InputState {
281 pub fn in_open(&self) -> usize {
283 self.stack
284 .iter()
285 .filter(|l| matches!(l, Level::Text(t) if t.is_input_file))
286 .count()
287 }
288
289 pub fn cur_text(&self) -> Option<&TextLevel> {
291 self.stack.iter().rev().find_map(|l| match l {
292 Level::Text(t) => Some(&**t),
293 Level::Tokens(_) => None,
294 })
295 }
296
297 pub fn line(&self) -> i32 {
299 self.stack
300 .iter()
301 .rev()
302 .find_map(|l| match l {
303 Level::Text(t) if !matches!(t.source, LineSource::Read(_)) => Some(t.line),
304 _ => None,
305 })
306 .unwrap_or(0)
307 }
308
309 pub fn in_token_list(&self) -> bool {
311 matches!(self.stack.last(), Some(Level::Tokens(_)))
312 }
313
314 pub fn terminal_input(&self) -> bool {
316 matches!(self.stack.last(), Some(Level::Text(t)) if matches!(t.source, LineSource::Terminal))
317 }
318}
319
320pub fn next_line(data: &[u8], pos: usize) -> Option<(&[u8], usize)> {
323 if pos >= data.len() {
324 return None;
325 }
326 let rest = &data[pos..];
327 match rest.iter().position(|&b| b == b'\n' || b == b'\r') {
328 Some(i) => {
329 let mut next = pos + i + 1;
330 if rest[i] == b'\r' && rest.get(i + 1) == Some(&b'\n') {
331 next += 1;
332 }
333 Some((&rest[..i], next))
334 }
335 None => Some((rest, data.len())),
336 }
337}
338
339pub fn decode_line(bytes: &[u8]) -> Vec<u32> {
342 let mut end = bytes.len();
343 while end > 0 && bytes[end - 1] == b' ' {
344 end -= 1;
345 }
346 utf8x::chars_marking_invalid(&bytes[..end])
347}
348
349fn is_hex(c: u32) -> bool {
350 matches!(c, 0x30..=0x39 | 0x61..=0x66)
351}
352
353fn hex_val(c: u32) -> u32 {
354 if c <= 0x39 { c - 0x30 } else { c - 0x61 + 10 }
355}
356
357enum Expanded {
359 None,
361 Char { c: u32, len: usize },
363 Bad { six: bool },
365}
366
367fn expanded_at(buf: &[u32], k: usize) -> Expanded {
370 let sup = buf[k];
371 if buf.get(k + 1) != Some(&sup) {
372 return Expanded::None;
373 }
374 let run = buf[k..].iter().take(6).take_while(|&&c| c == sup).count();
376 if run >= 4 {
377 let six = run == 6;
378 let n = if six { 6 } else { 4 };
379 let start = k + run;
380 let digits = buf.get(start..start + n);
381 return match digits {
382 Some(d) if d.iter().all(|&c| is_hex(c)) => {
383 let c = d.iter().fold(0u32, |acc, &c| acc * 16 + hex_val(c));
384 if c > 0x10_FFFF {
385 Expanded::Bad { six }
386 } else {
387 Expanded::Char { c, len: run + n }
388 }
389 }
390 _ => Expanded::Bad { six },
391 };
392 }
393 let Some(&c) = buf.get(k + 2) else {
395 return Expanded::None;
396 };
397 if c >= 0o200 {
398 return Expanded::None;
399 }
400 if is_hex(c)
401 && let Some(&cc) = buf.get(k + 3)
402 && is_hex(cc)
403 {
404 return Expanded::Char {
405 c: hex_val(c) * 16 + hex_val(cc),
406 len: 4,
407 };
408 }
409 Expanded::Char {
410 c: if c < 0o100 { c + 0o100 } else { c - 0o100 },
411 len: 3,
412 }
413}
414
415impl Engine {
416 pub fn begin_token_list(&mut self, list: TokenList, kind: TokKind) {
420 let name = match kind {
423 TokKind::OutputText => Some("output"),
424 TokKind::EveryParText => Some("everypar"),
425 TokKind::EveryMathText => Some("everymath"),
426 TokKind::EveryDisplayText => Some("everydisplay"),
427 TokKind::EveryHboxText => Some("everyhbox"),
428 TokKind::EveryVboxText => Some("everyvbox"),
429 TokKind::EveryJobText => Some("everyjob"),
430 TokKind::EveryCrText => Some("everycr"),
431 TokKind::EveryEofText => Some("everyeof"),
432 TokKind::MarkText => Some("mark"),
433 TokKind::WriteText => Some("write"),
434 _ => None,
435 };
436 if let Some(name) = name
437 && self
438 .eqtb
439 .int(crate::primitives_gen::assign_int::TRACINGMACROS)
440 > 1
441 {
442 self.begin_diagnostic();
443 self.printer.print_nl("");
444 self.print_esc_str(name);
445 self.printer.print_str("->");
446 self.token_show_list(&list, 0, list.len(), 10_000_000);
447 self.end_diagnostic(false);
448 }
449 self.push_input(Level::Tokens(TokLevel {
450 list,
451 loc: 0,
452 kind,
453 param_start: self.input.param_stack.len(),
454 name: NO_CS,
455 }));
456 }
457
458 fn push_input(&mut self, level: Level) {
461 if self.input.stack.len() > INPUT_STACK_SIZE {
462 self.overflow("input stack size", INPUT_STACK_SIZE as i64);
463 }
464 self.input.stack.push(level);
465 }
466
467 pub fn begin_macro(&mut self, list: TokenList, start: usize, name: CsId, param_start: usize) {
470 self.push_input(Level::Tokens(TokLevel {
471 list,
472 loc: start,
473 kind: TokKind::Macro,
474 param_start,
475 name,
476 }));
477 }
478
479 pub fn ins_list(&mut self, list: TokenList) {
481 self.begin_token_list(list, TokKind::Inserted);
482 }
483
484 pub fn end_token_list(&mut self) {
486 if let Some(Level::Tokens(t)) = self.input.stack.pop() {
487 if matches!(t.kind, TokKind::BackedUp | TokKind::Inserted)
488 && t.list.len() == 1
489 && Rc::strong_count(&t.list) == 1
490 && self.input.spare_tokens.len() < SPARE_TOKENS
491 {
492 self.input.spare_tokens.push(t.list);
493 self.check_interrupt();
494 return;
495 }
496 match t.kind {
497 TokKind::Macro => {
498 self.input.param_stack.truncate(t.param_start);
499 }
500 TokKind::UTemplate => {
501 if self.input.align_state > 500_000 {
502 self.input.align_state = 0;
503 } else {
504 self.fatal_error("(interwoven alignment preambles are not allowed)");
505 }
506 }
507 _ => {}
508 }
509 } else {
510 debug_assert!(false, "end_token_list on a text level");
511 }
512 self.check_interrupt();
513 }
514
515 pub fn back_input(&mut self) {
517 while let Some(Level::Tokens(t)) = self.input.stack.last() {
519 if t.loc < t.list.len() || t.kind == TokKind::VTemplate {
520 break;
521 }
522 self.end_token_list();
523 }
524 let tok = self.cur_tok;
525 if tok.0 < crate::token::CS_TOKEN_FLAG {
526 let cmd = tok.cmd_code();
527 if cmd == Cmd::RightBrace as u32 {
528 self.input.align_state += 1;
529 } else if cmd == Cmd::LeftBrace as u32 {
530 self.input.align_state -= 1;
531 }
532 }
533 let list = match self.input.spare_tokens.pop() {
534 Some(mut list) => {
535 if let Some(slot) = Rc::get_mut(&mut list) {
537 slot[0] = tok;
538 list
539 } else {
540 Rc::from([tok])
541 }
542 }
543 None => Rc::from([tok]),
544 };
545 self.push_input(Level::Tokens(TokLevel {
546 list,
547 loc: 0,
548 kind: TokKind::BackedUp,
549 param_start: self.input.param_stack.len(),
550 name: NO_CS,
551 }));
552 }
553
554 pub fn back_input_inserted(&mut self) {
557 self.back_input();
558 if let Some(Level::Tokens(t)) = self.input.stack.last_mut() {
559 t.kind = TokKind::Inserted;
560 }
561 }
562
563 pub fn begin_file_reading(&mut self, source: LineSource, name: Vec<u8>, is_input_file: bool) {
566 if counts_as_open(&source) {
567 self.check_in_open();
568 self.input.in_open += 1;
569 }
570 self.push_input(Level::Text(Box::new(TextLevel {
571 state: LexState::NewLine,
572 buf: Vec::new(),
573 loc: 0,
574 source,
575 line: 0,
576 name,
577 is_input_file,
578 eof_seen: false,
579 grp_boundary: self.save.cur_boundary,
580 if_depth: self.cond.stack.len(),
581 catmode: CatMode::Current,
582 sync: (0, 0),
583 })));
584 self.sync_input_changed();
585 }
586
587 pub(crate) fn check_in_open(&mut self) {
590 if self.input.in_open >= MAX_IN_OPEN {
591 self.overflow("text input levels", MAX_IN_OPEN as i64);
592 }
593 }
594
595 pub fn end_file_reading(&mut self) {
597 let lvl = self.input.stack.pop();
598 if let Some(Level::Text(t)) = lvl
599 && counts_as_open(&t.source)
600 {
601 self.input.in_open = self.input.in_open.saturating_sub(1);
602 }
603 self.sync_input_changed();
604 }
605
606 fn read_next_line(&mut self) -> bool {
609 let end_line_char = self
610 .eqtb
611 .int(crate::primitives_gen::assign_int::ENDLINECHAR);
612 let Some(Level::Text(t)) = self.input.stack.last_mut() else {
613 return false;
614 };
615 let mut eol = true;
617 let line: Vec<u32> = match &mut t.source {
618 LineSource::File { data, pos } => match next_line(data, *pos) {
619 Some((bytes, next)) => {
620 let l = decode_line(bytes);
621 *pos = next;
622 l
623 }
624 None => return false,
625 },
626 LineSource::Lines { lines, pseudo, .. } => match lines.pop_front() {
627 Some(bytes) => {
628 eol = *pseudo || !lines.is_empty();
629 decode_line(&bytes)
630 }
631 None => return false,
632 },
633 LineSource::Lua(items) => {
634 let last = items.len() == 1;
635 match items.pop_front() {
636 Some(LuaItem::Text {
637 text,
638 kind,
639 catmode,
640 }) => {
641 t.catmode = catmode;
642 t.loc = 0;
643 t.sync.1 += 1;
644 t.line += 1;
648 if kind == LuaLineKind::Sprint {
649 t.buf = utf8x::chars(&text);
650 t.state = LexState::MidLine;
651 self.sync_input_changed();
652 return true;
653 }
654 t.buf = decode_line(&text);
655 t.state = LexState::NewLine;
656 if !last && (0..=0x10_FFFF).contains(&end_line_char) {
657 t.buf.push(end_line_char as u32);
658 }
659 self.sync_input_changed();
660 return true;
661 }
662 Some(LuaItem::Tokens(toks)) => {
663 t.buf.clear();
664 t.loc = 0;
665 t.line += 1;
666 self.pending_lua_tokens = Some(toks);
667 return true;
668 }
669 None => return false,
670 }
671 }
672 LineSource::Terminal | LineSource::Read(_) => return false,
673 };
674 t.buf = line;
675 if eol && (0..=0x10_FFFF).contains(&end_line_char) {
676 t.buf.push(end_line_char as u32);
677 }
678 t.loc = 0;
679 t.line += 1;
680 t.sync.1 = t.line;
681 self.sync_input_changed();
682 true
683 }
684
685 #[inline]
690 pub fn get_next(&mut self) {
691 if let Some(Level::Tokens(t)) = self.input.stack.last_mut()
695 && let Some(&tok) = t.list.get(t.loc)
696 {
697 if tok.is_cs() {
698 let cs = tok.cs_id();
699 let m = self.eqtb.meaning(cs);
700 if m.cmd < Cmd::TabMark || (m.cmd > Cmd::CarRet && !m.cmd.is_outer_or_special()) {
701 t.loc += 1;
702 self.cur_cs = cs;
703 self.cur_cmd = m.cmd;
704 self.cur_chr = m.chr;
705 return;
706 }
707 } else {
708 let cmd = tok.cmd();
709 if cmd > Cmd::CarRet || cmd < Cmd::MathShift {
710 t.loc += 1;
711 match cmd {
712 Cmd::LeftBrace => self.input.align_state += 1,
713 Cmd::RightBrace => self.input.align_state -= 1,
714 _ => {}
715 }
716 self.cur_cs = NO_CS;
717 self.cur_cmd = cmd;
718 self.cur_chr = tok.chr();
719 return;
720 }
721 }
722 }
723 self.get_next_slow();
724 }
725
726 #[inline(never)]
728 fn get_next_slow(&mut self) {
729 'restart: loop {
730 self.cur_cs = NO_CS;
731 let Some(top) = self.input.stack.last_mut() else {
732 self.cur_cmd = Cmd::Relax;
734 self.cur_chr = 0;
735 self.fatal_error("*** (job aborted, no legal \\end found)");
736 };
737 match top {
738 Level::Tokens(t) => {
739 if t.loc < t.list.len() {
740 let tok = t.list[t.loc];
741 t.loc += 1;
742 if tok.is_cs() {
743 let cs = tok.cs_id();
744 self.cur_cs = cs;
745 let m = self.eqtb.meaning(cs);
746 self.cur_cmd = m.cmd;
747 self.cur_chr = m.chr;
748 if self.cur_cmd.is_outer_or_special() {
749 if self.cur_cmd == Cmd::DontExpand {
750 let next = t.list[t.loc];
753 t.loc = t.list.len();
754 let cs = next.cs_id();
755 self.cur_cs = cs;
756 let m = self.eqtb.meaning(cs);
757 self.cur_cmd = m.cmd;
758 self.cur_chr = m.chr;
759 if self.cur_cmd > Cmd::MAX_COMMAND {
760 self.cur_cmd = Cmd::Relax;
761 self.cur_chr = NO_EXPAND_FLAG;
762 }
763 } else {
764 self.check_outer_validity();
765 }
766 }
767 } else {
768 self.cur_cmd = tok.cmd();
769 self.cur_chr = tok.chr();
770 match self.cur_cmd {
771 Cmd::LeftBrace => self.input.align_state += 1,
772 Cmd::RightBrace => self.input.align_state -= 1,
773 Cmd::CarRet => {
774 let p = (self.cur_chr as usize)
780 .checked_sub(1)
781 .and_then(|i| self.input.param_stack.get(t.param_start + i))
782 .cloned();
783 if let Some(p) = p {
784 self.begin_token_list(p, TokKind::Parameter);
785 }
786 continue 'restart;
787 }
788 _ => {}
789 }
790 }
791 } else {
792 self.end_token_list();
793 continue 'restart;
794 }
795 }
796 Level::Text(_) => {
797 if !self.get_next_from_text() {
798 continue 'restart;
799 }
800 }
801 }
802 if self.cur_cmd <= Cmd::CarRet
804 && self.cur_cmd >= Cmd::TabMark
805 && self.input.align_state == 0
806 {
807 self.insert_v_template();
808 continue 'restart;
809 }
810 return;
811 }
812 }
813
814 fn get_next_from_text(&mut self) -> bool {
817 loop {
818 let (c, catmode, state) = {
820 let Some(Level::Text(t)) = self.input.stack.last_mut() else {
821 return false;
822 };
823 if t.loc < t.buf.len() {
824 let c = t.buf[t.loc];
825 t.loc += 1;
826 (c, t.catmode, t.state)
827 } else {
828 if matches!(t.source, LineSource::Read(_)) {
829 self.cur_cmd = Cmd::Relax;
832 self.cur_chr = 0;
833 return true;
834 }
835 if !self.next_line_or_pop() {
836 return false;
837 }
838 continue;
839 }
840 };
841 let c = if utf8x::is_raw(c) {
842 self.invalid_utf8_sequence()
843 } else {
844 c
845 };
846 self.cur_chr = c;
847 let mut c = c;
849 loop {
850 let cat = self.catcode_in_mode(c, catmode);
851 self.cur_cmd = Cmd::from_u8(cat);
852 match (cat, state) {
853 (9, _) | (10, LexState::SkipBlanks | LexState::NewLine) => break,
856 (0, _) => {
857 self.scan_control_sequence();
858 return true;
859 }
860 (13, _) => {
861 let cs = self.hash.active(c);
862 self.cur_cs = cs;
863 let m = self.eqtb.meaning(cs);
864 self.cur_cmd = m.cmd;
865 self.cur_chr = m.chr;
866 self.set_state(LexState::MidLine);
867 if self.cur_cmd.is_outer_or_special() {
868 self.check_outer_validity();
869 }
870 return true;
871 }
872 (7, _) => {
873 match self.expanded_in_line() {
874 Expanded::Char { c: nc, len } => {
875 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
876 t.loc += len - 1;
877 }
878 c = nc;
879 self.cur_chr = c;
880 continue; }
882 Expanded::Bad { six } => {
883 self.print_err(if six {
884 "^^^^^^ needs six hex digits"
885 } else {
886 "^^^^ needs four hex digits"
887 });
888 self.help(&[]);
889 self.error();
890 self.set_state(LexState::MidLine);
891 return true;
892 }
893 Expanded::None => {
894 self.set_state(LexState::MidLine);
895 return true;
896 }
897 }
898 }
899 (15, _) => {
900 self.print_err("Text line contains an invalid character");
901 self.help(&[
902 "A funny symbol that I can't read has just been input.",
903 "Continue, and I'll forget that it ever happened.",
904 ]);
905 self.err.deletions_allowed = false;
906 self.error();
907 self.err.deletions_allowed = true;
908 return false;
909 }
910 (10, LexState::MidLine) => {
911 self.set_state(LexState::SkipBlanks);
912 self.cur_chr = ' ' as u32;
913 return true;
914 }
915 (5, LexState::MidLine) => {
916 self.finish_line();
917 self.cur_cmd = Cmd::Spacer;
918 self.cur_chr = ' ' as u32;
919 return true;
920 }
921 (5, LexState::SkipBlanks) | (14, _) => {
922 self.finish_line();
923 break;
924 }
925 (5, LexState::NewLine) => {
926 self.finish_line();
927 let par = self.cs.par;
928 self.cur_cs = par;
929 let m = self.eqtb.meaning(par);
930 self.cur_cmd = m.cmd;
931 self.cur_chr = m.chr;
932 if self.cur_cmd.is_outer_or_special() {
933 self.check_outer_validity();
934 }
935 return true;
936 }
937 (1, s) => {
938 if s != LexState::MidLine {
939 self.set_state(LexState::MidLine);
940 }
941 self.input.align_state += 1;
942 return true;
943 }
944 (2, s) => {
945 if s != LexState::MidLine {
946 self.set_state(LexState::MidLine);
947 }
948 self.input.align_state -= 1;
949 return true;
950 }
951 (_, s) => {
952 if s != LexState::MidLine {
953 self.set_state(LexState::MidLine);
954 }
955 return true;
956 }
957 }
958 }
959 }
960 }
961
962 #[cold]
967 fn invalid_utf8_sequence(&mut self) -> u32 {
968 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
970 t.loc -= 1;
971 }
972 self.print_err("String contains an invalid utf-8 sequence");
973 self.help(&[
974 "A funny symbol that I can't read has just been (re)read.",
975 "Just continue, I'll change it to 0xFFFD.",
976 ]);
977 self.error();
978 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
979 let mut bytes = 0;
980 while bytes < 3 && t.loc < t.buf.len() {
981 let c = t.buf[t.loc];
982 bytes += if utf8x::is_raw(c) { 1 } else { utf8x::len(c) };
983 t.loc += 1;
984 }
985 }
986 0xFFFD
987 }
988
989 #[inline]
991 pub fn catcode_in_mode(&self, c: u32, mode: CatMode) -> u8 {
992 match mode {
993 CatMode::Current => self.eqtb.catcode(c),
994 CatMode::Table(t) => self.eqtb.catcode_in(t, c),
995 CatMode::String => {
996 if c == ' ' as u32 {
997 10
998 } else {
999 12
1000 }
1001 }
1002 CatMode::Single(k) => k,
1003 }
1004 }
1005
1006 pub fn push_lua_output(&mut self, items: Vec<LuaItem>) {
1008 if items.is_empty() {
1009 return;
1010 }
1011 self.begin_file_reading(
1012 LineSource::Lua(items.into_iter().collect()),
1013 Vec::new(),
1014 false,
1015 );
1016 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1017 t.line = self.input_line_for_lua;
1018 }
1019 }
1020
1021 fn set_state(&mut self, s: LexState) {
1022 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1023 t.state = s;
1024 }
1025 }
1026
1027 fn finish_line(&mut self) {
1028 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1029 t.loc = t.buf.len();
1030 }
1031 }
1032
1033 fn expanded_in_line(&self) -> Expanded {
1035 let Some(Level::Text(t)) = self.input.stack.last() else {
1036 return Expanded::None;
1037 };
1038 expanded_at(&t.buf, t.loc - 1)
1039 }
1040
1041 fn next_line_or_pop(&mut self) -> bool {
1045 let (is_terminal, is_read, is_file) = match self.input.stack.last() {
1046 Some(Level::Text(t)) => (
1047 matches!(t.source, LineSource::Terminal),
1048 matches!(t.source, LineSource::Read(_)),
1049 t.is_input_file,
1050 ),
1051 _ => return false,
1052 };
1053 if is_read {
1054 return false;
1055 }
1056 if is_terminal {
1057 if self.input.stack.len() > 1 {
1059 self.end_file_reading();
1061 return false;
1062 }
1063 self.terminal_exhausted();
1064 return false;
1065 }
1066 let is_lua = matches!(
1067 self.input.stack.last(),
1068 Some(Level::Text(t)) if matches!(t.source, LineSource::Lua(_))
1069 );
1070 if !self.input.force_eof && self.read_next_line() {
1071 if let Some(toks) = self.pending_lua_tokens.take() {
1072 self.begin_token_list(std::rc::Rc::from(toks), TokKind::Inserted);
1073 return false;
1074 }
1075 if !is_lua {
1076 self.set_state(LexState::NewLine);
1077 }
1078 return true;
1079 }
1080 let is_pseudo = matches!(
1082 self.input.stack.last(),
1083 Some(Level::Text(t)) if matches!(t.source, LineSource::Lines { pseudo: true, .. })
1084 );
1085 if !self.input.force_eof && (is_file || is_pseudo) {
1086 let every_eof = self
1089 .eqtb
1090 .toks_param(crate::primitives_gen::assign_toks::EVERYEOF)
1091 .clone();
1092 let seen = match self.input.stack.last_mut() {
1093 Some(Level::Text(t)) => std::mem::replace(&mut t.eof_seen, true),
1094 _ => true,
1095 };
1096 if !seen && !every_eof.is_empty() {
1097 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1098 t.buf.clear();
1099 t.loc = 0;
1100 }
1101 self.begin_token_list(every_eof, TokKind::EveryEofText);
1102 return false;
1103 }
1104 }
1105 if is_file {
1106 self.printer.print_char(')' as u32);
1107 if is_pseudo {
1108 self.printer.print_char(')' as u32);
1111 }
1112 self.input.open_parens -= 1;
1113 self.printer.update_terminal();
1114 self.file_closed_checks();
1115 }
1116 self.input.force_eof = false;
1117 let check = match self.input.stack.last() {
1120 Some(Level::Text(t)) => match &t.source {
1121 LineSource::Lua(_) => false,
1122 LineSource::Lines { pseudo, .. } => *pseudo,
1123 _ => true,
1124 },
1125 _ => true,
1126 };
1127 self.end_file_reading();
1128 if check {
1129 self.check_outer_validity();
1130 }
1131 false
1132 }
1133
1134 fn scan_control_sequence(&mut self) {
1136 'start_cs: loop {
1137 let Some(Level::Text(t)) = self.input.stack.last() else {
1138 return;
1139 };
1140 let len = t.buf.len();
1141 let loc = t.loc;
1142 let catmode = t.catmode;
1143 if loc >= len {
1144 self.cur_cs = NULL_CS;
1145 break;
1146 }
1147 let mut k = loc;
1148 let mut ch = t.buf[k];
1149 let mut cat = self.catcode_in_mode(ch, catmode);
1150 k += 1;
1151 let state = if cat == 11 || cat == 10 {
1152 LexState::SkipBlanks
1153 } else {
1154 LexState::MidLine
1155 };
1156 if cat == 11 && k < len {
1157 loop {
1159 ch = t.buf[k];
1160 cat = self.catcode_in_mode(ch, catmode);
1161 k += 1;
1162 if cat != 11 || k >= len {
1163 break;
1164 }
1165 }
1166 if self.reduce_expanded(k, ch, cat) {
1167 continue 'start_cs;
1168 }
1169 if cat != 11 {
1170 k -= 1;
1171 }
1172 if k > loc + 1 {
1173 let mut name = Vec::with_capacity(k - loc);
1174 if let Some(Level::Text(t)) = self.input.stack.last() {
1175 for &c in &t.buf[loc..k] {
1176 utf8x::push(&mut name, c);
1177 }
1178 }
1179 self.cur_cs = self.hash.lookup_or_insert(&name);
1180 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1181 t.loc = k;
1182 t.state = state;
1183 }
1184 break;
1185 }
1186 } else if self.reduce_expanded(k, ch, cat) {
1187 continue 'start_cs;
1188 }
1189 let c = match self.input.stack.last() {
1190 Some(Level::Text(t)) => t.buf[loc],
1191 _ => return,
1192 };
1193 self.cur_cs = self.hash.single(c);
1194 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
1195 t.loc = loc + 1;
1196 t.state = state;
1197 }
1198 break;
1199 }
1200 let m = self.eqtb.meaning(self.cur_cs);
1201 self.cur_cmd = m.cmd;
1202 self.cur_chr = m.chr;
1203 if self.cur_cmd.is_outer_or_special() {
1204 self.check_outer_validity();
1205 }
1206 }
1207
1208 fn reduce_expanded(&mut self, k: usize, ch: u32, cat: u8) -> bool {
1212 if cat != 7 {
1213 return false;
1214 }
1215 let Some(Level::Text(t)) = self.input.stack.last_mut() else {
1216 return false;
1217 };
1218 if t.buf.get(k) != Some(&ch) {
1219 return false;
1220 }
1221 match expanded_at(&t.buf, k - 1) {
1222 Expanded::Char { c, len } => {
1223 t.buf[k - 1] = c;
1224 t.buf.drain(k..k - 1 + len);
1225 true
1226 }
1227 _ => false,
1228 }
1229 }
1230
1231 pub fn get_token(&mut self) {
1233 self.err.no_new_control_sequence = false;
1234 self.get_next();
1235 self.err.no_new_control_sequence = true;
1236 self.cur_tok = self.make_cur_tok();
1237 }
1238
1239 #[inline]
1241 pub fn make_cur_tok(&self) -> Token {
1242 if self.cur_cs == NO_CS {
1243 Token::char(self.cur_cmd, self.cur_chr)
1244 } else {
1245 Token::cs(self.cur_cs)
1246 }
1247 }
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252 use super::*;
1253
1254 #[test]
1255 fn line_splitting() {
1256 let data = b"ab \r\ncd\rx\n\nlast";
1257 let mut pos = 0;
1258 let mut lines = Vec::new();
1259 while let Some((l, next)) = next_line(data, pos) {
1260 lines.push(decode_line(l));
1261 pos = next;
1262 }
1263 let strs: Vec<String> = lines
1264 .iter()
1265 .map(|l| l.iter().map(|&c| char::from_u32(c).unwrap()).collect())
1266 .collect();
1267 assert_eq!(strs, vec!["ab", "cd", "x", "", "last"]);
1268 assert_eq!(decode_line(b"x\t "), vec!['x' as u32, 9]);
1269 }
1270
1271 #[test]
1272 fn expanded_chars() {
1273 let s = |x: &str| x.chars().map(|c| c as u32).collect::<Vec<_>>();
1274 assert!(matches!(
1275 expanded_at(&s("^^A"), 0),
1276 Expanded::Char { c: 1, len: 3 }
1277 ));
1278 assert!(matches!(
1279 expanded_at(&s("^^e9"), 0),
1280 Expanded::Char { c: 0xE9, len: 4 }
1281 ));
1282 assert!(matches!(
1283 expanded_at(&s("^^?"), 0),
1284 Expanded::Char { c: 127, len: 3 }
1285 ));
1286 assert!(matches!(
1287 expanded_at(&s("^^^^00e9"), 0),
1288 Expanded::Char { c: 0xE9, len: 8 }
1289 ));
1290 assert!(matches!(
1291 expanded_at(&s("^^^^^^01f600"), 0),
1292 Expanded::Char {
1293 c: 0x1F600,
1294 len: 12
1295 }
1296 ));
1297 assert!(matches!(
1298 expanded_at(&s("^^^^zz"), 0),
1299 Expanded::Bad { six: false }
1300 ));
1301 assert!(matches!(expanded_at(&s("^x"), 0), Expanded::None));
1302 }
1303}