From c5221e509495397b9c0d02939b37f98db0b5c96b Mon Sep 17 00:00:00 2001 From: matt rice Date: Wed, 4 Mar 2026 02:18:12 -0800 Subject: [PATCH] Add ability to get a `Span` from a `PIdx`, improve errors --- cfgrammar/src/lib/yacc/grammar.rs | 14 ++++++++++++++ lrpar/src/lib/ctbuilder.rs | 14 ++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cfgrammar/src/lib/yacc/grammar.rs b/cfgrammar/src/lib/yacc/grammar.rs index ce323295e..242b59097 100644 --- a/cfgrammar/src/lib/yacc/grammar.rs +++ b/cfgrammar/src/lib/yacc/grammar.rs @@ -76,6 +76,10 @@ pub struct YaccGrammar { prods_rules: Box<[RIdx]>, /// The precedence of each production. prod_precs: Box<[Option]>, + /// The span for each production. + /// + /// In the case of an empty span this may be a zero length span. + prod_spans: Box<[Span]>, /// The index of the rule added for implicit tokens, if they were specified; otherwise /// `None`. implicit_rule: Option>, @@ -131,6 +135,7 @@ where rules_prods: Decode::decode(decoder)?, prods_rules: Decode::decode(decoder)?, prod_precs: Decode::decode(decoder)?, + prod_spans: Decode::decode(decoder)?, implicit_rule: Decode::decode(decoder)?, actions: Decode::decode(decoder)?, action_spans: Decode::decode(decoder)?, @@ -171,6 +176,7 @@ where rules_prods: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, prods_rules: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, prod_precs: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, + prod_spans: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, implicit_rule: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, actions: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, action_spans: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?, @@ -463,6 +469,7 @@ where .map(|x| x.unwrap().into_boxed_slice()) .collect(), prod_precs: prod_precs.into_iter().map(Option::unwrap).collect(), + prod_spans: ast.prods.iter().map(|prod| prod.prod_span).collect(), implicit_rule: implicit_rule.map(|x| rule_map[&x]), actions: actions.into_boxed_slice(), action_spans: action_spans.into_boxed_slice(), @@ -513,6 +520,13 @@ where self.prod_precs[usize::from(pidx)] } + /// Return the span for a production `pidx` + /// + /// May return a zero length span such as when there is an empty production. + pub fn prod_span(&self, pidx: PIdx) -> Span { + self.prod_spans[usize::from(pidx)] + } + /// Return the production index of the start rule's sole production (for Yacc grammars the /// start rule is defined to have precisely one production). pub fn start_prod(&self) -> PIdx { diff --git a/lrpar/src/lib/ctbuilder.rs b/lrpar/src/lib/ctbuilder.rs index d078e2e48..a54cd1909 100644 --- a/lrpar/src/lib/ctbuilder.rs +++ b/lrpar/src/lib/ctbuilder.rs @@ -1511,10 +1511,16 @@ where // Iterate over all $-arguments and replace them with their respective // element from the argument vector (e.g. $1 is replaced by args[0]). let pre_action = grm.action(pidx).as_ref().ok_or_else(|| { - format!( - "Rule {} has a production which is missing action code", - grm.rule_name_str(grm.prod_to_rule(pidx)) - ) + let mut s = String::from("\n"); + let span = grm.prod_span(pidx); + s.push_str(&diag.file_location_msg("Error", Some(span))); + s.push_str("\n"); + s.push_str(&diag.underline_span_with_text( + span, + "Production is missing action code".to_string(), + '^', + )); + ErrorString(s) })?; let mut last = 0; let mut outs = String::new();