Skip to content

Commit 1cc1df3

Browse files
committed
Allow recoverer key in Header
1 parent c5d7b00 commit 1cc1df3

3 files changed

Lines changed: 140 additions & 15 deletions

File tree

lrpar/src/lib/ctbuilder.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ where
235235
grammar_path: Option<PathBuf>,
236236
output_path: Option<PathBuf>,
237237
mod_name: Option<&'a str>,
238-
recoverer: RecoveryKind,
238+
recoverer: Option<RecoveryKind>,
239239
yacckind: Option<YaccKind>,
240240
error_on_conflicts: bool,
241241
warnings_are_errors: bool,
@@ -279,7 +279,7 @@ where
279279
grammar_path: None,
280280
output_path: None,
281281
mod_name: None,
282-
recoverer: RecoveryKind::CPCTPlus,
282+
recoverer: None,
283283
yacckind: None,
284284
error_on_conflicts: true,
285285
warnings_are_errors: true,
@@ -378,7 +378,7 @@ where
378378

379379
/// Set the recoverer for this parser to `rk`. Defaults to `RecoveryKind::CPCTPlus`.
380380
pub fn recoverer(mut self, rk: RecoveryKind) -> Self {
381-
self.recoverer = rk;
381+
self.recoverer = Some(rk);
382382
self
383383
}
384384

@@ -494,6 +494,18 @@ where
494494
}
495495
},
496496
}
497+
if let Some(recoverer) = self.recoverer {
498+
match header.entry("recoverer".to_string()) {
499+
Entry::Occupied(_) => unreachable!(),
500+
Entry::Vacant(v) => {
501+
let rk_value: Value = Value::try_from(recoverer)?;
502+
let mut o =
503+
v.insert_entry((Location::Other("CTParserBuilder".to_string()), rk_value));
504+
o.set_merge_behavior(MergeBehavior::Ours);
505+
}
506+
}
507+
}
508+
497509
{
498510
let mut lk = GENERATED_PATHS.lock().unwrap();
499511
if lk.contains(outp.as_path()) {
@@ -505,6 +517,16 @@ where
505517
let inc =
506518
read_to_string(grmp).map_err(|e| format!("When reading '{}': {e}", grmp.display()))?;
507519
let ast_validation = ASTWithValidityInfo::new(&mut header, &inc);
520+
header.mark_used(&"recoverer".to_string());
521+
let rk_val = header.get("recoverer").map(|(_, rk_val)| rk_val);
522+
523+
if let Some(rk_val) = rk_val {
524+
self.recoverer = Some(RecoveryKind::try_from(rk_val)?);
525+
} else {
526+
// Fallback to the default recoverykind.
527+
self.recoverer = Some(RecoveryKind::CPCTPlus);
528+
}
529+
508530
let unused_keys = header.unused();
509531
if !unused_keys.is_empty() {
510532
return Err(format!("Unused keys in header: {}", unused_keys.join(", ")).into());

lrpar/src/lib/parser.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::time::{Duration, Instant};
1414
use web_time::{Duration, Instant};
1515

1616
use cactus::Cactus;
17-
use cfgrammar::{yacc::YaccGrammar, RIdx, Span, TIdx};
17+
use cfgrammar::{header::Value, yacc::YaccGrammar, RIdx, Span, TIdx};
1818
use lrtable::{Action, StIdx, StateTable};
1919
use num_traits::{AsPrimitive, PrimInt, Unsigned};
2020
use proc_macro2::TokenStream;
@@ -629,6 +629,90 @@ pub enum RecoveryKind {
629629
None,
630630
}
631631

632+
impl TryFrom<RecoveryKind> for Value {
633+
type Error = cfgrammar::header::HeaderError;
634+
fn try_from(rk: RecoveryKind) -> Result<Value, Self::Error> {
635+
use cfgrammar::{
636+
header::{Namespaced, Setting},
637+
Location,
638+
};
639+
let from_loc = Location::Other("From<RecoveryKind>".to_string());
640+
Ok(match rk {
641+
RecoveryKind::CPCTPlus => Value::Setting(Setting::Unitary(Namespaced {
642+
namespace: Some(("RecoveryKind".to_string(), from_loc.clone())),
643+
member: ("CPCTPlus".to_string(), from_loc.clone()),
644+
})),
645+
RecoveryKind::None => Value::Setting(Setting::Unitary(Namespaced {
646+
namespace: Some(("RecoveryKind".to_string(), from_loc.clone())),
647+
member: ("None".to_string(), from_loc.clone()),
648+
})),
649+
})
650+
}
651+
}
652+
653+
impl TryFrom<&Value> for RecoveryKind {
654+
type Error = cfgrammar::header::HeaderError;
655+
fn try_from(rk: &Value) -> Result<RecoveryKind, Self::Error> {
656+
use cfgrammar::header::{HeaderError, HeaderErrorKind, Namespaced, Setting};
657+
658+
match rk {
659+
Value::Flag(_, loc) => Err(HeaderError {
660+
kind: HeaderErrorKind::ConversionError(
661+
"RecoveryKind",
662+
"Cannot convert boolean to RecoveryKind",
663+
),
664+
locations: vec![loc.clone()],
665+
}),
666+
Value::Setting(Setting::Num(_, loc)) => Err(HeaderError {
667+
kind: HeaderErrorKind::ConversionError(
668+
"RecoveryKind",
669+
"Cannot convert number to RecoveryKind",
670+
),
671+
locations: vec![loc.clone()],
672+
}),
673+
Value::Setting(Setting::Unitary(Namespaced {
674+
namespace,
675+
member: (kind, kind_loc),
676+
})) => {
677+
match namespace {
678+
Some((ns, loc)) if ns.to_lowercase() != "recoverykind" => {
679+
return Err(HeaderError {
680+
kind: HeaderErrorKind::ConversionError(
681+
"RecoveryKind",
682+
"Unknown namespace",
683+
),
684+
locations: vec![loc.clone()],
685+
})
686+
}
687+
_ => {}
688+
}
689+
match kind.to_lowercase().as_ref() {
690+
"cpctplus" => Ok(RecoveryKind::CPCTPlus),
691+
"none" => Ok(RecoveryKind::None),
692+
_ => Err(HeaderError {
693+
kind: HeaderErrorKind::ConversionError("RecoveryKind", "Unknown variant"),
694+
locations: vec![kind_loc.clone()],
695+
}),
696+
}
697+
}
698+
Value::Setting(Setting::Constructor {
699+
ctor: _,
700+
arg:
701+
Namespaced {
702+
namespace: _,
703+
member: (_, arg_loc),
704+
},
705+
}) => Err(HeaderError {
706+
kind: HeaderErrorKind::ConversionError(
707+
"RecoveryKind",
708+
"Cannot convert constructor to RecoveryKind",
709+
),
710+
locations: vec![arg_loc.clone()],
711+
}),
712+
}
713+
}
714+
}
715+
632716
impl quote::ToTokens for RecoveryKind {
633717
fn to_tokens(&self, tokens: &mut TokenStream) {
634718
tokens.extend(match *self {

nimbleparse/src/main.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,22 +114,29 @@ fn main() {
114114

115115
let dump_state_graph = matches.opt_present("d");
116116
let quiet = matches.opt_present("q");
117-
118-
let recoverykind = match matches.opt_str("r") {
119-
None => RecoveryKind::CPCTPlus,
120-
Some(s) => match &*s.to_lowercase() {
121-
"cpctplus" => RecoveryKind::CPCTPlus,
122-
"none" => RecoveryKind::None,
123-
_ => usage(prog, &format!("Unknown recoverer '{}'.", s)),
124-
},
125-
};
126-
127117
let mut header = Header::new();
118+
match matches.opt_str("r") {
119+
None => (),
120+
Some(s) => {
121+
header.set_merge_behavior(&"recoverer".to_string(), cfgrammar::markmap::MergeBehavior::Ours);
122+
header.insert(
123+
"recoverer".to_string(),
124+
(
125+
Location::CommandLine,
126+
Value::try_from(match &*s.to_lowercase() {
127+
"cpctplus" => RecoveryKind::CPCTPlus,
128+
"none" => RecoveryKind::None,
129+
_ => usage(prog, &format!("Unknown recoverer '{}'.", s)),
130+
})
131+
.expect("All these RecoveryKinds should convert without error"),
132+
),
133+
);
134+
}
135+
};
128136
let entry = match header.entry("yacckind".to_string()) {
129137
Entry::Occupied(_) => unreachable!("Header should be empty"),
130138
Entry::Vacant(v) => v.occupied_entry(),
131139
};
132-
133140
match matches.opt_str("y") {
134141
None => {}
135142
Some(s) => {
@@ -167,6 +174,18 @@ fn main() {
167174
let yacc_y_path = PathBuf::from(&matches.free[1]);
168175
let yacc_src = read_file(&yacc_y_path);
169176
let ast_validation = ASTWithValidityInfo::new(&mut header, &yacc_src);
177+
let recoverykind = if let Some((_, rk_val)) = header.get("recoverer") {
178+
match RecoveryKind::try_from(rk_val) {
179+
Err(e) => {
180+
eprintln!("{e}");
181+
process::exit(1)
182+
}
183+
Ok(rk) => rk,
184+
}
185+
} else {
186+
// Fallback to the default recoverykind
187+
RecoveryKind::CPCTPlus
188+
};
170189
let warnings = ast_validation.ast().warnings();
171190
let res = YaccGrammar::new_from_ast_with_validity_info(&ast_validation);
172191
let mut yacc_diagnostic_formatter: Option<SpannedDiagnosticFormatter> = None;

0 commit comments

Comments
 (0)