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::branch::alt;
use nom::bytes::complete::{escaped, is_not, tag, take_until}; use nom::bytes::complete::{escaped, is_not, tag, take_until};
use nom::character::complete::{anychar, char, digit1}; 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::error::{Error, ParseError};
use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1}; use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, terminated, tuple}; 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 mut targets = vec![first_target];
let (i, _) = tuple(( let (i, _) = cut(tuple((
fold_many0(preceded(ws(tag(",")), target), (), |_, target| { fold_many0(preceded(ws(tag(",")), target), (), |_, target| {
targets.push(target); targets.push(target);
}), }),
opt(ws(tag(","))), opt(ws(tag(","))),
ws(tag(")")), ws(cut(tag(")"))),
))(i)?; )))(i)?;
return Ok((i, Target::Tuple(Vec::new(), targets))); return Ok((i, Target::Tuple(Vec::new(), targets)));
} }
@ -408,8 +408,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
let (i, targets) = alt(( let (i, targets) = alt((
map(tag(")"), |_| Vec::new()), map(tag(")"), |_| Vec::new()),
terminated( terminated(
separated_list1(ws(tag(",")), target), cut(separated_list1(ws(tag(",")), target)),
pair(opt(ws(tag(","))), ws(tag(")"))), pair(opt(ws(tag(","))), ws(cut(tag(")")))),
), ),
))(i)?; ))(i)?;
return Ok((i, Target::Tuple(path, targets))); return Ok((i, Target::Tuple(path, targets)));
@ -420,8 +420,8 @@ fn target(i: &[u8]) -> IResult<&[u8], Target<'_>> {
let (i, targets) = alt(( let (i, targets) = alt((
map(tag("}"), |_| Vec::new()), map(tag("}"), |_| Vec::new()),
terminated( terminated(
separated_list1(ws(tag(",")), named_target), cut(separated_list1(ws(tag(",")), named_target)),
pair(opt(ws(tag(","))), ws(tag("}"))), pair(opt(ws(tag(","))), ws(cut(tag("}")))),
), ),
))(i)?; ))(i)?;
return Ok((i, Target::Struct(path, targets))); 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>> { fn expr_node<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple(( let mut p = tuple((
|i| tag_expr_start(i, s), |i| tag_expr_start(i, s),
opt(tag("-")), cut(tuple((opt(tag("-")), ws(expr_any), opt(tag("-")), |i| {
ws(expr_any), tag_expr_end(i, s)
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))) 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("call")), ws(tag("call")),
opt(tuple((ws(identifier), ws(tag("::"))))), cut(tuple((
ws(identifier), opt(tuple((ws(identifier), ws(tag("::"))))),
ws(arguments), ws(identifier),
opt(tag("-")), 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); let scope = scope.map(|(scope, _)| scope);
Ok(( Ok((
i, i,
@ -691,23 +692,19 @@ fn block_call(i: &[u8]) -> IResult<&[u8], Node<'_>> {
} }
fn cond_if(i: &[u8]) -> IResult<&[u8], CondTest<'_>> { fn cond_if(i: &[u8]) -> IResult<&[u8], CondTest<'_>> {
let mut p = tuple(( let mut p = preceded(
ws(tag("if")), ws(tag("if")),
opt(tuple(( cut(tuple((
ws(alt((tag("let"), tag("set")))), opt(delimited(
ws(target), ws(alt((tag("let"), tag("set")))),
ws(tag("=")), ws(target),
ws(tag("=")),
)),
ws(expr_any),
))), ))),
ws(expr_any), );
)); let (i, (target, expr)) = p(i)?;
let (i, (_, dest, expr)) = p(i)?; Ok((i, CondTest { target, expr }))
Ok((
i,
CondTest {
target: dest.map(|(_, target, _)| target),
expr,
},
))
} }
fn cond_block<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Cond<'a>> { 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), |i| tag_block_start(i, s),
opt(tag("-")), opt(tag("-")),
ws(tag("else")), ws(tag("else")),
opt(cond_if), cut(tuple((
opt(tag("-")), opt(cond_if),
|i| tag_block_end(i, s), opt(tag("-")),
|i| parse_template(i, s), |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))) 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
cond_if, cond_if,
opt(tag("-")), cut(tuple((
|i| tag_block_end(i, s), opt(tag("-")),
|i| parse_template(i, s), |i| tag_block_end(i, s),
many0(|i| cond_block(i, s)), cut(tuple((
|i| tag_block_start(i, s), |i| parse_template(i, s),
opt(tag("-")), many0(|i| cond_block(i, s)),
ws(tag("endif")), cut(tuple((
opt(tag("-")), |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)]; let mut res = vec![(Ws(pws1.is_some(), nws1.is_some()), Some(cond), block)];
res.extend(elifs); 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), |i| tag_block_start(i, s),
opt(tag("-")), opt(tag("-")),
ws(tag("else")), ws(tag("else")),
opt(tag("-")), cut(tuple((
|i| tag_block_end(i, s), opt(tag("-")),
|i| parse_template(i, s), |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(( Ok((
i, i,
(Ws(pws.is_some(), nws.is_some()), Target::Name("_"), block), (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), |i| tag_block_start(i, s),
opt(tag("-")), opt(tag("-")),
ws(tag("when")), ws(tag("when")),
ws(target), cut(tuple((
opt(tag("-")), ws(target),
|i| tag_block_end(i, s), opt(tag("-")),
|i| parse_template(i, s), |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))) 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("match")), ws(tag("match")),
ws(expr_any), cut(tuple((
opt(tag("-")), ws(expr_any),
|i| tag_block_end(i, s), opt(tag("-")),
opt(|i| take_content(i, s)), |i| tag_block_end(i, s),
many1(|i| when_block(i, s)), cut(tuple((
opt(|i| match_else_block(i, s)), opt(|i| take_content(i, s)),
ws(|i| tag_block_start(i, s)), many1(|i| when_block(i, s)),
opt(tag("-")), cut(tuple((
ws(tag("endmatch")), opt(|i| match_else_block(i, s)),
opt(tag("-")), 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; let mut arms = arms;
if let Some(arm) = else_arm { if let Some(arm) = else_arm {
@ -828,11 +845,13 @@ fn block_let(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(alt((tag("let"), tag("set")))), ws(alt((tag("let"), tag("set")))),
ws(target), cut(tuple((
opt(tuple((ws(tag("=")), ws(expr_any)))), ws(target),
opt(tag("-")), 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(( Ok((
i, 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("for")), ws(tag("for")),
ws(target), cut(tuple((
ws(tag("in")), ws(target),
ws(expr_any), ws(tag("in")),
opt(tag("-")), cut(tuple((
|i| tag_block_end(i, s), ws(expr_any),
|i| parse_template(i, s), opt(tag("-")),
|i| tag_block_start(i, s), |i| tag_block_end(i, s),
opt(tag("-")), cut(tuple((
ws(tag("endfor")), |i| parse_template(i, s),
opt(tag("-")), 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(( Ok((
i, i,
Node::Loop( 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(( let mut start = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("block")), ws(tag("block")),
ws(identifier), cut(tuple((ws(identifier), opt(tag("-")), |i| {
opt(tag("-")), tag_block_end(i, s)
|i| tag_block_end(i, s), }))),
|i| parse_template(i, s),
)); ));
let (i, (pws1, _, name, nws1, _, contents)) = start(i)?; let (i, (pws1, _, (name, nws1, _))) = start(i)?;
let mut end = tuple(( let mut end = cut(tuple((
|i| tag_block_start(i, s), |i| parse_template(i, s),
opt(tag("-")), cut(tuple((
ws(tag("endblock")), |i| tag_block_start(i, s),
opt(ws(tag(name))), opt(tag("-")),
opt(tag("-")), ws(tag("endblock")),
)); cut(tuple((opt(ws(tag(name))), opt(tag("-"))))),
let (i, (_, pws2, _, _, nws2)) = end(i)?; ))),
)));
let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?;
Ok(( Ok((
i, i,
@ -912,10 +940,9 @@ fn block_include(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("include")), ws(tag("include")),
ws(expr_str_lit), cut(pair(ws(expr_str_lit), opt(tag("-")))),
opt(tag("-")),
)); ));
let (i, (pws, _, name, nws)) = p(i)?; let (i, (pws, _, (name, nws))) = p(i)?;
Ok(( Ok((
i, i,
Node::Include( Node::Include(
@ -932,12 +959,13 @@ fn block_import(i: &[u8]) -> IResult<&[u8], Node<'_>> {
let mut p = tuple(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("import")), ws(tag("import")),
ws(expr_str_lit), cut(tuple((
ws(tag("as")), ws(expr_str_lit),
ws(identifier), ws(tag("as")),
opt(tag("-")), cut(pair(ws(identifier), opt(tag("-")))),
))),
)); ));
let (i, (pws, _, name, _, scope, nws)) = p(i)?; let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?;
Ok(( Ok((
i, i,
Node::Import( 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("macro")), ws(tag("macro")),
ws(identifier), cut(tuple((
ws(parameters), ws(identifier),
opt(tag("-")), ws(parameters),
|i| tag_block_end(i, s), opt(tag("-")),
|i| parse_template(i, s), |i| tag_block_end(i, s),
|i| tag_block_start(i, s), cut(tuple((
opt(tag("-")), |i| parse_template(i, s),
ws(tag("endmacro")), cut(tuple((
opt(tag("-")), |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" { if name == "super" {
panic!("invalid macro 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(( let mut p = tuple((
opt(tag("-")), opt(tag("-")),
ws(tag("raw")), ws(tag("raw")),
opt(tag("-")), cut(tuple((
|i| tag_block_end(i, s), opt(tag("-")),
take_until("{% endraw %}"), |i| tag_block_end(i, s),
|i| tag_block_start(i, s), take_until("{% endraw %}"),
opt(tag("-")), |i| tag_block_start(i, s),
ws(tag("endraw")), opt(tag("-")),
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(); let str_contents = str::from_utf8(contents).unwrap();
Ok(( Ok((
i, 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_macro(i, s),
|i| block_raw(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)?; let (i, (_, contents, _)) = p(i)?;
Ok((i, contents)) 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>> { fn block_comment<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a>> {
let mut p = tuple(( let mut p = tuple((
|i| tag_comment_start(i, s), |i| tag_comment_start(i, s),
opt(tag("-")), cut(tuple((
|i| block_comment_body(i, s), opt(tag("-")),
|i| tag_comment_end(i, s), |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"-"))))) 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) Ok(res)
} }
} }
Err(nom::Err::Error(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;
Err(nom::Err::Failure(err)) => { let offset = src.len() - input.len();
Err(format!("problems parsing template source: {:?}", err).into()) 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()), 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)