1use std::fmt::{Display, Formatter};
4use crate::fixed_sym_table::{FixedSymTable, SymInfoTable};
5use crate::{AltId, TokenId, VarId};
6use crate::lexer::{Pos, PosSpan};
7use crate::log::{LogMsg, Logger};
8use crate::alt::Alternative;
9
10pub(crate) mod tests;
11
12#[derive(Clone, Copy, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
15pub enum Symbol {
16 T(TokenId), NT(VarId), #[default] Empty, End }
21
22impl Symbol {
23 pub fn is_end(&self) -> bool {
24 matches!(self, Symbol::End)
25 }
26
27 pub fn is_empty(&self) -> bool {
28 matches!(self, Symbol::Empty)
29 }
30
31 pub fn is_t(&self) -> bool {
32 matches!(self, Symbol::T(_))
33 }
34
35 pub fn is_nt(&self) -> bool {
36 matches!(self, Symbol::NT(_))
37 }
38
39 pub fn is_t_or_nt(&self) -> bool {
40 matches!(self, Symbol::T(_) | Symbol::NT(_))
41 }
42
43 pub fn to_str<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
44 symbol_table.map(|t| t.get_str(self)).unwrap_or_else(|| self.to_string())
45 }
46
47 pub fn to_str_quote<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
50 symbol_table.map(|t| t.get_name_quote(self)).unwrap_or_else(|| self.to_string())
51 }
52
53 pub fn to_str_name<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
54 symbol_table.map(|t| t.get_name(self)).unwrap_or_else(|| self.to_string())
55 }
56
57 pub fn to_str_ext<T: SymInfoTable>(&self, symbol_table: Option<&T>, ext: &String) -> String {
59 let mut result = self.to_str(symbol_table);
60 if let Some(t) = symbol_table {
61 if t.is_symbol_t_data(self) {
62 result.push_str(&format!("({ext})"));
63 }
64 }
65 result
66 }
67
68 pub fn to_macro_item(&self) -> String {
70 match self {
71 Symbol::Empty => "e".to_string(),
72 Symbol::T(x) => format!("t {x}"),
73 Symbol::NT(x) => format!("nt {x}"),
74 Symbol::End => "end".to_string(),
75 }
76 }
77}
78
79impl Display for Symbol {
80 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Symbol::Empty => write!(f, "ε"),
83 Symbol::T(id) => write!(f, ":{id}"),
84 Symbol::NT(id) => write!(f, "{id}"),
85 Symbol::End => write!(f, "$"),
86 }
87 }
88}
89
90#[derive(Clone, Copy, PartialEq, Debug)]
91pub enum OpCode {
92 Empty, T(TokenId), NT(VarId), Loop(VarId), Exit(VarId), Hook, End, }
100
101
102impl Display for OpCode {
103 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
104 match self {
105 OpCode::Empty => write!(f, "ε"),
106 OpCode::T(t) => write!(f, ":{t}"),
107 OpCode::NT(v) => write!(f, "►{v}"),
108 OpCode::Loop(v) => write!(f, "●{v}"),
109 OpCode::Exit(v) => write!(f, "◄{v}"),
110 OpCode::Hook => write!(f, "▲"),
111 OpCode::End => write!(f, "$"),
112 }
113 }
114}
115
116impl OpCode {
117 pub fn is_loop(&self) -> bool {
118 matches!(self, OpCode::Loop(_))
119 }
120
121 pub fn is_empty(&self) -> bool {
122 matches!(self, OpCode::Empty)
123 }
124
125 pub fn has_span(&self) -> bool {
126 matches!(self, OpCode::T(_) | OpCode::NT(_))
127 }
128
129 pub fn matches(&self, s: Symbol) -> bool {
130 match self {
131 OpCode::Empty => s == Symbol::Empty,
132 OpCode::T(t) => s == Symbol::T(*t),
133 OpCode::NT(v) => s == Symbol::NT(*v),
134 OpCode::End => s == Symbol::End,
135 OpCode::Loop(_)
136 | OpCode::Exit(_)
137 | OpCode::Hook => false,
138 }
139 }
140
141 pub fn to_str<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
142 if let Some(t) = symbol_table {
143 match self {
144 OpCode::Empty => "ε".to_string(),
145 OpCode::T(v) => format!("{}{}", t.get_t_str(*v), if t.is_token_data(*v) { "!" } else { "" }),
146 OpCode::NT(v) => format!("►{}", t.get_nt_name(*v)),
147 OpCode::Loop(v) => format!("●{}", t.get_nt_name(*v)),
148 OpCode::Exit(f) => format!("◄{f}"),
149 OpCode::Hook => "▲".to_string(),
150 OpCode::End => "$".to_string(),
151 }
152 } else {
153 self.to_string()
154 }
155 }
156
157 pub fn to_str_name<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
158 if let Some(tbl) = symbol_table {
159 match self {
160 OpCode::T(v) => tbl.get_t_str(*v),
161 _ => self.to_str(symbol_table),
162 }
163 } else {
164 self.to_string()
165 }
166 }
167
168 pub fn to_str_quote<T: SymInfoTable>(&self, symbol_table: Option<&T>) -> String {
169 if let Some(t) = symbol_table {
170 match self {
171 OpCode::T(v) => format!("{}{}", Symbol::T(*v).to_str_quote(symbol_table), if t.is_token_data(*v) { "!" } else { "" }),
172 _ => self.to_str(symbol_table)
173 }
174 } else {
175 self.to_string()
176 }
177 }
178
179 pub fn to_str_ext<T: SymInfoTable>(&self, symbol_table: Option<&T>, ext: &String) -> String {
180 let mut result = self.to_str(symbol_table);
181 if let Some(t) = symbol_table {
182 if let OpCode::T(tok) = self {
183 if t.is_symbol_t_data(&Symbol::T(*tok)) {
184 result.push_str(&format!("({ext})"));
185 }
186 }
187 }
188 result
189 }
190}
191
192impl From<Symbol> for OpCode {
193 fn from(value: Symbol) -> Self {
194 match value {
195 Symbol::Empty => OpCode::Empty,
196 Symbol::T(t) => OpCode::T(t),
197 Symbol::NT(v) => OpCode::NT(v),
198 Symbol::End => OpCode::End,
199 }
200 }
201}
202
203#[cfg(feature = "test_utils")]
204impl OpCode {
205 pub fn to_macro_item(&self) -> String {
206 match self {
207 OpCode::Empty => "e".to_string(),
208 OpCode::T(t) => format!("t {t}"),
209 OpCode::NT(v) => format!("nt {v}"),
210 OpCode::Loop(v) => format!("loop {v}"),
211 OpCode::Exit(v) => format!("exit {v}"),
212 OpCode::Hook => "hook".to_string(),
213 OpCode::End => "end".to_string(),
214 }
215 }
216}
217
218#[derive(Clone, Copy, PartialEq, Debug)]
223pub enum Terminate {
224 None,
226 Abort,
228 Conclude,
230}
231
232#[derive(PartialEq, Debug)]
236pub enum Call {
237 Enter,
247 Loop,
249 Exit,
254 End(Terminate)
265}
266
267pub trait ListenerWrapper {
268 #[allow(unused_variables)]
270 fn switch(&mut self, call: Call, nt: VarId, alt_id: AltId, t_data: Option<Vec<String>>) {}
271
272 fn check_abort_request(&self) -> Terminate { Terminate::None }
282
283 fn abort(&mut self) {}
285
286 fn get_log_mut(&mut self) -> &mut impl Logger;
288
289 #[allow(unused_variables)]
291 fn report(&mut self, span_opt: Option<&PosSpan>, msg: LogMsg) {
292 self.get_log_mut().add(msg);
293 }
294
295 #[allow(unused_variables)]
297 fn push_span(&mut self, span: PosSpan) {}
298
299 fn is_stack_empty(&self) -> bool { true }
301
302 fn is_stack_t_empty(&self) -> bool { true }
304
305 fn is_stack_span_empty(&self) -> bool { true }
307
308 #[allow(unused_variables)]
310 fn hook(&mut self, token: TokenId, text: &str, span: &PosSpan) -> TokenId {
311 token
312 }
313
314 #[allow(unused_variables)]
316 fn intercept_token(&mut self, token: TokenId, text: &str, span: &PosSpan) -> TokenId {
317 token
318 }
319}
320
321pub type ParserToken = (TokenId, String, PosSpan);
324
325#[derive(PartialEq, Debug)]
328pub enum ParserError {
329 SyntaxError,
344 TooManyErrors,
354 Irrecoverable,
357 ExtraSymbol,
365 UnexpectedEOS,
367 UnexpectedError,
369 EncounteredErrors,
374 AbortRequest,
377}
378
379impl Display for ParserError {
380 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
381 write!(f, "{}", match self {
382 ParserError::SyntaxError => "syntax error",
383 ParserError::TooManyErrors => "too many errors while trying to recover",
384 ParserError::Irrecoverable => "irrecoverable syntax error",
385 ParserError::ExtraSymbol => "extra symbol after end of parsing",
386 ParserError::UnexpectedEOS => "unexpected end of stream",
387 ParserError::UnexpectedError => "unexpected error",
388 ParserError::EncounteredErrors => "parsing failed due to previously encountered error(s)",
389 ParserError::AbortRequest => "abort request",
390 })
391 }
392}
393
394pub struct Parser<'a> {
396 num_nt: usize,
397 num_t: usize,
398 alt_var: &'a [VarId],
399 alts: Vec<Alternative>,
400 opcodes: Vec<Vec<OpCode>>,
401 init_opcodes: Vec<OpCode>,
402 table: &'a [AltId],
403 symbol_table: FixedSymTable,
404 start: VarId,
405 try_recover: bool, }
407
408impl<'a> Parser<'a> {
409 pub const MAX_NBR_RECOVERS: u32 = 5;
411 pub const MAX_NBR_LEXER_ERRORS: u32 = 3;
412
413 pub fn new(
414 num_nt: usize,
415 num_t: usize,
416 alt_var: &'a [VarId],
417 alts: Vec<Alternative>,
418 opcodes: Vec<Vec<OpCode>>,
419 init_opcodes: Vec<OpCode>,
420 table: &'a [AltId],
421 symbol_table: FixedSymTable,
422 start: VarId,
423 ) -> Self {
424 Parser { num_nt, num_t, alt_var, alts, opcodes, init_opcodes, table, symbol_table, start, try_recover: true }
425 }
426
427 pub fn get_symbol_table(&self) -> Option<&FixedSymTable> {
429 Some(&self.symbol_table)
430 }
431
432 pub fn set_start(&mut self, start: VarId) {
434 assert!(self.num_nt > start as usize);
435 self.start = start;
436 }
437
438 pub fn set_try_recover(&mut self, try_recover: bool) {
442 self.try_recover = try_recover;
443 }
444
445 fn simulate(&self, stream_sym: Symbol, mut stack: Vec<OpCode>, mut stack_sym: OpCode) -> bool {
448 const VERBOSE: bool = false;
449 let error_skip_alt_id = self.alt_var.len() as AltId;
450 let end_var_id = (self.num_t - 1) as VarId;
451 if VERBOSE { print!(" next symbol could be: {}?", stream_sym.to_str(self.get_symbol_table())); }
452
453 let ok = loop {
454 match (stack_sym, stream_sym) {
455 (OpCode::NT(var), _) | (OpCode::Loop(var), _) => {
456 let sr = if let Symbol::T(sr) = stream_sym { sr } else { end_var_id };
457 let alt_id = self.table[var as usize * self.num_t + sr as usize];
458 if alt_id >= error_skip_alt_id {
459 break false;
460 }
461 stack.extend(self.opcodes[alt_id as usize].clone());
462 stack_sym = stack.pop().unwrap();
463 }
464 (OpCode::Exit(_), _) => {
465 stack_sym = stack.pop().unwrap();
466 }
467 (OpCode::T(sk), Symbol::T(sr)) => {
468 break sk == sr;
469 }
470 (OpCode::End, Symbol::End) => {
471 break true;
472 }
473 (_, _) => {
474 break false;
475 }
476 }
477 };
478 if VERBOSE { println!(" {}", if ok { "yes" } else { "no" }); }
479 ok
480 }
481
482 pub fn parse_stream<I, L>(&mut self, wrapper: &mut L, mut stream: I) -> Result<(), ParserError>
491 where I: Iterator<Item=ParserToken>,
492 L: ListenerWrapper,
493 {
494 const VERBOSE: bool = false;
496
497 const DELAY_STREAM_INTERCEPTION: bool = cfg!(feature = "delay_stream_interception");
501
502 let sym_table: Option<&FixedSymTable> = Some(&self.symbol_table);
503 let mut stack = self.init_opcodes.clone();
504 let mut stack_t = Vec::<String>::new();
505 let error_skip_alt_id = self.alt_var.len() as AltId;
506 let error_pop_alt_id = error_skip_alt_id + 1;
507 if VERBOSE { println!("skip = {error_skip_alt_id}, pop = {error_pop_alt_id}"); }
508 let mut recover_mode = false;
509 let mut nbr_recovers = 0;
510 let mut nbr_lexer_errors = 0;
511 let end_var_id = (self.num_t - 1) as VarId;
512 let mut stack_sym = stack.pop().unwrap();
513 let mut stream_n = 0;
514 let mut stream_pos = None;
515 let mut stream_span = PosSpan::empty();
516 let mut stream_sym = Symbol::default(); let mut stream_str = String::default(); let mut advance_stream = true;
519 let mut hook_active = false;
520 loop {
521 if advance_stream &&
522 (!DELAY_STREAM_INTERCEPTION || (!matches!(stack_sym, OpCode::Exit(_)) || stream_sym == Symbol::Empty)) {
526 stream_n += 1;
527 (stream_sym, stream_str) = stream.next().map(|(t, s, span)| {
528 let new_t = wrapper.intercept_token(t, &s, &span);
531 stream_pos = Some(span.first_forced());
532 stream_span = span;
533 (Symbol::T(new_t), s)
534 }).unwrap_or_else(|| {
535 if let Some((_t, s, span)) = stream.next() {
537 stream_span = span;
538 (Symbol::Empty, s)
539 } else {
540 (Symbol::End, String::new())
541 }
542 });
543 advance_stream = false;
544 hook_active = true;
545 }
546 if VERBOSE {
547 println!("{:-<40}", "");
548 println!("input ({stream_n}{}): {} stack_t: [{}] stack: [{}] current: {}",
549 if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() },
550 stream_sym.to_str_ext(sym_table, &stream_str),
551 stack_t.join(", "),
552 stack.iter().map(|s| s.to_str(sym_table)).collect::<Vec<_>>().join(" "),
553 stack_sym.to_str_name(sym_table));
554 }
555 match (stack_sym, stream_sym) {
556 (_, Symbol::Empty) => {
557 if VERBOSE { println!("lexer error: {stream_str}"); }
559 wrapper.report(Some(&stream_span), LogMsg::Error(format!("lexical error: {stream_str}")));
560 nbr_lexer_errors += 1;
561 if nbr_lexer_errors >= Self::MAX_NBR_LEXER_ERRORS {
562 wrapper.report(None, LogMsg::Note(format!("too many lexical errors ({nbr_lexer_errors}), giving up")));
563 wrapper.abort();
564 return Err(ParserError::TooManyErrors);
565 }
566 advance_stream = true;
567 }
568 (OpCode::Hook, Symbol::T(t)) => {
569 if hook_active {
570 let new_t = wrapper.hook(t, stream_str.as_str(), &stream_span);
571 stream_sym = Symbol::T(new_t);
572 hook_active = false;
573 }
574 stack_sym = stack.pop().unwrap();
575 }
576 (OpCode::Hook, _) => {
577 stack_sym = stack.pop().unwrap();
579 }
580 (OpCode::NT(var), _) | (OpCode::Loop(var), _) => {
581 let sr = if let Symbol::T(sr) = stream_sym { sr } else { end_var_id };
582 let alt_id = self.table[var as usize * self.num_t + sr as usize];
583 if VERBOSE {
584 println!("- table[{var}, {sr}] = {alt_id}: {} -> {}",
585 Symbol::NT(var).to_str(self.get_symbol_table()),
586 if alt_id >= error_skip_alt_id {
587 "ERROR".to_string()
588 } else if let Some(a) = self.alts.get(alt_id as usize) {
589 a.to_str(sym_table)
590 } else {
591 "(alternative)".to_string()
592 });
593 }
594 if !recover_mode && alt_id >= error_skip_alt_id {
595 let expected = (0..self.num_t as VarId).filter(|t| self.table[var as usize * self.num_t + *t as usize] < error_skip_alt_id)
596 .filter(|t| self.simulate(Symbol::T(*t), stack.clone(), stack_sym))
597 .map(|t| format!("'{}'", if t < end_var_id { Symbol::T(t).to_str(sym_table) } else { "<EOF>".to_string() }))
598 .collect::<Vec<_>>().join(", ");
599 let stream_sym_txt = if stream_sym.is_end() { "end of stream".to_string() } else { format!("input '{}'", stream_sym.to_str(sym_table)) };
600 let msg = format!("syntax error: found {stream_sym_txt} instead of {expected} while parsing '{}'{}",
601 stack_sym.to_str(sym_table),
602 if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() });
603 if self.try_recover {
604 wrapper.report(Some(&stream_span), LogMsg::Error(msg));
605 if nbr_recovers >= Self::MAX_NBR_RECOVERS {
606 wrapper.report(None, LogMsg::Note(format!("too many errors ({nbr_recovers}), giving up")));
607 wrapper.abort();
608 return Err(ParserError::TooManyErrors);
609 }
610 nbr_recovers += 1;
611 recover_mode = true;
612 } else {
613 wrapper.report(Some(&stream_span), LogMsg::Error(msg));
614 wrapper.abort();
615 return Err(ParserError::SyntaxError);
616 }
617 }
618 if recover_mode {
619 if VERBOSE { println!("!NT {} <-> {}, alt_id = {alt_id}", stack_sym.to_str(self.get_symbol_table()), stream_sym.to_str(self.get_symbol_table())); }
620 if alt_id == error_skip_alt_id {
621 if stream_sym == Symbol::End {
622 let msg = "irrecoverable error, reached end of stream".to_string();
623 if VERBOSE { println!("(recovering) {msg}"); }
624 wrapper.report(None, LogMsg::Note(msg));
625 wrapper.abort();
626 return Err(ParserError::Irrecoverable);
627 }
628 if VERBOSE { println!("(recovering) skipping token {}", stream_sym.to_str(self.get_symbol_table())); }
629 advance_stream = true;
630 } else if alt_id == error_pop_alt_id {
631 if VERBOSE { println!("(recovering) popping {}", stack_sym.to_str(self.get_symbol_table())); }
632 stack_sym = stack.pop().unwrap();
633 } else if alt_id < error_skip_alt_id {
634 recover_mode = false;
635 let pos_str = if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() };
636 wrapper.report(None, LogMsg::Note(format!("resynchronized on '{}'{pos_str}", stream_sym.to_str(self.get_symbol_table()))));
637 if VERBOSE { println!("(recovering) resynchronized{pos_str}"); }
638 } else {
639 panic!("illegal alt_id {alt_id}")
640 }
641 }
642 if !recover_mode {
643 let call = if stack_sym.is_loop() { Call::Loop } else { Call::Enter };
644 let t_data = std::mem::take(&mut stack_t);
645 if VERBOSE {
646 let f_str = if let Some(f) = &self.alts.get(alt_id as usize) {
647 f.to_str(sym_table)
648 } else {
649 "(alternative)".to_string()
650 };
651 println!(
652 "- to stack: [{}]",
653 self.opcodes[alt_id as usize].iter().filter(|s| !s.is_empty()).map(|s| s.to_str(sym_table))
654 .collect::<Vec<_>>().join(" "));
655 println!(
656 "- {} {} -> {f_str} ({}): [{}]",
657 if stack_sym.is_loop() { "LOOP" } else { "ENTER" },
658 Symbol::NT(self.alt_var[alt_id as usize]).to_str(sym_table), t_data.len(), t_data.join(" "));
659 }
660 if nbr_recovers == 0 {
661 wrapper.switch(call, var, alt_id, Some(t_data));
662 }
663 stack.extend(self.opcodes[alt_id as usize].clone());
664 stack_sym = stack.pop().unwrap();
665 }
666 }
667 (OpCode::Exit(alt_id), _) => {
668 let var = self.alt_var[alt_id as usize];
669 let t_data = std::mem::take(&mut stack_t);
670 if VERBOSE {
671 println!(
672 "- EXIT {} syn ({}): [{}]",
673 Symbol::NT(var).to_str(sym_table), t_data.len(), t_data.join(" "));
674 }
675 if nbr_recovers == 0 {
676 wrapper.switch(Call::Exit, var, alt_id, Some(t_data));
677 }
678 stack_sym = stack.pop().unwrap();
679 }
680 (OpCode::T(sk), Symbol::T(sr)) => {
681 if !recover_mode && sk != sr {
682 let msg = format!(
683 "syntax error: found input '{}' instead of '{}'{}",
684 stream_sym.to_str(sym_table),
685 Symbol::T(sk).to_str(sym_table),
686 if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() });
687 if self.try_recover {
688 wrapper.report(Some(&stream_span), LogMsg::Error(msg));
689 if nbr_recovers >= Self::MAX_NBR_RECOVERS {
690 wrapper.report(None, LogMsg::Note(format!("too many errors ({nbr_recovers}), giving up")));
691 wrapper.abort();
692 return Err(ParserError::TooManyErrors);
693 }
694 nbr_recovers += 1;
695 recover_mode = true;
696 } else {
697 wrapper.report(Some(&stream_span), LogMsg::Error(msg));
698 wrapper.abort();
699 return Err(ParserError::SyntaxError);
700 }
701 }
702 if recover_mode {
703 if VERBOSE { println!("!T {} <-> {}", Symbol::T(sk).to_str(self.get_symbol_table()), stream_sym.to_str(self.get_symbol_table())); }
704 if sk == sr {
705 recover_mode = false;
706 let pos_str = if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() };
707 wrapper.report(Some(&stream_span), LogMsg::Note(format!("resynchronized on '{}'{pos_str}", stream_sym.to_str(self.get_symbol_table()))));
708 if VERBOSE { println!("(recovering) resynchronized{pos_str}"); }
709 } else {
710 if VERBOSE { println!("(recovering) popping {}", Symbol::T(sk).to_str(self.get_symbol_table())); }
711 stack_sym = stack.pop().unwrap();
712 }
713 }
714 if !recover_mode {
715 if VERBOSE { println!("- MATCH {}", stream_sym.to_str(sym_table)); }
716 if self.symbol_table.is_token_data(sk) {
717 stack_t.push(std::mem::take(&mut stream_str)); }
719 stack_sym = stack.pop().unwrap();
720 wrapper.push_span(stream_span.take());
721 advance_stream = true;
722 }
723 }
724 (OpCode::End, Symbol::End) => {
725 if nbr_recovers == 0 {
726 wrapper.switch(Call::End(Terminate::None), 0, 0, None);
727 }
728 break;
729 }
730 (OpCode::End, _) => {
731 wrapper.report(Some(&stream_span), LogMsg::Error(format!("syntax error: found extra symbol '{}' after end of parsing", stream_sym.to_str(sym_table))));
732 wrapper.abort();
733 return Err(ParserError::ExtraSymbol);
734 }
735 (_, Symbol::End) => {
736 wrapper.report(None, LogMsg::Error(format!("syntax error: found end of stream instead of '{}'", stack_sym.to_str_name(sym_table))));
737 wrapper.abort();
738 return Err(ParserError::UnexpectedEOS);
739 }
740 (_, _) => {
741 let text = format!(
742 "unexpected syntax error: input '{}' while expecting '{}'{}",
743 stream_sym.to_str(sym_table), stack_sym.to_str_name(sym_table),
744 if let Some(Pos(line, col)) = stream_pos { format!(", line {line}, col {col}") } else { String::new() });
745 wrapper.report(Some(&stream_span), LogMsg::Error(text));
746 wrapper.abort();
747 return Err(ParserError::UnexpectedError);
748 }
749 }
750 match wrapper.check_abort_request() {
751 Terminate::None => {}
752 terminate @ (Terminate::Abort | Terminate::Conclude) => {
753 if VERBOSE { println!("detected {terminate:?}"); }
754 stack_t.clear();
755 stack.clear();
756 wrapper.abort();
757 if nbr_recovers == 0 {
758 wrapper.switch(Call::End(terminate), 0, 0, None);
759 }
760 if terminate == Terminate::Abort {
761 return Err(ParserError::AbortRequest);
762 } else {
763 break;
764 }
765 }
766 }
767 }
768 assert!(stack_t.is_empty(), "stack_t: {}", stack_t.join(", "));
769 assert!(stack.is_empty(), "stack: {}", stack.iter().map(|s| s.to_str(sym_table)).collect::<Vec<_>>().join(", "));
770 if nbr_recovers == 0 {
771 assert!(wrapper.is_stack_empty(), "symbol stack isn't empty");
772 assert!(wrapper.is_stack_t_empty(), "text stack isn't empty");
773 assert!(wrapper.is_stack_span_empty(), "span stack isn't empty");
774 Ok(())
775 } else {
776 wrapper.abort();
778 Err(ParserError::EncounteredErrors)
779 }
780 }
781}
782
783#[cfg(feature = "test_utils")]
784impl<'a> Parser<'a> {
785 pub fn get_alt_var(&self) -> &[VarId] {
786 self.alt_var
787 }
788
789 pub fn get_alts(&self) -> &Vec<Alternative> {
790 &self.alts
791 }
792
793 pub fn get_opcodes(&self) -> &Vec<Vec<OpCode>> {
794 &self.opcodes
795 }
796}