very rough error handling

This commit is contained in:
Edward Shen 2021-02-25 11:11:26 -05:00
parent 23e2a37785
commit 6b9fb8f8e5
Signed by: edward
GPG key ID: 19182661E818369F
2 changed files with 73 additions and 32 deletions

View file

@ -163,27 +163,39 @@ impl Display for ParsedComment<'_> {
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub struct ParserError<'a> { pub struct ParserError<'a> {
line_number: usize, line_number: usize,
last_attempted_parser: ParserNode, last_attempted_parser: Vec<ParserNode>,
parsed_until: &'a [u8], parsed_until: &'a [u8],
} }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug, Clone, Copy)]
enum ParserNode { enum ParserNode {
Section, SectionHeader,
SectionBody, ConfigName,
ConfigValue,
Comment, Comment,
Eof, }
impl Display for ParserNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SectionHeader => write!(f, "section header"),
Self::ConfigName => write!(f, "config name"),
Self::ConfigValue => write!(f, "config value"),
Self::Comment => write!(f, "comment"),
}
}
} }
impl Display for ParserError<'_> { impl Display for ParserError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let data_size = self.parsed_until.len(); let data_size = self.parsed_until.len();
let data = std::str::from_utf8(self.parsed_until); let data = std::str::from_utf8(self.parsed_until);
let failed_parser = self.last_attempted_parser[self.last_attempted_parser.len() - 2];
write!( write!(
f, f,
"Got an unexpected token on line {} while trying to parse a {:?}: ", "Got an unexpected token on line {} while trying to parse a {}: ",
self.line_number, self.last_attempted_parser self.line_number + 1,
failed_parser
)?; )?;
match (data, data_size) { match (data, data_size) {
@ -556,11 +568,12 @@ pub fn parse_from_bytes(input: &[u8]) -> Result<Parser<'_>, ParserError> {
}); });
} }
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
let (i, sections) = many1(|i| section(i, &mut node))(i).map_err(|_| ParserError { let maybe_sections = many1(|i| section(i, &mut node))(i);
let (i, sections) = maybe_sections.map_err(|_| ParserError {
line_number: newlines, line_number: newlines,
last_attempted_parser: ParserNode::Section, last_attempted_parser: node.clone(),
parsed_until: i, parsed_until: i,
})?; })?;
@ -600,9 +613,12 @@ fn comment<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedComment> {
fn section<'a, 'b>( fn section<'a, 'b>(
i: &'a [u8], i: &'a [u8],
node: &'b mut ParserNode, node: &'b mut Vec<ParserNode>,
) -> IResult<&'a [u8], (ParsedSection<'a>, usize)> { ) -> IResult<&'a [u8], (ParsedSection<'a>, usize)> {
node.push(ParserNode::SectionHeader);
let (i, section_header) = section_header(i)?; let (i, section_header) = section_header(i)?;
node.pop();
let mut newlines = 0; let mut newlines = 0;
// todo: unhack this (manually implement many0 and alt to avoid closure moves) // todo: unhack this (manually implement many0 and alt to avoid closure moves)
let node = std::sync::Mutex::new(node); let node = std::sync::Mutex::new(node);
@ -614,17 +630,27 @@ fn section<'a, 'b>(
newlines += counter; newlines += counter;
vec![Event::Newline(Cow::Borrowed(newline.into()))] vec![Event::Newline(Cow::Borrowed(newline.into()))]
}), }),
map(section_body, |(key, values)| { map(
**node.lock().unwrap() = ParserNode::SectionBody; |i| section_body(i, *node.lock().unwrap()),
|(key, values)| {
let mut vec = vec![Event::Key(Cow::Borrowed(key.into()))]; let mut vec = vec![Event::Key(Cow::Borrowed(key.into()))];
vec.extend(values); vec.extend(values);
vec vec
}), },
map(comment, |comment| { ),
**node.lock().unwrap() = ParserNode::Comment; map(
|i| {
node.lock().unwrap().push(ParserNode::Comment);
comment(i)
},
|comment| {
node.lock().unwrap().pop();
vec![Event::Comment(comment)] vec![Event::Comment(comment)]
}), },
),
)))(i)?; )))(i)?;
node.lock().unwrap().pop();
Ok(( Ok((
i, i,
( (
@ -688,11 +714,21 @@ fn section_header<'a>(i: &'a [u8]) -> IResult<&'a [u8], ParsedSectionHeader> {
)) ))
} }
fn section_body<'a>(i: &'a [u8]) -> IResult<&'a [u8], (&'a [u8], Vec<Event>)> { fn section_body<'a, 'b>(
i: &'a [u8],
node: &'b mut Vec<ParserNode>,
) -> IResult<&'a [u8], (&'a [u8], Vec<Event<'a>>)> {
// maybe need to check for [ here // maybe need to check for [ here
node.push(ParserNode::ConfigName);
let (i, name) = config_name(i)?; let (i, name) = config_name(i)?;
node.pop();
let (i, whitespace) = opt(take_spaces)(i)?; let (i, whitespace) = opt(take_spaces)(i)?;
node.push(ParserNode::ConfigValue);
let (i, value) = config_value(i)?; let (i, value) = config_value(i)?;
node.pop();
if let Some(whitespace) = whitespace { if let Some(whitespace) = whitespace {
let mut events = vec![Event::Whitespace(Cow::Borrowed(whitespace.into()))]; let mut events = vec![Event::Whitespace(Cow::Borrowed(whitespace.into()))];
events.extend(value); events.extend(value);
@ -992,8 +1028,9 @@ mod section_body {
#[test] #[test]
fn whitespace_is_not_ambigious() { fn whitespace_is_not_ambigious() {
let mut node = vec![ParserNode::SectionHeader];
assert_eq!( assert_eq!(
section_body(b"a =b").unwrap().1, section_body(b"a =b", &mut node).unwrap().1,
( (
"a".as_bytes(), "a".as_bytes(),
vec![ vec![
@ -1004,7 +1041,7 @@ mod section_body {
) )
); );
assert_eq!( assert_eq!(
section_body(b"a= b").unwrap().1, section_body(b"a= b", &mut node).unwrap().1,
( (
"a".as_bytes(), "a".as_bytes(),
vec![ vec![
@ -1169,7 +1206,7 @@ mod section {
#[test] #[test]
fn empty_section() { fn empty_section() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
assert_eq!( assert_eq!(
section(b"[test]", &mut node).unwrap(), section(b"[test]", &mut node).unwrap(),
fully_consumed(( fully_consumed((
@ -1184,7 +1221,7 @@ mod section {
#[test] #[test]
fn simple_section() { fn simple_section() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
let section_data = br#"[hello] let section_data = br#"[hello]
a = b a = b
c c
@ -1222,7 +1259,7 @@ mod section {
#[test] #[test]
fn section_single_line() { fn section_single_line() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
assert_eq!( assert_eq!(
section(b"[hello] c", &mut node).unwrap(), section(b"[hello] c", &mut node).unwrap(),
fully_consumed(( fully_consumed((
@ -1237,7 +1274,7 @@ mod section {
#[test] #[test]
fn section_very_commented() { fn section_very_commented() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
let section_data = br#"[hello] ; commentA let section_data = br#"[hello] ; commentA
a = b # commentB a = b # commentB
; commentC ; commentC
@ -1282,7 +1319,7 @@ mod section {
#[test] #[test]
fn complex_continuation() { fn complex_continuation() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
// This test is absolute hell. Good luck if this fails. // This test is absolute hell. Good luck if this fails.
assert_eq!( assert_eq!(
section( section(
@ -1315,7 +1352,7 @@ mod section {
#[test] #[test]
fn quote_split_over_two_lines() { fn quote_split_over_two_lines() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
assert_eq!( assert_eq!(
section(b"[section \"a\"] b =\"\\\n;\";a", &mut node).unwrap(), section(b"[section \"a\"] b =\"\\\n;\";a", &mut node).unwrap(),
fully_consumed(( fully_consumed((
@ -1339,7 +1376,7 @@ mod section {
#[test] #[test]
fn section_handles_extranous_whitespace_before_comment() { fn section_handles_extranous_whitespace_before_comment() {
let mut node = ParserNode::Eof; let mut node = vec![ParserNode::SectionHeader];
assert_eq!( assert_eq!(
section(b"[s]hello #world", &mut node).unwrap(), section(b"[s]hello #world", &mut node).unwrap(),
fully_consumed(( fully_consumed((

View file

@ -221,4 +221,8 @@ fn newline_events_are_merged() {
fn error() { fn error() {
let input = "[core] a=b\n 4a=3"; let input = "[core] a=b\n 4a=3";
println!("{}", parse_from_str(input).unwrap_err()); println!("{}", parse_from_str(input).unwrap_err());
let input = "[core] a=b\n =3";
println!("{}", parse_from_str(input).unwrap_err());
let input = "[core";
println!("{}", parse_from_str(input).unwrap_err());
} }