parser: simplify ErrorContext construction

This commit is contained in:
Dirkjan Ochtman 2024-05-21 10:14:33 +02:00
parent c3582f5d89
commit 627d58bc3b
3 changed files with 62 additions and 73 deletions

View File

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::collections::HashSet; use std::collections::HashSet;
use std::str; use std::str;
@ -111,12 +110,10 @@ impl<'a> Expr<'a> {
move |i| Self::parse(i, level), move |i| Self::parse(i, level),
))(i)?; ))(i)?;
if has_named_arguments && !matches!(expr, Self::NamedArgument(_, _)) { if has_named_arguments && !matches!(expr, Self::NamedArgument(_, _)) {
Err(nom::Err::Failure(ErrorContext { Err(nom::Err::Failure(ErrorContext::new(
input: start, "named arguments must always be passed last",
message: Some(Cow::Borrowed( start,
"named arguments must always be passed last", )))
)),
}))
} else { } else {
Ok((i, expr)) Ok((i, expr))
} }
@ -146,12 +143,10 @@ impl<'a> Expr<'a> {
if named_arguments.insert(argument) { if named_arguments.insert(argument) {
Ok((i, Self::NamedArgument(argument, Box::new(value)))) Ok((i, Self::NamedArgument(argument, Box::new(value))))
} else { } else {
Err(nom::Err::Failure(ErrorContext { Err(nom::Err::Failure(ErrorContext::new(
input: start, format!("named argument `{argument}` was passed more than once"),
message: Some(Cow::Owned(format!( start,
"named argument `{argument}` was passed more than once" )))
))),
}))
} }
} }

View File

@ -168,9 +168,13 @@ pub(crate) struct ErrorContext<'a> {
impl<'a> ErrorContext<'a> { impl<'a> ErrorContext<'a> {
fn unclosed(kind: &str, tag: &str, i: &'a str) -> Self { fn unclosed(kind: &str, tag: &str, i: &'a str) -> Self {
Self::new(format!("unclosed {kind}, missing {tag:?}"), i)
}
fn new(message: impl Into<Cow<'static, str>>, input: &'a str) -> Self {
Self { Self {
input: i, input,
message: Some(Cow::Owned(format!("unclosed {kind}, missing {tag:?}"))), message: Some(message.into()),
} }
} }
@ -373,19 +377,20 @@ fn char_lit(i: &str) -> ParseResult<'_> {
opt(escaped(is_not("\\\'"), '\\', anychar)), opt(escaped(is_not("\\\'"), '\\', anychar)),
char('\''), char('\''),
)(i)?; )(i)?;
let Some(s) = s else { let Some(s) = s else {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: start, "empty character literal",
// Same error as rustc. start,
message: Some(Cow::Borrowed("empty character literal")), )));
}));
}; };
let Ok(("", c)) = Char::parse(s) else { let Ok(("", c)) = Char::parse(s) else {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: start, "invalid character",
message: Some(Cow::Borrowed("invalid character")), start,
})); )));
}; };
let (nb, max_value, err1, err2) = match c { let (nb, max_value, err1, err2) = match c {
Char::Literal | Char::Escaped => return Ok((i, s)), Char::Literal | Char::Escaped => return Ok((i, s)),
Char::AsciiEscape(nb) => ( Char::AsciiEscape(nb) => (
@ -405,17 +410,12 @@ fn char_lit(i: &str) -> ParseResult<'_> {
}; };
let Ok(nb) = u32::from_str_radix(nb, 16) else { let Ok(nb) = u32::from_str_radix(nb, 16) else {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(err1, start)));
input: start,
message: Some(Cow::Borrowed(err1)),
}));
}; };
if nb > max_value { if nb > max_value {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(err2, start)));
input: start,
message: Some(Cow::Borrowed(err2)),
}));
} }
Ok((i, s)) Ok((i, s))
} }

View File

@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::str; use std::str;
use nom::branch::alt; use nom::branch::alt;
@ -113,10 +112,10 @@ impl<'a> Node<'a> {
)); ));
let (j, (pws, _, nws)) = p(i)?; let (j, (pws, _, nws)) = p(i)?;
if !s.is_in_loop() { if !s.is_in_loop() {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: i, "you can only `break` inside a `for` loop",
message: Some(Cow::Borrowed("you can only `break` inside a `for` loop")), i,
})); )));
} }
Ok((j, Self::Break(Ws(pws, nws)))) Ok((j, Self::Break(Ws(pws, nws))))
} }
@ -129,10 +128,10 @@ impl<'a> Node<'a> {
)); ));
let (j, (pws, _, nws)) = p(i)?; let (j, (pws, _, nws)) = p(i)?;
if !s.is_in_loop() { if !s.is_in_loop() {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: i, "you can only `continue` inside a `for` loop",
message: Some(Cow::Borrowed("you can only `continue` inside a `for` loop")), i,
})); )));
} }
Ok((j, Self::Continue(Ws(pws, nws)))) Ok((j, Self::Continue(Ws(pws, nws))))
} }
@ -297,10 +296,10 @@ impl<'a> Target<'a> {
fn verify_name(input: &'a str, name: &'a str) -> Result<Self, nom::Err<ErrorContext<'a>>> { fn verify_name(input: &'a str, name: &'a str) -> Result<Self, nom::Err<ErrorContext<'a>>> {
match name { match name {
"self" | "writer" => Err(nom::Err::Failure(ErrorContext { "self" | "writer" => Err(nom::Err::Failure(ErrorContext::new(
format!("cannot use `{name}` as a name"),
input, input,
message: Some(Cow::Owned(format!("Cannot use `{name}` as a name"))), ))),
})),
_ => Ok(Self::Name(name)), _ => Ok(Self::Name(name)),
} }
} }
@ -375,12 +374,10 @@ impl<'a> Cond<'a> {
opt(Whitespace::parse), opt(Whitespace::parse),
ws(alt((keyword("else"), |i| { ws(alt((keyword("else"), |i| {
let _ = keyword("elif")(i)?; let _ = keyword("elif")(i)?;
Err(nom::Err::Failure(ErrorContext { Err(nom::Err::Failure(ErrorContext::new(
input: i, "unknown `elif` keyword; did you mean `else if`?",
message: Some(Cow::Borrowed( i,
"unknown `elif` keyword; did you mean `else if`?", )))
)),
}))
}))), }))),
cut(tuple(( cut(tuple((
opt(|i| CondTest::parse(i, s)), opt(|i| CondTest::parse(i, s)),
@ -559,10 +556,10 @@ impl<'a> Macro<'a> {
)); ));
let (j, (pws1, _, (name, params, nws1, _))) = start(i)?; let (j, (pws1, _, (name, params, nws1, _))) = start(i)?;
if name == "super" { if name == "super" {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: i, "'super' is not a valid name for a macro",
message: Some(Cow::Borrowed("'super' is not a valid name for a macro")), i,
})); )));
} }
let mut end = cut(tuple(( let mut end = cut(tuple((
@ -831,15 +828,14 @@ fn check_end_name<'a>(
if name == end_name { if name == end_name {
return Ok((after, end_name)); return Ok((after, end_name));
} }
let message = if name.is_empty() && !end_name.is_empty() {
format!("unexpected name `{end_name}` in `end{kind}` tag for unnamed `{kind}`") Err(nom::Err::Failure(ErrorContext::new(
} else { match name.is_empty() && !end_name.is_empty() {
format!("expected name `{name}` in `end{kind}` tag, found `{end_name}`") true => format!("unexpected name `{end_name}` in `end{kind}` tag for unnamed `{kind}`"),
}; false => format!("expected name `{name}` in `end{kind}` tag, found `{end_name}`"),
Err(nom::Err::Failure(ErrorContext { },
input: before, before,
message: Some(Cow::Owned(message)), )))
}))
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -1036,12 +1032,10 @@ impl<'a> Extends<'a> {
))(i)?; ))(i)?;
match (pws, nws) { match (pws, nws) {
(None, None) => Ok((i, Self { path })), (None, None) => Ok((i, Self { path })),
(_, _) => Err(nom::Err::Failure(ErrorContext { (_, _) => Err(nom::Err::Failure(ErrorContext::new(
input: start, "whitespace control is not allowed on `extends`",
message: Some(Cow::Borrowed( start,
"whitespace control is not allowed on `extends`", ))),
)),
})),
} }
} }
} }
@ -1078,10 +1072,10 @@ impl<'a> Comment<'a> {
Tag::Open => match depth.checked_add(1) { Tag::Open => match depth.checked_add(1) {
Some(new_depth) => depth = new_depth, Some(new_depth) => depth = new_depth,
None => { None => {
return Err(nom::Err::Failure(ErrorContext { return Err(nom::Err::Failure(ErrorContext::new(
input: i, "too deeply nested comments",
message: Some(Cow::Borrowed("too deeply nested comments")), i,
})) )));
} }
}, },
Tag::Close => match depth.checked_sub(1) { Tag::Close => match depth.checked_sub(1) {