diff --git a/cfgrammar/src/lib/yacc/ast.rs b/cfgrammar/src/lib/yacc/ast.rs index 04c269130..fa5d09499 100644 --- a/cfgrammar/src/lib/yacc/ast.rs +++ b/cfgrammar/src/lib/yacc/ast.rs @@ -6,35 +6,154 @@ use std::{ use indexmap::{IndexMap, IndexSet}; use super::{ - parser::YaccParser, Precedence, YaccGrammarError, YaccGrammarErrorKind, YaccGrammarWarning, - YaccGrammarWarningKind, YaccKind, + parser::YaccGrammarConstructionFailure, parser::YaccParser, Precedence, YaccGrammarError, + YaccGrammarErrorKind, YaccGrammarWarning, YaccGrammarWarningKind, YaccKind, }; - use crate::Span; +use std::sync::mpsc; /// Contains a `GrammarAST` structure produced from a grammar source file. /// As well as any errors which occurred during the construction of the AST. pub struct ASTWithValidityInfo { pub(crate) ast: GrammarAST, - pub(crate) errs: Vec, + pub(crate) error_receiver: Option>, + pub(crate) error_sender: ErrorSender, +} + +#[allow(dead_code)] +pub struct AstBuilder<'a> { + kind: YaccKind, + grammar_src: Option<&'a str>, + error_receiver: Option>, + error_sender: ErrorSender, +} + +pub struct ErrorSender { + error_occurred: bool, + sender: Option>, +} + +#[allow(dead_code)] +impl ErrorSender { + pub(crate) fn new(sender: mpsc::Sender) -> Self { + Self { + error_occurred: false, + sender: Some(sender), + } + } + pub(crate) fn close(&mut self) { + drop(self.sender.take()) + } + + pub(crate) fn send( + &mut self, + e: YaccGrammarError, + ) -> Result { + self.error_occurred = true; + if let Some(sender) = &self.sender { + sender.send(e)?; + Ok(YaccGrammarConstructionFailure::ConstructionFailure) + } else { + Err(YaccGrammarConstructionFailure::ErrorChannelClosed(e)) + } + } + pub(crate) fn error_occurred(&self) -> bool { + self.error_occurred + } } -impl ASTWithValidityInfo { +#[allow(dead_code)] +impl<'a> AstBuilder<'a> { + fn yacc_kind(mut self, kind: YaccKind) -> Self { + self.kind = kind; + self + } + + fn error_receiver(&mut self) -> mpsc::Receiver { + self.error_receiver.take().unwrap() + } + + fn build(self) -> ASTWithValidityInfo { + ASTWithValidityInfo::new_with_error_channel( + self.kind, + self.grammar_src.unwrap(), + self.error_sender, + self.error_receiver, + ) + } +} + +impl<'a> ASTWithValidityInfo { + pub fn builder() -> AstBuilder<'a> { + let (error_sender, error_receiver) = std::sync::mpsc::channel(); + AstBuilder { + kind: YaccKind::Grmtools, + grammar_src: None, + error_receiver: Some(error_receiver), + error_sender: ErrorSender::new(error_sender), + } + } + + pub(crate) fn new_with_error_channel( + yacc_kind: YaccKind, + s: &str, + mut error_sender: ErrorSender, + error_receiver: Option>, + ) -> Self { + let ast = match yacc_kind { + YaccKind::Original(_) | YaccKind::Grmtools | YaccKind::Eco => { + let mut yp = YaccParser::new(yacc_kind, s.to_string()); + yp.parse_with_error_channel(&mut error_sender).unwrap(); + let mut ast = yp.ast(); + ast.complete_and_validate_with_error_channel(&mut error_sender) + .ok(); + ast + } + }; + error_sender.close(); + ASTWithValidityInfo { + ast, + error_receiver, + error_sender, + } + } + + #[cfg(test)] + pub(crate) fn validate_ast(mut ast: GrammarAST) -> Self { + let (error_sender, error_receiver) = mpsc::channel(); + let mut error_sender = ErrorSender::new(error_sender); + ast.complete_and_validate_with_error_channel(&mut error_sender) + .ok(); + error_sender.close(); + ASTWithValidityInfo { + ast, + error_receiver: Some(error_receiver), + error_sender, + } + } + /// Parses a source file into an AST, returning an ast and any errors that were /// encountered during the construction of it. The `ASTWithValidityInfo` can be /// then unused to construct a `YaccGrammar`, which will either produce an /// `Ok(YaccGrammar)` or an `Err` which includes these errors. pub fn new(yacc_kind: YaccKind, s: &str) -> Self { - let mut errs = Vec::new(); + let (error_sender, error_receiver) = mpsc::channel(); + let mut error_sender = ErrorSender::new(error_sender); let ast = match yacc_kind { YaccKind::Original(_) | YaccKind::Grmtools | YaccKind::Eco => { let mut yp = YaccParser::new(yacc_kind, s.to_string()); - yp.parse().map_err(|e| errs.extend(e)).ok(); + let _ = yp.parse_with_error_channel(&mut error_sender).ok(); let mut ast = yp.ast(); - ast.complete_and_validate().map_err(|e| errs.push(e)).ok(); + ast.complete_and_validate_with_error_channel(&mut error_sender) + .ok(); + error_sender.close(); ast } }; - ASTWithValidityInfo { ast, errs } + ASTWithValidityInfo { + ast, + error_receiver: Some(error_receiver), + error_sender, + } } /// Returns a `GrammarAST` constructed as the result of parsing a source file. @@ -48,12 +167,16 @@ impl ASTWithValidityInfo { /// Returns whether any errors where encountered during the /// parsing and validation of the AST during it's construction. pub fn is_valid(&self) -> bool { - self.errs.is_empty() + !self.error_sender.error_occurred() } /// Returns all errors which were encountered during AST construction. - pub fn errors(&self) -> &[YaccGrammarError] { - self.errs.as_slice() + pub fn errors(&self) -> Vec { + let errs = self + .error_receiver + .as_ref() + .map_or(vec![], |errs| errs.iter().collect::>()); + errs } } @@ -209,20 +332,23 @@ impl GrammarAST { /// 4) If a production has a precedence token, then it references a declared token /// 5) Every token declared with %epp matches a known token /// If the validation succeeds, None is returned. - pub(crate) fn complete_and_validate(&mut self) -> Result<(), YaccGrammarError> { + pub(crate) fn complete_and_validate_with_error_channel( + &mut self, + errs: &mut ErrorSender, + ) -> Result<(), YaccGrammarConstructionFailure> { match self.start { None => { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::NoStartRule, spans: vec![Span::new(0, 0)], - }); + })?); } Some((ref s, span)) => { if !self.rules.contains_key(s) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::InvalidStartRule(s.clone()), spans: vec![span], - }); + })?); } } } @@ -231,34 +357,34 @@ impl GrammarAST { let prod = &self.prods[pidx]; if let Some(ref n) = prod.precedence { if !self.tokens.contains(n) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownToken(n.clone()), spans: vec![Span::new(0, 0)], - }); + })?); } if !self.precs.contains_key(n) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::NoPrecForToken(n.clone()), spans: vec![Span::new(0, 0)], - }); + })?); } } for sym in &prod.symbols { match *sym { Symbol::Rule(ref name, span) => { if !self.rules.contains_key(name) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownRuleRef(name.clone()), spans: vec![span], - }); + })?); } } Symbol::Token(ref name, span) => { if !self.tokens.contains(name) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownToken(name.clone()), spans: vec![span], - }); + })?); } } } @@ -275,28 +401,28 @@ impl GrammarAST { continue; } } - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownEPP(k.clone()), spans: vec![*sp], - }); + })?); } for sym in &self.expect_unused { match sym { Symbol::Rule(sym_name, sym_span) => { if self.get_rule(sym_name).is_none() { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownRuleRef(sym_name.clone()), spans: vec![*sym_span], - }); + })?); } } Symbol::Token(sym_name, sym_span) => { if !self.has_token(sym_name) { - return Err(YaccGrammarError { + return Err(errs.send(YaccGrammarError { kind: YaccGrammarErrorKind::UnknownToken(sym_name.clone()), spans: vec![*sym_span], - }); + })?); } } } @@ -396,7 +522,7 @@ impl GrammarAST { mod test { use super::{ super::{AssocKind, Precedence}, - GrammarAST, Span, Symbol, YaccGrammarError, YaccGrammarErrorKind, + ASTWithValidityInfo, GrammarAST, Span, Symbol, YaccGrammarError, YaccGrammarErrorKind, }; fn rule(n: &str) -> Symbol { @@ -409,12 +535,12 @@ mod test { #[test] fn test_empty_grammar() { - let mut grm = GrammarAST::new(); - match grm.complete_and_validate() { - Err(YaccGrammarError { + let grm = GrammarAST::new(); + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::NoStartRule, .. - }) => (), + }] => (), _ => panic!("Validation error"), } } @@ -426,11 +552,12 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("B".to_string(), empty_span), None); grm.add_prod("B".to_string(), vec![], None, None); - match grm.complete_and_validate() { - Err(YaccGrammarError { + + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::InvalidStartRule(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } } @@ -442,7 +569,7 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![], None, None); - assert!(grm.complete_and_validate().is_ok()); + assert!(ASTWithValidityInfo::validate_ast(grm).is_valid()); } #[test] @@ -454,7 +581,7 @@ mod test { grm.add_rule(("B".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![rule("B")], None, None); grm.add_prod("B".to_string(), vec![], None, None); - assert!(grm.complete_and_validate().is_ok()); + assert!(ASTWithValidityInfo::validate_ast(grm).is_valid()); } #[test] @@ -464,11 +591,11 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![rule("B")], None, None); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::UnknownRuleRef(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } } @@ -481,7 +608,7 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![token("b")], None, None); - assert!(grm.complete_and_validate().is_ok()); + assert!(ASTWithValidityInfo::validate_ast(grm).is_valid()); } #[test] @@ -494,7 +621,7 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![rule("b")], None, None); - assert!(grm.complete_and_validate().is_err()); + assert!(!ASTWithValidityInfo::validate_ast(grm).is_valid()); } #[test] @@ -504,11 +631,11 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![token("b")], None, None); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::UnknownToken(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } } @@ -520,11 +647,11 @@ mod test { grm.start = Some(("A".to_string(), empty_span)); grm.add_rule(("A".to_string(), empty_span), None); grm.add_prod("A".to_string(), vec![rule("b"), token("b")], None, None); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::UnknownRuleRef(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } } @@ -538,11 +665,11 @@ mod test { grm.add_prod("A".to_string(), vec![], None, None); grm.epp .insert("k".to_owned(), (empty_span, ("v".to_owned(), empty_span))); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::UnknownEPP(_), spans, - }) if spans.len() == 1 && spans[0] == Span::new(2, 3) => (), + }] if spans.len() == 1 && spans[0] == Span::new(2, 3) => (), _ => panic!("Validation error"), } } @@ -570,7 +697,7 @@ mod test { Some("b".to_string()), None, ); - assert!(grm.complete_and_validate().is_ok()); + assert!(ASTWithValidityInfo::validate_ast(grm).is_valid()); } #[test] @@ -585,19 +712,28 @@ mod test { Some("b".to_string()), None, ); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::UnknownToken(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } + let mut grm = GrammarAST::new(); + grm.start = Some(("A".to_string(), empty_span)); + grm.add_rule(("A".to_string(), empty_span), None); + grm.add_prod( + "A".to_string(), + vec![token("b")], + Some("b".to_string()), + None, + ); grm.tokens.insert("b".to_string()); - match grm.complete_and_validate() { - Err(YaccGrammarError { + match ASTWithValidityInfo::validate_ast(grm).errors().as_slice() { + [YaccGrammarError { kind: YaccGrammarErrorKind::NoPrecForToken(_), .. - }) => (), + }] => (), _ => panic!("Validation error"), } } diff --git a/cfgrammar/src/lib/yacc/grammar.rs b/cfgrammar/src/lib/yacc/grammar.rs index 7f3e0870c..c155bf82e 100644 --- a/cfgrammar/src/lib/yacc/grammar.rs +++ b/cfgrammar/src/lib/yacc/grammar.rs @@ -115,7 +115,7 @@ where ast_validation: &ast::ASTWithValidityInfo, ) -> YaccGrammarResult { if !ast_validation.is_valid() { - return Err(ast_validation.errs.clone()); + return Err(ast_validation.errors()); } let ast = &ast_validation.ast; // Check that StorageT is big enough to hold RIdx/PIdx/SIdx/TIdx values; after these diff --git a/cfgrammar/src/lib/yacc/parser.rs b/cfgrammar/src/lib/yacc/parser.rs index fc98307b2..caaab7a23 100644 --- a/cfgrammar/src/lib/yacc/parser.rs +++ b/cfgrammar/src/lib/yacc/parser.rs @@ -10,6 +10,7 @@ use std::{ error::Error, fmt, str::FromStr, + sync::mpsc, }; pub type YaccGrammarResult = Result>; @@ -17,10 +18,33 @@ pub type YaccGrammarResult = Result>; use crate::{Span, Spanned}; use super::{ - ast::{GrammarAST, Symbol}, + ast::{ErrorSender, GrammarAST, Symbol}, AssocKind, Precedence, YaccKind, }; +#[derive(Debug)] +pub enum YaccGrammarConstructionFailure { + ConstructionFailure, + ErrorChannelClosed(YaccGrammarError), + ErrorChannel(mpsc::SendError), +} + +impl From> for YaccGrammarConstructionFailure { + fn from(it: mpsc::SendError) -> Self { + Self::ErrorChannel(it) + } +} + +impl fmt::Display for YaccGrammarConstructionFailure { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::ErrorChannelClosed(e) => write!(f, "Attempt to send error {} over closed channel", e), + Self::ConstructionFailure => write!(f, "Error constructing YaccGrammar: "), + Self::ErrorChannel(e) => write!(f, "Failure to send an error {} over a channel.", e), + } + } +} + /// The various different possible Yacc parser errors. #[derive(Debug, PartialEq, Eq, Clone)] pub enum YaccGrammarErrorKind { @@ -269,27 +293,6 @@ lazy_static! { Regex::new("^(?:(\".+?\")|('.+?')|([a-zA-Z_][a-zA-Z_0-9]*))").unwrap(); } -fn add_duplicate_occurrence( - errs: &mut Vec, - kind: YaccGrammarErrorKind, - orig_span: Span, - dup_span: Span, -) { - if !errs.iter_mut().any(|e| { - if e.kind == kind && e.spans[0] == orig_span { - e.spans.push(dup_span); - true - } else { - false - } - }) { - errs.push(YaccGrammarError { - kind, - spans: vec![orig_span, dup_span], - }); - } -} - /// The actual parser is intended to be entirely opaque from outside users. impl YaccParser { pub(crate) fn new(yacc_kind: YaccKind, src: String) -> YaccParser { @@ -302,37 +305,35 @@ impl YaccParser { } } - pub(crate) fn parse(&mut self) -> YaccGrammarResult { - let mut errs: Vec = Vec::new(); + pub(crate) fn parse_with_error_channel( + &mut self, + errs: &mut ErrorSender, + ) -> Result { // We pass around an index into the *bytes* of self.src. We guarantee that at all times // this points to the beginning of a UTF-8 character (since multibyte characters exist, not // every byte within the string is also a valid character). - let mut result = self.parse_declarations(0, &mut errs); - result = self.parse_rules(match result { - Ok(i) => i, - Err(e) => { - errs.push(e); - return Err(errs); - } - }); - result = self.parse_programs( - match result { - Ok(i) => i, - Err(e) => { - errs.push(e); - return Err(errs); - } - }, - &mut errs, - ); + let mut i = self.parse_declarations(0, errs)?; + i = self.parse_rules(i, errs)?; + i = self.parse_programs(i, errs)?; + + if errs.error_occurred() { + Err(YaccGrammarConstructionFailure::ConstructionFailure) + } else { + Ok(i) + } + } + #[cfg(test)] + pub(crate) fn parse(&mut self) -> Result> { + let (error_sender, error_receiver) = mpsc::channel(); + let mut error_sender = ErrorSender::new(error_sender); + let result = self.parse_with_error_channel(&mut error_sender); match result { - Ok(i) if errs.is_empty() => Ok(i), - Err(e) => { - errs.push(e); - Err(errs) + Ok(x) => Ok(x), + Err(_) => { + drop(error_sender); + Err(error_receiver.iter().collect()) } - _ => Err(errs), } } @@ -343,106 +344,110 @@ impl YaccParser { fn parse_declarations( &mut self, mut i: usize, - errs: &mut Vec, - ) -> Result { - i = self.parse_ws(i, true)?; + errs: &mut ErrorSender, + ) -> Result { + i = self.parse_ws(i, true, errs)?; let mut prec_level = 0; while i < self.src.len() { if self.lookahead_is("%%", i).is_some() { return Ok(i); } if let Some(j) = self.lookahead_is("%token", i) { - i = self.parse_ws(j, false)?; + i = self.parse_ws(j, false, errs)?; while i < self.src.len() && self.lookahead_is("%", i).is_none() { - let (j, n, span) = self.parse_token(i)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n, span) = result.unwrap(); if self.ast.tokens.insert(n) { self.ast.spans.push(span); } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; } continue; } if let YaccKind::Original(_) = self.yacc_kind { if let Some(j) = self.lookahead_is("%actiontype", i) { - i = self.parse_ws(j, false)?; - let (j, n) = self.parse_to_eol(i)?; + i = self.parse_ws(j, false, errs)?; + let (j, n) = self.parse_to_eol(i, errs)?; let span = Span::new(i, j); if let Some((_, orig_span)) = self.global_actiontype { - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateActiontypeDeclaration, - orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateActiontypeDeclaration, + spans: vec![orig_span, span], + })?; } else { self.global_actiontype = Some((n, span)); } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } } if let Some(j) = self.lookahead_is("%start", i) { - i = self.parse_ws(j, false)?; - let (j, n) = self.parse_name(i)?; + i = self.parse_ws(j, false, errs)?; + let result = self.try_parse_name(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n) = result.unwrap(); let span = Span::new(i, j); if let Some((_, orig_span)) = self.ast.start { - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateStartDeclaration, - orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateStartDeclaration, + spans: vec![orig_span, span], + })?; } else { self.ast.start = Some((n, span)); } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } if let Some(j) = self.lookahead_is("%epp", i) { - i = self.parse_ws(j, false)?; - let (j, n, _) = self.parse_token(i)?; + i = self.parse_ws(j, false, errs)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n, _) = result.unwrap(); let span = Span::new(i, j); - i = self.parse_ws(j, false)?; - let (j, v) = self.parse_string(i)?; + i = self.parse_ws(j, false, errs)?; + let (j, v) = self.parse_string(i, errs)?; let vspan = Span::new(i, j); match self.ast.epp.entry(n) { Entry::Occupied(orig) => { let (orig_span, _) = orig.get(); - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateEPP, - *orig_span, - span, - ) + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateEPP, + spans: vec![*orig_span, span], + })?; } Entry::Vacant(epp) => { epp.insert((span, (v, vspan))); } } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } if let Some(j) = self.lookahead_is("%expect-rr", i) { - i = self.parse_ws(j, false)?; - let (j, n) = self.parse_int(i)?; + i = self.parse_ws(j, false, errs)?; + let (j, n) = self.parse_int(i, errs)?; let span = Span::new(i, j); if let Some((_, orig_span)) = self.ast.expectrr { - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateExpectRRDeclaration, - orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateExpectRRDeclaration, + spans: vec![orig_span, span], + })?; } else { self.ast.expectrr = Some((n, span)); } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } if let Some(j) = self.lookahead_is("%expect-unused", i) { - i = self.parse_ws(j, false)?; + i = self.parse_ws(j, false, errs)?; while i < self.src.len() && self.lookahead_is("%", i).is_none() { - let j = match self.parse_name(i) { + let j = match self.try_parse_name(i) { Ok((j, n)) => { self.ast .expect_unused @@ -455,101 +460,107 @@ impl YaccParser { j } Err(_) => { - return Err(self.mk_error(YaccGrammarErrorKind::UnknownSymbol, i)) + return Err(errs.send( + self.mk_error(YaccGrammarErrorKind::UnknownSymbol, i), + )?); } }, }; - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; } continue; } if let Some(j) = self.lookahead_is("%expect", i) { - i = self.parse_ws(j, false)?; - let (j, n) = self.parse_int(i)?; + i = self.parse_ws(j, false, errs)?; + let (j, n) = self.parse_int(i, errs)?; let span = Span::new(i, j); if let Some((_, orig_span)) = self.ast.expect { - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateExpectDeclaration, - orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateExpectDeclaration, + spans: vec![orig_span, span], + })?; } else { self.ast.expect = Some((n, span)); } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } if let Some(j) = self.lookahead_is("%avoid_insert", i) { - i = self.parse_ws(j, false)?; + i = self.parse_ws(j, false, errs)?; let num_newlines = self.num_newlines; if self.ast.avoid_insert.is_none() { self.ast.avoid_insert = Some(HashMap::new()); } while j < self.src.len() && self.num_newlines == num_newlines { - let (j, n, span) = self.parse_token(i)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n, span) = result.unwrap(); if self.ast.tokens.insert(n.clone()) { self.ast.spans.push(span); } match self.ast.avoid_insert.as_mut().unwrap().entry(n) { Entry::Occupied(occupied) => { - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateAvoidInsertDeclaration, - *occupied.get(), - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateAvoidInsertDeclaration, + spans: vec![*occupied.get(), span], + })?; } Entry::Vacant(vacant) => { vacant.insert(span); } } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; } continue; } if let Some(j) = self.lookahead_is("%parse-param", i) { - i = self.parse_ws(j, false)?; - let (j, name) = self.parse_to_single_colon(i)?; + i = self.parse_ws(j, false, errs)?; + let (j, name) = self.parse_to_single_colon(i, errs)?; match self.lookahead_is(":", j) { - Some(j) => i = self.parse_ws(j, false)?, + Some(j) => i = self.parse_ws(j, false, errs)?, None => { - return Err(self.mk_error(YaccGrammarErrorKind::MissingColon, j)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::MissingColon, j))? + ); } } - let (j, ty) = self.parse_to_eol(i)?; + let (j, ty) = self.parse_to_eol(i, errs)?; self.ast.parse_param = Some((name, ty)); - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } if let YaccKind::Eco = self.yacc_kind { if let Some(j) = self.lookahead_is("%implicit_tokens", i) { - i = self.parse_ws(j, false)?; + i = self.parse_ws(j, false, errs)?; let num_newlines = self.num_newlines; if self.ast.implicit_tokens.is_none() { self.ast.implicit_tokens = Some(HashMap::new()); } while j < self.src.len() && self.num_newlines == num_newlines { - let (j, n, span) = self.parse_token(i)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n, span) = result.unwrap(); if self.ast.tokens.insert(n.clone()) { self.ast.spans.push(span); } match self.ast.implicit_tokens.as_mut().unwrap().entry(n) { Entry::Occupied(entry) => { let orig_span = *entry.get(); - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicateImplicitTokensDeclaration, - orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicateImplicitTokensDeclaration, + spans: vec![orig_span, span], + })?; } Entry::Vacant(entry) => { entry.insert(span); } } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; } continue; } @@ -567,22 +578,26 @@ impl YaccParser { kind = AssocKind::Nonassoc; k = j; } else { - return Err(self.mk_error(YaccGrammarErrorKind::UnknownDeclaration, i)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::UnknownDeclaration, i))? + ); } - i = self.parse_ws(k, false)?; + i = self.parse_ws(k, false, errs)?; let num_newlines = self.num_newlines; while i < self.src.len() && num_newlines == self.num_newlines { - let (j, n, span) = self.parse_token(i)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, n, span) = result.unwrap(); match self.ast.precs.entry(n) { Entry::Occupied(orig) => { let (_, orig_span) = orig.get(); - add_duplicate_occurrence( - errs, - YaccGrammarErrorKind::DuplicatePrecedence, - *orig_span, - span, - ); + let _ = errs.send(YaccGrammarError { + kind: YaccGrammarErrorKind::DuplicatePrecedence, + spans: vec![*orig_span, span], + })?; } Entry::Vacant(entry) => { let prec = Precedence { @@ -593,28 +608,40 @@ impl YaccParser { } } - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; } prec_level += 1; } } debug_assert!(i == self.src.len()); - Err(self.mk_error(YaccGrammarErrorKind::PrematureEnd, i)) + Err(errs.send(self.mk_error(YaccGrammarErrorKind::PrematureEnd, i))?) } - fn parse_rules(&mut self, mut i: usize) -> Result { + fn parse_rules( + &mut self, + mut i: usize, + errs: &mut ErrorSender, + ) -> Result { // self.parse_declarations should have left the input at '%%' i = self.lookahead_is("%%", i).unwrap(); - i = self.parse_ws(i, true)?; + i = self.parse_ws(i, true, errs)?; while i < self.src.len() && self.lookahead_is("%%", i).is_none() { - i = self.parse_rule(i)?; - i = self.parse_ws(i, true)?; + i = self.parse_rule(i, errs)?; + i = self.parse_ws(i, true, errs)?; } Ok(i) } - fn parse_rule(&mut self, mut i: usize) -> Result { - let (j, rn) = self.parse_name(i)?; + fn parse_rule( + &mut self, + mut i: usize, + errs: &mut ErrorSender, + ) -> Result { + let result = self.try_parse_name(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, rn) = result.unwrap(); let span = Span::new(i, j); if self.ast.start.is_none() { self.ast.start = Some((rn.clone(), span)); @@ -630,38 +657,40 @@ impl YaccParser { i = j; } YaccKind::Grmtools => { - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; if let Some(j) = self.lookahead_is("->", i) { i = j; } else { - return Err(self.mk_error(YaccGrammarErrorKind::MissingRightArrow, i)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::MissingRightArrow, i))? + ); } - i = self.parse_ws(i, true)?; - let (j, actiont) = self.parse_to_single_colon(i)?; + i = self.parse_ws(i, true, errs)?; + let (j, actiont) = self.parse_to_single_colon(i, errs)?; if self.ast.get_rule(&rn).is_none() { self.ast.add_rule((rn.clone(), span), Some(actiont)); } i = j; } } - i = self.parse_ws(i, true)?; + i = self.parse_ws(i, true, errs)?; match self.lookahead_is(":", i) { Some(j) => i = j, None => { - return Err(self.mk_error(YaccGrammarErrorKind::MissingColon, i)); + return Err(errs.send(self.mk_error(YaccGrammarErrorKind::MissingColon, i))?); } } let mut syms = Vec::new(); let mut prec = None; let mut action = None; - i = self.parse_ws(i, true)?; + i = self.parse_ws(i, true, errs)?; while i < self.src.len() { if let Some(j) = self.lookahead_is("|", i) { self.ast.add_prod(rn.clone(), syms, prec, action); syms = Vec::new(); prec = None; action = None; - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; continue; } else if let Some(j) = self.lookahead_is(";", i) { self.ast.add_prod(rn, syms, prec, action); @@ -669,27 +698,37 @@ impl YaccParser { } if self.lookahead_is("\"", i).is_some() || self.lookahead_is("'", i).is_some() { - let (j, sym, span) = self.parse_token(i)?; - i = self.parse_ws(j, true)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, sym, span) = result.unwrap(); + i = self.parse_ws(j, true, errs)?; if self.ast.tokens.insert(sym.clone()) { self.ast.spans.push(span); } syms.push(Symbol::Token(sym, span)); } else if let Some(j) = self.lookahead_is("%prec", i) { - i = self.parse_ws(j, true)?; - let (k, sym, _) = self.parse_token(i)?; + i = self.parse_ws(j, true, errs)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (k, sym, _) = result.unwrap(); if self.ast.tokens.contains(&sym) { prec = Some(sym); } else { - return Err(self.mk_error(YaccGrammarErrorKind::PrecNotFollowedByToken, i)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::PrecNotFollowedByToken, i))? + ); } i = k; } else if self.lookahead_is("{", i).is_some() { - let (j, a) = self.parse_action(i)?; + let (j, a) = self.parse_action(i, errs)?; i = j; action = Some(a); } else if let Some(mut j) = self.lookahead_is("%empty", i) { - j = self.parse_ws(j, true)?; + j = self.parse_ws(j, true, errs)?; // %empty could be followed by all sorts of weird syntax errors: all we try and do // is say "does this production look like it's finished" and trust that the other // errors will be caught by other parts of the parser. @@ -698,11 +737,17 @@ impl YaccParser { || self.lookahead_is(";", j).is_some() || self.lookahead_is("{", j).is_some()) { - return Err(self.mk_error(YaccGrammarErrorKind::NonEmptyProduction, i)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::NonEmptyProduction, i))? + ); } i = j; } else { - let (j, sym, span) = self.parse_token(i)?; + let result = self.parse_token(i); + if let Err(e) = result { + return Err(errs.send(e)?); + } + let (j, sym, span) = result.unwrap(); if self.ast.tokens.contains(&sym) { syms.push(Symbol::Token(sym, span)); } else { @@ -710,12 +755,12 @@ impl YaccParser { } i = j; } - i = self.parse_ws(i, true)?; + i = self.parse_ws(i, true, errs)?; } - Err(self.mk_error(YaccGrammarErrorKind::IncompleteRule, i)) + Err(errs.send(self.mk_error(YaccGrammarErrorKind::IncompleteRule, i))?) } - fn parse_name(&self, i: usize) -> Result<(usize, String), YaccGrammarError> { + fn try_parse_name(&self, i: usize) -> Result<(usize, String), YaccGrammarError> { match RE_NAME.find(&self.src[i..]) { Some(m) => { assert_eq!(m.start(), 0); @@ -751,7 +796,11 @@ impl YaccParser { } } - fn parse_action(&mut self, i: usize) -> Result<(usize, String), YaccGrammarError> { + fn parse_action( + &mut self, + i: usize, + errs: &mut ErrorSender, + ) -> Result<(usize, String), YaccGrammarConstructionFailure> { debug_assert!(self.lookahead_is("{", i).is_some()); let mut j = i; let mut c = 0; // Count braces @@ -772,7 +821,7 @@ impl YaccParser { j += ch.len_utf8(); } if c > 0 { - Err(self.mk_error(YaccGrammarErrorKind::IncompleteAction, j)) + Err(errs.send(self.mk_error(YaccGrammarErrorKind::IncompleteAction, j))?) } else { debug_assert!(self.lookahead_is("}", j).is_some()); let s = self.src[i + '{'.len_utf8()..j].trim().to_string(); @@ -783,10 +832,10 @@ impl YaccParser { fn parse_programs( &mut self, mut i: usize, - _: &mut Vec, - ) -> Result { + errs: &mut ErrorSender, + ) -> Result { if let Some(j) = self.lookahead_is("%%", i) { - i = self.parse_ws(j, true)?; + i = self.parse_ws(j, true, errs)?; let prog = self.src[i..].to_string(); i += prog.len(); self.ast.set_programs(prog); @@ -795,7 +844,11 @@ impl YaccParser { } /// Parse up to (but do not include) the end of line (or, if it comes sooner, the end of file). - fn parse_to_eol(&mut self, i: usize) -> Result<(usize, String), YaccGrammarError> { + fn parse_to_eol( + &mut self, + i: usize, + _: &mut ErrorSender, + ) -> Result<(usize, String), YaccGrammarConstructionFailure> { let mut j = i; while j < self.src.len() { let c = self.src[j..].chars().next().unwrap(); @@ -809,7 +862,11 @@ impl YaccParser { /// Parse up to (but do not include) a single colon (double colons are allowed so that strings /// like `a::b::c:` treat `a::b::c` as a single name. Errors if EOL encountered. - fn parse_to_single_colon(&mut self, i: usize) -> Result<(usize, String), YaccGrammarError> { + fn parse_to_single_colon( + &mut self, + i: usize, + errs: &mut ErrorSender, + ) -> Result<(usize, String), YaccGrammarConstructionFailure> { let mut j = i; while j < self.src.len() { let c = self.src[j..].chars().next().unwrap(); @@ -828,14 +885,15 @@ impl YaccParser { _ => j += c.len_utf8(), } } - Err(self.mk_error(YaccGrammarErrorKind::ReachedEOL, j)) + Err(errs.send(self.mk_error(YaccGrammarErrorKind::ReachedEOL, j))?) } /// Parse a quoted string, allowing escape characters. fn parse_int( &mut self, i: usize, - ) -> Result<(usize, T), YaccGrammarError> { + errs: &mut ErrorSender, + ) -> Result<(usize, T), YaccGrammarConstructionFailure> { let mut j = i; while j < self.src.len() { let c = self.src[j..].chars().next().unwrap(); @@ -846,18 +904,22 @@ impl YaccParser { } match self.src[i..j].parse::() { Ok(x) => Ok((j, x)), - Err(_) => Err(self.mk_error(YaccGrammarErrorKind::IllegalInteger, i)), + Err(_) => Err(errs.send(self.mk_error(YaccGrammarErrorKind::IllegalInteger, i))?), } } /// Parse a quoted string, allowing escape characters. - fn parse_string(&mut self, mut i: usize) -> Result<(usize, String), YaccGrammarError> { + fn parse_string( + &mut self, + mut i: usize, + errs: &mut ErrorSender, + ) -> Result<(usize, String), YaccGrammarConstructionFailure> { let qc = if self.lookahead_is("'", i).is_some() { '\'' } else if self.lookahead_is("\"", i).is_some() { '"' } else { - return Err(self.mk_error(YaccGrammarErrorKind::InvalidString, i)); + return Err(errs.send(self.mk_error(YaccGrammarErrorKind::InvalidString, i))?); }; debug_assert!('"'.len_utf8() == 1 && '\''.len_utf8() == 1); @@ -872,7 +934,7 @@ impl YaccParser { let c = self.src[j..].chars().next().unwrap(); match c { '\n' | '\r' => { - return Err(self.mk_error(YaccGrammarErrorKind::InvalidString, j)); + return Err(errs.send(self.mk_error(YaccGrammarErrorKind::InvalidString, j))?); } x if x == qc => { s.push_str(&self.src[i..j]); @@ -887,26 +949,33 @@ impl YaccParser { j += 2; } _ => { - return Err(self.mk_error(YaccGrammarErrorKind::InvalidString, j)); + return Err( + errs.send(self.mk_error(YaccGrammarErrorKind::InvalidString, j))? + ); } } } _ => j += c.len_utf8(), } } - Err(self.mk_error(YaccGrammarErrorKind::InvalidString, j)) + Err(errs.send(self.mk_error(YaccGrammarErrorKind::InvalidString, j))?) } /// Skip whitespace from `i` onwards. If `inc_newlines` is `false`, will return `Err` if a /// newline is encountered; otherwise newlines are consumed and skipped. - fn parse_ws(&mut self, mut i: usize, inc_newlines: bool) -> Result { + fn parse_ws( + &mut self, + mut i: usize, + inc_newlines: bool, + errs: &mut ErrorSender, + ) -> Result { while i < self.src.len() { let c = self.src[i..].chars().next().unwrap(); match c { ' ' | '\t' => i += c.len_utf8(), '\n' | '\r' => { if !inc_newlines { - return Err(self.mk_error(YaccGrammarErrorKind::ReachedEOL, i)); + return Err(errs.send(self.mk_error(YaccGrammarErrorKind::ReachedEOL, i))?); } self.num_newlines += 1; i += c.len_utf8(); @@ -939,10 +1008,10 @@ impl YaccParser { match c { '\n' | '\r' => { if !inc_newlines { - return Err(self.mk_error( + return Err(errs.send(self.mk_error( YaccGrammarErrorKind::ReachedEOL, i, - )); + ))?); } self.num_newlines += 1; } @@ -959,9 +1028,9 @@ impl YaccParser { } } if !found { - return Err( - self.mk_error(YaccGrammarErrorKind::IncompleteComment, i) - ); + return Err(errs.send( + self.mk_error(YaccGrammarErrorKind::IncompleteComment, i), + )?); } } _ => break, @@ -1134,27 +1203,26 @@ mod test { src: &str, expected: &mut dyn Iterator)>, ) { - match self { - Ok(_) => panic!("Parsed ok while expecting error"), - Err(errs) - if errs - .iter() - .map(|e| { - // Check that it is valid to slice the source with the spans. - for span in e.spans() { - let _ = &src[span.start()..span.end()]; - } - ( - e.kind.clone(), - e.spans() - .iter() - .map(|span| line_col!(src, span)) - .collect::>(), - ) - }) - .eq(expected) => {} - Err(errs) => incorrect_errs!(src, errs), - } + let errs = self.expect_err("Parsed ok while expecting error"); + let kinds_lines_cols = &errs + .iter() + .map(|e| { + ( + e.kind.clone(), + e.spans() + .iter() + .map(|span| line_col!(src, span)) + .collect::>(), + ) + }) + .collect::>(); + errs.iter().for_each(|e| { + for span in e.spans() { + let _ = &src[span.start()..span.end()]; + } + }); + let expected = expected.collect::>(); + assert_eq!(kinds_lines_cols, &expected); } } @@ -1687,7 +1755,7 @@ x" } #[test] - fn test_multiple_dup_precs() { + fn test_multiple_duplicate_precs() { let src = " %left 'x' %left 'x' @@ -1707,11 +1775,23 @@ x" &mut [ ( YaccGrammarErrorKind::DuplicatePrecedence, - vec![(2, 18), (3, 18), (4, 19), (5, 22)], + vec![(2, 18), (3, 18)], + ), + ( + YaccGrammarErrorKind::DuplicatePrecedence, + vec![(2, 18), (4, 19)], ), ( YaccGrammarErrorKind::DuplicatePrecedence, - vec![(6, 18), (7, 22), (8, 19)], + vec![(2, 18), (5, 22)], + ), + ( + YaccGrammarErrorKind::DuplicatePrecedence, + vec![(6, 18), (7, 22)], + ), + ( + YaccGrammarErrorKind::DuplicatePrecedence, + vec![(6, 18), (8, 19)], ), ] .into_iter(), @@ -2024,10 +2104,13 @@ x" %epp A \"a\" %% "; - parse(YaccKind::Eco, src).expect_error_at_lines_cols( + parse(YaccKind::Eco, src).expect_multiple_errors( src, - YaccGrammarErrorKind::DuplicateEPP, - &mut [(2, 14), (3, 14), (4, 14)].into_iter(), + &mut [ + (YaccGrammarErrorKind::DuplicateEPP, vec![(2, 14), (3, 14)]), + (YaccGrammarErrorKind::DuplicateEPP, vec![(2, 14), (4, 14)]), + ] + .into_iter(), ); } @@ -2045,14 +2128,10 @@ x" parse(YaccKind::Eco, src).expect_multiple_errors( src, &mut [ - ( - YaccGrammarErrorKind::DuplicateEPP, - vec![(2, 14), (3, 14), (4, 14)], - ), - ( - YaccGrammarErrorKind::DuplicateEPP, - vec![(5, 14), (6, 14), (7, 14)], - ), + (YaccGrammarErrorKind::DuplicateEPP, vec![(2, 14), (3, 14)]), + (YaccGrammarErrorKind::DuplicateEPP, vec![(2, 14), (4, 14)]), + (YaccGrammarErrorKind::DuplicateEPP, vec![(5, 14), (6, 14)]), + (YaccGrammarErrorKind::DuplicateEPP, vec![(5, 14), (7, 14)]), ] .into_iter(), ); @@ -2115,11 +2194,20 @@ x" YaccKind::Original(YaccOriginalActionKind::GenericParseTree), src, ) - .expect_error_at_lines_cols( + .expect_multiple_errors( src, - YaccGrammarErrorKind::DuplicateExpectDeclaration, - &mut [(2, 19), (3, 19), (4, 19)].into_iter(), - ) + &mut [ + ( + YaccGrammarErrorKind::DuplicateExpectDeclaration, + vec![(2, 19), (3, 19)], + ), + ( + YaccGrammarErrorKind::DuplicateExpectDeclaration, + vec![(2, 19), (4, 19)], + ), + ] + .into_iter(), + ); } #[test] @@ -2139,7 +2227,11 @@ x" &mut [ ( YaccGrammarErrorKind::DuplicateExpectDeclaration, - vec![(2, 19), (3, 19), (4, 19)], + vec![(2, 19), (3, 19)], + ), + ( + YaccGrammarErrorKind::DuplicateExpectDeclaration, + vec![(2, 19), (4, 19)], ), (YaccGrammarErrorKind::MissingColon, vec![(6, 13)]), ] @@ -2159,10 +2251,19 @@ x" YaccKind::Original(YaccOriginalActionKind::GenericParseTree), src, ) - .expect_error_at_lines_cols( + .expect_multiple_errors( src, - YaccGrammarErrorKind::DuplicateExpectRRDeclaration, - &mut [(2, 22), (3, 22), (4, 22)].into_iter(), + &mut [ + ( + YaccGrammarErrorKind::DuplicateExpectRRDeclaration, + vec![(2, 22), (3, 22)], + ), + ( + YaccGrammarErrorKind::DuplicateExpectRRDeclaration, + vec![(2, 22), (4, 22)], + ), + ] + .into_iter(), ); } @@ -2183,7 +2284,11 @@ x" &mut [ ( YaccGrammarErrorKind::DuplicateExpectRRDeclaration, - vec![(2, 22), (3, 22), (4, 22)], + vec![(2, 22), (3, 22)], + ), + ( + YaccGrammarErrorKind::DuplicateExpectRRDeclaration, + vec![(2, 22), (4, 22)], ), (YaccGrammarErrorKind::IllegalName, vec![(6, 11)]), ] @@ -2343,7 +2448,7 @@ x" } #[test] - fn test_only_one_type() { + fn test_duplicate_actiontype() { let src = " %actiontype T1 %actiontype T2 @@ -2354,11 +2459,20 @@ x" YaccKind::Original(YaccOriginalActionKind::GenericParseTree), src, ) - .expect_error_at_lines_cols( + .expect_multiple_errors( src, - YaccGrammarErrorKind::DuplicateActiontypeDeclaration, - &mut [(2, 22), (3, 22), (4, 22)].into_iter(), - ); + &mut [ + ( + YaccGrammarErrorKind::DuplicateActiontypeDeclaration, + vec![(2, 22), (3, 22)], + ), + ( + YaccGrammarErrorKind::DuplicateActiontypeDeclaration, + vec![(2, 22), (4, 22)], + ), + ] + .into_iter(), + ) } #[test] @@ -2376,7 +2490,11 @@ x" &mut [ ( YaccGrammarErrorKind::DuplicateActiontypeDeclaration, - vec![(2, 22), (3, 22), (4, 22)], + vec![(2, 22), (3, 22)], + ), + ( + YaccGrammarErrorKind::DuplicateActiontypeDeclaration, + vec![(2, 22), (4, 22)], ), (YaccGrammarErrorKind::PrematureEnd, vec![(4, 24)]), ] @@ -2441,7 +2559,11 @@ B"; &mut [ ( YaccGrammarErrorKind::DuplicateStartDeclaration, - vec![(1, 8), (2, 8), (3, 8)], + vec![(1, 8), (2, 8)], + ), + ( + YaccGrammarErrorKind::DuplicateStartDeclaration, + vec![(1, 8), (3, 8)], ), (YaccGrammarErrorKind::MissingRightArrow, vec![(6, 2)]), ]