Skip to content

Commit ceb7b83

Browse files
committed
Add span information for action code, improve errors.
This adds span information for rust action code in yacc files to the ast. This subsequently gets added to `YaccGrammar` then used for an error message in `CTParserBuilder`.
1 parent 0ebc37c commit ceb7b83

4 files changed

Lines changed: 43 additions & 14 deletions

File tree

cfgrammar/src/lib/yacc/ast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub struct Rule {
190190
pub struct Production {
191191
pub symbols: Vec<Symbol>,
192192
pub precedence: Option<String>,
193-
pub action: Option<String>,
193+
pub action: Option<(String, Span)>,
194194
pub prod_span: Span,
195195
}
196196

@@ -271,7 +271,7 @@ impl GrammarAST {
271271
rule_name: String,
272272
symbols: Vec<Symbol>,
273273
precedence: Option<String>,
274-
action: Option<String>,
274+
action: Option<(String, Span)>,
275275
prod_span: Span,
276276
) {
277277
self.rules[&rule_name].pidxs.push(self.prods.len());

cfgrammar/src/lib/yacc/grammar.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub struct YaccGrammar<StorageT = u32> {
8181
implicit_rule: Option<RIdx<StorageT>>,
8282
/// User defined Rust programs which can be called within actions
8383
actions: Box<[Option<String>]>,
84+
/// Spans for each action.
85+
action_spans: Box<[Option<Span>]>,
8486
/// A `(name, type)` pair defining an extra parameter to pass to action functions.
8587
parse_param: Option<(String, String)>,
8688
/// Generic parameters (types and lifetimes) to pass to action functions.
@@ -131,6 +133,7 @@ where
131133
prod_precs: Decode::decode(decoder)?,
132134
implicit_rule: Decode::decode(decoder)?,
133135
actions: Decode::decode(decoder)?,
136+
action_spans: Decode::decode(decoder)?,
134137
parse_param: Decode::decode(decoder)?,
135138
parse_generics: Decode::decode(decoder)?,
136139
programs: Decode::decode(decoder)?,
@@ -170,6 +173,7 @@ where
170173
prod_precs: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
171174
implicit_rule: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
172175
actions: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
176+
action_spans: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
173177
parse_param: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
174178
parse_generics: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
175179
programs: ::bincode::BorrowDecode::<'_, __Context>::borrow_decode(decoder)?,
@@ -328,6 +332,7 @@ where
328332
let mut prod_precs: Vec<Option<Option<Precedence>>> = vec![None; ast.prods.len()];
329333
let mut prods_rules = vec![None; ast.prods.len()];
330334
let mut actions = vec![None; ast.prods.len()];
335+
let mut action_spans = vec![None; ast.prods.len()];
331336
let mut actiontypes = vec![None; rule_names.len()];
332337
let (start_name, _) = ast.start.as_ref().unwrap();
333338
for (astrulename, _) in &rule_names {
@@ -419,8 +424,9 @@ where
419424
prods[pidx] = Some(prod);
420425
prod_precs[pidx] = Some(prec.map(|(prec, _)| prec));
421426
prods_rules[pidx] = Some(ridx);
422-
if let Some(ref s) = astprod.action {
427+
if let Some((s, span)) = &astprod.action {
423428
actions[pidx] = Some(s.clone());
429+
action_spans[pidx] = Some(*span);
424430
}
425431
}
426432
}
@@ -459,6 +465,7 @@ where
459465
prod_precs: prod_precs.into_iter().map(Option::unwrap).collect(),
460466
implicit_rule: implicit_rule.map(|x| rule_map[&x]),
461467
actions: actions.into_boxed_slice(),
468+
action_spans: action_spans.into_boxed_slice(),
462469
parse_param: ast.parse_param.clone(),
463470
parse_generics: ast.parse_generics.clone(),
464471
programs: ast.programs.clone(),
@@ -625,6 +632,10 @@ where
625632
&self.actions[usize::from(pidx)]
626633
}
627634

635+
pub fn action_span(&self, pidx: PIdx<StorageT>) -> Option<Span> {
636+
self.action_spans[usize::from(pidx)]
637+
}
638+
628639
pub fn actiontype(&self, ridx: RIdx<StorageT>) -> &Option<String> {
629640
&self.actiontypes[usize::from(ridx)]
630641
}

cfgrammar/src/lib/yacc/parser.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -734,10 +734,13 @@ impl YaccParser<'_> {
734734
pos_prod_end = Some(k);
735735
i = k;
736736
} else if self.lookahead_is("{", i).is_some() {
737+
let pos_action_start = i + 1;
737738
pos_prod_end = Some(i);
739+
// With j the location of the right brace, i the location of the left brace.
738740
let (j, a) = self.parse_action(i)?;
739741
i = self.parse_ws(j, true)?;
740-
action = Some(a);
742+
let action_span = Span::new(pos_action_start, pos_action_start + a.len());
743+
action = Some((a, action_span));
741744

742745
if !(self.lookahead_is("|", i).is_some() || self.lookahead_is(";", i).is_some()) {
743746
return Err(self.mk_error(YaccGrammarErrorKind::ProductionNotTerminated, i));
@@ -2284,13 +2287,15 @@ x"
22842287
",
22852288
)
22862289
.unwrap();
2290+
let action_str = "println!(\"test\");".to_string();
22872291
assert_eq!(
22882292
grm.prods[grm.rules["A"].pidxs[0]].action,
2289-
Some("println!(\"test\");".to_string())
2293+
Some((action_str.clone(), Span::new(34, 34 + action_str.len())))
22902294
);
2295+
let action_str = "add($1, $2);".to_string();
22912296
assert_eq!(
22922297
grm.prods[grm.rules["B"].pidxs[0]].action,
2293-
Some("add($1, $2);".to_string())
2298+
Some((action_str.clone(), Span::new(90, 90 + action_str.len())))
22942299
);
22952300
assert_eq!(grm.prods[grm.rules["B"].pidxs[1]].action, None);
22962301
}
@@ -2302,9 +2307,10 @@ x"
23022307
"%%A: '_' {(); // 🦀};",
23032308
)
23042309
.unwrap();
2310+
let action_str = "(); // 🦀".to_string();
23052311
assert_eq!(
23062312
grm.prods[grm.rules["A"].pidxs[0]].action,
2307-
Some("(); // 🦀".to_string())
2313+
Some((action_str.clone(), Span::new(10, 10 + action_str.len())))
23082314
);
23092315
}
23102316

lrpar/src/lib/ctbuilder.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::unstable_api::UnstableApi;
2424

2525
use bincode::{Decode, Encode, decode_from_slice, encode_to_vec};
2626
use cfgrammar::{
27-
Location, RIdx, Symbol,
27+
Location, RIdx, Span, Symbol,
2828
header::{GrmtoolsSectionParser, Header, HeaderValue, Value},
2929
markmap::{Entry, MergeBehavior},
3030
yacc::{YaccGrammar, YaccKind, YaccOriginalActionKind, ast::ASTWithValidityInfo},
@@ -810,6 +810,7 @@ where
810810
&derived_mod_name,
811811
outp,
812812
&format!("/* CACHE INFORMATION {} */\n", cache),
813+
&yacc_diag,
813814
)?;
814815
let conflicts = if stable.conflicts().is_some() {
815816
Some((sgraph, stable))
@@ -937,13 +938,14 @@ where
937938
mod_name: &str,
938939
outp_rs: P,
939940
cache: &str,
941+
diag: &SpannedDiagnosticFormatter,
940942
) -> Result<(), Box<dyn Error>> {
941943
let visibility = self.visibility.clone();
942944
let user_actions = if let Some(
943945
YaccKind::Original(YaccOriginalActionKind::UserAction) | YaccKind::Grmtools,
944946
) = self.yacckind
945947
{
946-
Some(self.gen_user_actions(grm)?)
948+
Some(self.gen_user_actions(grm, diag)?)
947949
} else {
948950
None
949951
};
@@ -1419,7 +1421,11 @@ where
14191421
}
14201422

14211423
/// Generate the user action functions (if any).
1422-
fn gen_user_actions(&self, grm: &YaccGrammar<StorageT>) -> Result<TokenStream, Box<dyn Error>> {
1424+
fn gen_user_actions(
1425+
&self,
1426+
grm: &YaccGrammar<StorageT>,
1427+
diag: &SpannedDiagnosticFormatter,
1428+
) -> Result<TokenStream, Box<dyn Error>> {
14231429
let programs = grm
14241430
.programs()
14251431
.as_ref()
@@ -1520,10 +1526,16 @@ where
15201526
write!(outs, "{prefix}arg_", prefix = ACTION_PREFIX).ok();
15211527
last = last + off + "$".len();
15221528
} else {
1523-
panic!(
1524-
"Unknown text following '$' operator: {}",
1525-
&pre_action[last + off..]
1526-
);
1529+
let span = grm.action_span(pidx).unwrap();
1530+
let inner_span =
1531+
Span::new(span.start() + last + off + "$".len(), span.end());
1532+
let mut s = String::from("\n");
1533+
s.push_str(&diag.underline_span_with_text(
1534+
inner_span,
1535+
"Unknown text following '$'".to_string(),
1536+
'^',
1537+
));
1538+
return Err(ErrorString(s).into());
15271539
}
15281540
}
15291541
None => {

0 commit comments

Comments
 (0)