Better error messages using cuts

This commit is contained in:
René Kijewski 2021-07-30 15:54:38 +02:00 committed by Dirkjan Ochtman
parent 268d8250fb
commit 4123ec37a2
3 changed files with 205 additions and 131 deletions

View File

@ -1,7 +1,7 @@
use nom::branch::alt;
use nom::bytes::complete::{escaped, is_not, tag, take_until};
use nom::character::complete::{anychar, char, digit1};
use nom::combinator::{complete, map, opt, recognize, value};
use nom::combinator::{complete, cut, map, opt, recognize, value};
use nom::error::{Error, ParseError};
use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
@ -387,13 +387,13 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
}
let mut targets = vec![first_target];
let (i, _) = tuple((
let (i, _) = cut(tuple((
fold_many0(preceded(ws(tag(",")), target), (), |_, target| {
targets.push(target);
}),
opt(ws(tag(","))),
ws(tag(")")),
))(i)?;
ws(cut(tag(")"))),
)))(i)?;
return Ok((i, Target::Tuple(Vec::new(), targets)));
}
@ -408,8 +408,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
let (i, targets) = alt((
map(tag(")"), |_| Vec::new()),
terminated(
separated_list1(ws(tag(",")), target),
pair(opt(ws(tag(","))), ws(tag(")"))),
cut(separated_list1(ws(tag(",")), target)),
pair(opt(ws(tag(","))), ws(cut(tag(")")))),
),
))(i)?;
return Ok((i, Target::Tuple(path, targets)));
@ -420,8 +420,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
let (i, targets) = alt((
map(tag("}"), |_| Vec::new()),
terminated(
separated_list1(ws(tag(",")), named_target),
pair(opt(ws(tag(","))), ws(tag("}"))),
cut(separated_list1(ws(tag(",")), named_target)),
pair(opt(ws(tag(","))), ws(cut(tag("}")))),
),
))(i)?;
return Ok((i, Target::Struct(path, targets)));
@ -664,12 +664,11 @@ fn expr_any(i: &[u8]) -> IResult<&[u8], Expr<'_>> {
fn expr_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple((
|i| tag_expr_start(i, s),
opt(tag("-")),
ws(expr_any),
opt(tag("-")),
|i| tag_expr_end(i, s),
cut(tuple((opt(tag("-")), ws(expr_any), opt(tag("-")), |i| {
tag_expr_end(i, s)
}))),
));
let (i, (_, pws, expr, nws, _)) = p(i)?;
let (i, (_, (pws, expr, nws, _))) = p(i)?;
Ok((i, Node::Expr(Ws(pws.is_some(), nws.is_some()), expr)))
}
@ -677,12 +676,14 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple((
opt(tag("-")),
ws(tag("call")),
opt(tuple((ws(identifier), ws(tag("::"))))),
ws(identifier),
ws(arguments),
opt(tag("-")),
cut(tuple((
opt(tuple((ws(identifier), ws(tag("::"))))),
ws(identifier),
ws(arguments),
opt(tag("-")),
))),
));
let (i, (pws, _, scope, name, args, nws)) = p(i)?;
let (i, (pws, _, (scope, name, args, nws))) = p(i)?;
let scope = scope.map(|(scope, _)| scope);
Ok((
i,
@ -691,23 +692,19 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node<'_>> {
}
fn cond_if(i: &[u8]) -> IResult<&[u8], CondTest<'_>> {
let mut p = tuple((
let mut p = preceded(
ws(tag("if")),
opt(tuple((
ws(alt((tag("let"), tag("set")))),
ws(target),
ws(tag("=")),
cut(tuple((
opt(delimited(
ws(alt((tag("let"), tag("set")))),
ws(target),
ws(tag("=")),
)),
ws(expr_any),
))),
ws(expr_any),
));
let (i, (_, dest, expr)) = p(i)?;
Ok((
i,
CondTest {
target: dest.map(|(_, target, _)| target),
expr,
},
))
);
let (i, (target, expr)) = p(i)?;
Ok((i, CondTest { target, expr }))
}
fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>> {
@ -715,12 +712,14 @@ fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>>
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("else")),
opt(cond_if),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
cut(tuple((
opt(cond_if),
opt(tag("-")),
|i| tag_block_end(i, s),
cut(|i| parse_template(i, s)),
))),
));
let (i, (_, pws, _, cond, nws, _, block)) = p(i)?;
let (i, (_, pws, _, (cond, nws, _, block))) = p(i)?;
Ok((i, (Ws(pws.is_some(), nws.is_some()), cond, block)))
}
@ -728,16 +727,22 @@ fn block_if<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple((
opt(tag("-")),
cond_if,
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
many0(|i| cond_block(i, s)),
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endif")),
opt(tag("-")),
cut(tuple((
opt(tag("-")),
|i| tag_block_end(i, s),
cut(tuple((
|i| parse_template(i, s),
many0(|i| cond_block(i, s)),
cut(tuple((
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endif")),
opt(tag("-")),
))),
))),
))),
));
let (i, (pws1, cond, nws1, _, block, elifs, _, pws2, _, nws2)) = p(i)?;
let (i, (pws1, cond, (nws1, _, (block, elifs, (_, pws2, _, nws2))))) = p(i)?;
let mut res = vec![(Ws(pws1.is_some(), nws1.is_some()), Some(cond), block)];
res.extend(elifs);
@ -749,11 +754,13 @@ fn match_else_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Whe
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("else")),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
cut(tuple((
opt(tag("-")),
|i| tag_block_end(i, s),
cut(|i| parse_template(i, s)),
))),
));
let (i, (_, pws, _, nws, _, block)) = p(i)?;
let (i, (_, pws, _, (nws, _, block))) = p(i)?;
Ok((
i,
(Ws(pws.is_some(), nws.is_some()), Target::Name("_"), block),
@ -765,12 +772,14 @@ fn when_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], When<'a>>
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("when")),
ws(target),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
cut(tuple((
ws(target),
opt(tag("-")),
|i| tag_block_end(i, s),
cut(|i| parse_template(i, s)),
))),
));
let (i, (_, pws, _, target, nws, _, block)) = p(i)?;
let (i, (_, pws, _, (target, nws, _, block))) = p(i)?;
Ok((i, (Ws(pws.is_some(), nws.is_some()), target, block)))
}
@ -778,18 +787,26 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>
let mut p = tuple((
opt(tag("-")),
ws(tag("match")),
ws(expr_any),
opt(tag("-")),
|i| tag_block_end(i, s),
opt(|i| take_content(i, s)),
many1(|i| when_block(i, s)),
opt(|i| match_else_block(i, s)),
ws(|i| tag_block_start(i, s)),
opt(tag("-")),
ws(tag("endmatch")),
opt(tag("-")),
cut(tuple((
ws(expr_any),
opt(tag("-")),
|i| tag_block_end(i, s),
cut(tuple((
opt(|i| take_content(i, s)),
many1(|i| when_block(i, s)),
cut(tuple((
opt(|i| match_else_block(i, s)),
cut(tuple((
ws(|i| tag_block_start(i, s)),
opt(tag("-")),
ws(tag("endmatch")),
opt(tag("-")),
))),
))),
))),
))),
));
let (i, (pws1, _, expr, nws1, _, inter, arms, else_arm, _, pws2, _, nws2)) = p(i)?;
let (i, (pws1, _, (expr, nws1, _, (inter, arms, (else_arm, (_, pws2, _, nws2)))))) = p(i)?;
let mut arms = arms;
if let Some(arm) = else_arm {
@ -828,11 +845,13 @@ fn block_let(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple((
opt(tag("-")),
ws(alt((tag("let"), tag("set")))),
ws(target),
opt(tuple((ws(tag("=")), ws(expr_any)))),
opt(tag("-")),
cut(tuple((
ws(target),
opt(tuple((ws(tag("=")), ws(expr_any)))),
opt(tag("-")),
))),
));
let (i, (pws, _, var, val, nws)) = p(i)?;
let (i, (pws, _, (var, val, nws))) = p(i)?;
Ok((
i,
@ -848,18 +867,26 @@ fn block_for<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>>
let mut p = tuple((
opt(tag("-")),
ws(tag("for")),
ws(target),
ws(tag("in")),
ws(expr_any),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endfor")),
opt(tag("-")),
cut(tuple((
ws(target),
ws(tag("in")),
cut(tuple((
ws(expr_any),
opt(tag("-")),
|i| tag_block_end(i, s),
cut(tuple((
|i| parse_template(i, s),
cut(tuple((
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endfor")),
opt(tag("-")),
))),
))),
))),
))),
));
let (i, (pws1, _, var, _, iter, nws1, _, block, _, pws2, _, nws2)) = p(i)?;
let (i, (pws1, _, (var, _, (iter, nws1, _, (block, (_, pws2, _, nws2)))))) = p(i)?;
Ok((
i,
Node::Loop(
@ -881,21 +908,22 @@ fn block_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>
let mut start = tuple((
opt(tag("-")),
ws(tag("block")),
ws(identifier),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
cut(tuple((ws(identifier), opt(tag("-")), |i| {
tag_block_end(i, s)
}))),
));
let (i, (pws1, _, name, nws1, _, contents)) = start(i)?;
let (i, (pws1, _, (name, nws1, _))) = start(i)?;
let mut end = tuple((
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endblock")),
opt(ws(tag(name))),
opt(tag("-")),
));
let (i, (_, pws2, _, _, nws2)) = end(i)?;
let mut end = cut(tuple((
|i| parse_template(i, s),
cut(tuple((
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endblock")),
cut(tuple((opt(ws(tag(name))), opt(tag("-"))))),
))),
)));
let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?;
Ok((
i,
@ -912,10 +940,9 @@ fn block_include(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple((
opt(tag("-")),
ws(tag("include")),
ws(expr_str_lit),
opt(tag("-")),
cut(pair(ws(expr_str_lit), opt(tag("-")))),
));
let (i, (pws, _, name, nws)) = p(i)?;
let (i, (pws, _, (name, nws))) = p(i)?;
Ok((
i,
Node::Include(
@ -932,12 +959,13 @@ fn block_import(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple((
opt(tag("-")),
ws(tag("import")),
ws(expr_str_lit),
ws(tag("as")),
ws(identifier),
opt(tag("-")),
cut(tuple((
ws(expr_str_lit),
ws(tag("as")),
cut(pair(ws(identifier), opt(tag("-")))),
))),
));
let (i, (pws, _, name, _, scope, nws)) = p(i)?;
let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?;
Ok((
i,
Node::Import(
@ -955,18 +983,24 @@ fn block_macro<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>
let mut p = tuple((
opt(tag("-")),
ws(tag("macro")),
ws(identifier),
ws(parameters),
opt(tag("-")),
|i| tag_block_end(i, s),
|i| parse_template(i, s),
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endmacro")),
opt(tag("-")),
cut(tuple((
ws(identifier),
ws(parameters),
opt(tag("-")),
|i| tag_block_end(i, s),
cut(tuple((
|i| parse_template(i, s),
cut(tuple((
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endmacro")),
opt(tag("-")),
))),
))),
))),
));
let (i, (pws1, _, name, params, nws1, _, contents, _, pws2, _, nws2)) = p(i)?;
let (i, (pws1, _, (name, params, nws1, _, (contents, (_, pws2, _, nws2))))) = p(i)?;
if name == "super" {
panic!("invalid macro name 'super'");
}
@ -989,16 +1023,18 @@ fn block_raw<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>>
let mut p = tuple((
opt(tag("-")),
ws(tag("raw")),
opt(tag("-")),
|i| tag_block_end(i, s),
take_until("{% endraw %}"),
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endraw")),
opt(tag("-")),
cut(tuple((
opt(tag("-")),
|i| tag_block_end(i, s),
take_until("{% endraw %}"),
|i| tag_block_start(i, s),
opt(tag("-")),
ws(tag("endraw")),
opt(tag("-")),
))),
));
let (i, (pws1, _, nws1, _, contents, _, pws2, _, nws2)) = p(i)?;
let (i, (pws1, _, (nws1, _, contents, _, pws2, _, nws2))) = p(i)?;
let str_contents = str::from_utf8(contents).unwrap();
Ok((
i,
@ -1026,7 +1062,7 @@ fn block_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>>
|i| block_macro(i, s),
|i| block_raw(i, s),
)),
|i| tag_block_end(i, s),
cut(|i| tag_block_end(i, s)),
));
let (i, (_, contents, _)) = p(i)?;
Ok((i, contents))
@ -1053,11 +1089,13 @@ fn block_comment_body<'a>(mut i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8
fn block_comment<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple((
|i| tag_comment_start(i, s),
opt(tag("-")),
|i| block_comment_body(i, s),
|i| tag_comment_end(i, s),
cut(tuple((
opt(tag("-")),
|i| block_comment_body(i, s),
|i| tag_comment_end(i, s),
))),
));
let (i, (_, pws, tail, _)) = p(i)?;
let (i, (_, (pws, tail, _))) = p(i)?;
Ok((i, Node::Comment(Ws(pws.is_some(), tail.ends_with(b"-")))))
}
@ -1099,12 +1137,29 @@ pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'a>) -> Result<Vec<Node<'a>>,
Ok(res)
}
}
Err(nom::Err::Error(err)) => {
Err(format!("problems parsing template source: {:?}", err).into())
}
Err(nom::Err::Failure(err)) => {
Err(format!("problems parsing template source: {:?}", err).into())
Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
let nom::error::Error { input, .. } = err;
let offset = src.len() - input.len();
let (source_before, source_after) = src.split_at(offset);
let source_after = match source_after.char_indices().enumerate().take(41).last() {
Some((40, (i, _))) => format!("{:?}...", &source_after[..i]),
_ => format!("{:?}", source_after),
};
let (row, last_line) = source_before.lines().enumerate().last().unwrap();
let column = last_line.chars().count();
let msg = format!(
"problems parsing template source at row {}, column {} near:\n{}",
row + 1,
column,
source_after,
);
Err(msg.into())
}
Err(nom::Err::Incomplete(_)) => Err("parsing incomplete".into()),
}
}

View File

@ -0,0 +1,11 @@
use askama::Template;
#[derive(Template)]
#[template(
source = "{%for i in 1..=10%}{{i}}{%endfo%}\n1234567890123456789012345678901234567890",
ext = "txt"
)]
struct MyTemplate;
fn main() {
}

View File

@ -0,0 +1,8 @@
error: problems parsing template source at row 1, column 26 near:
"endfo%}\n12345678901234567890123456789012"...
--> $DIR/typo_in_keyword.rs:3:10
|
3 | #[derive(Template)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)