use std::{any::Any, collections::HashMap};
use crate::ParseError;
pub struct Reported;
pub struct ParseContext<'parse> {
source: &'parse str,
foremost_error: Option<ParseError>,
rule_sets: HashMap<usize, &'parse [Box<dyn Any>]>,
}
impl<'parse> ParseContext<'parse> {
pub fn new(source: &'parse str) -> Self {
ParseContext {
source,
foremost_error: None,
rule_sets: HashMap::new(),
}
}
pub fn source(&self) -> &'parse str {
self.source
}
pub fn into_reported_error(self) -> ParseError {
self.foremost_error
.expect("a parse error should have been reported")
}
pub(crate) fn with_slice<F, T>(&mut self, start: usize, end: usize, f: F) -> Result<T, Reported>
where
F: for<'a> FnOnce(&'a mut Self) -> Result<T, Reported>,
{
let mut inner_context = ParseContext {
source: &self.source[start..end],
foremost_error: None,
rule_sets: HashMap::new(),
};
std::mem::swap(&mut self.rule_sets, &mut inner_context.rule_sets);
let r = f(&mut inner_context);
std::mem::swap(&mut self.rule_sets, &mut inner_context.rule_sets);
if r.is_err() {
self.report(
inner_context
.into_reported_error()
.adjust_location(self.source, start),
);
}
r
}
pub fn report(&mut self, err: ParseError) -> Reported {
if Some(err.location) > self.foremost_error.as_ref().map(|err| err.location) {
self.foremost_error = Some(err);
}
Reported
}
pub fn error_expected(&mut self, start: usize, expected: &str) -> Reported {
self.report(ParseError::new_expected(self.source(), start, expected))
}
pub fn error_from_str_failed(
&mut self,
start: usize,
end: usize,
type_name: &'static str,
message: String,
) -> Reported {
self.report(ParseError::new_from_str_failed(
self.source(),
start,
end,
type_name,
message,
))
}
pub fn error_extra(&mut self, location: usize) -> Reported {
self.report(ParseError::new_extra(self.source(), location))
}
pub(crate) fn register_rule_set(
&mut self,
rule_set_id: usize,
rule_parsers: &'parse [Box<dyn Any>],
) {
self.rule_sets.insert(rule_set_id, rule_parsers);
}
pub(crate) fn fetch_parser_for_rule(
&self,
rule_set_id: usize,
index: usize,
) -> &'parse dyn Any {
let rule_parsers: &'parse [Box<dyn Any>] = self
.rule_sets
.get(&rule_set_id)
.expect("internal error: rule set not registered");
&*rule_parsers[index]
}
}
#[cfg(test)]
mod tests {
use crate::parsers::{lines, sections, u64};
use crate::testing::*;
#[test]
fn test_repeat_region_errors() {
let p = lines(u64);
let example1 = "\
194832\n\
2094235\n\
374274\n\
4297534:wq\n\
59842843\n\
";
assert_parse_error(
&p,
example1,
"matched part of the line, but not all of it at line 4 column 8",
);
let p = sections(lines(u64));
let example2 = "\
1\n\
\n\
3\n\
4\n\
5\n\
6:wq\n\
\n\
8\n\
9\n\
";
assert_parse_error(
&p,
example2,
"matched part of the line, but not all of it at line 6 column 2",
);
}
}