mirror of
https://github.com/askama-rs/askama.git
synced 2025-10-02 07:20:55 +00:00
refactor(parser): Migrate to Winnow 0.3
This commit is contained in:
parent
f6acf786b0
commit
a12a667871
@ -22,8 +22,8 @@ config = ["dep:serde"]
|
||||
|
||||
[dependencies]
|
||||
memchr = "2"
|
||||
nom = { version = "7", default-features = false, features = ["alloc"] }
|
||||
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||
winnow = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5"
|
||||
|
@ -1,14 +1,14 @@
|
||||
use std::collections::HashSet;
|
||||
use std::str;
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{tag, take_till};
|
||||
use nom::character::complete::{char, digit1};
|
||||
use nom::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value};
|
||||
use nom::error::ErrorKind;
|
||||
use nom::error_position;
|
||||
use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
|
||||
use nom::sequence::{pair, preceded, terminated, tuple};
|
||||
use winnow::branch::alt;
|
||||
use winnow::bytes::complete::{tag, take_till};
|
||||
use winnow::character::complete::{char, digit1};
|
||||
use winnow::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value};
|
||||
use winnow::error::ErrorKind;
|
||||
use winnow::multi::{fold_many0, many0, separated_list0, separated_list1};
|
||||
use winnow::sequence::{pair, preceded, terminated, tuple};
|
||||
use winnow::{Parser, error_position};
|
||||
|
||||
use crate::{
|
||||
CharLit, ErrorContext, Level, Num, ParseResult, PathOrIdentifier, StrLit, WithSpan, char_lit,
|
||||
@ -21,7 +21,9 @@ macro_rules! expr_prec_layer {
|
||||
let (_, level) = level.nest(i)?;
|
||||
let start = i;
|
||||
let (i, left) = Self::$inner(i, level)?;
|
||||
let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level)))(i)?;
|
||||
let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level)))
|
||||
.map(|v: Vec<_>| v)
|
||||
.parse_next(i)?;
|
||||
Ok((
|
||||
i,
|
||||
right.into_iter().fold(left, |left, (op, right)| {
|
||||
@ -102,7 +104,7 @@ impl<'a> Expr<'a> {
|
||||
move |i| Self::parse(i, level),
|
||||
))(i)?;
|
||||
if has_named_arguments && !matches!(*expr, Self::NamedArgument(_, _)) {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"named arguments must always be passed last",
|
||||
start,
|
||||
)))
|
||||
@ -138,7 +140,7 @@ impl<'a> Expr<'a> {
|
||||
WithSpan::new(Self::NamedArgument(argument, Box::new(value)), start),
|
||||
))
|
||||
} else {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("named argument `{argument}` was passed more than once"),
|
||||
start,
|
||||
)))
|
||||
@ -204,12 +206,12 @@ impl<'a> Expr<'a> {
|
||||
if crate::PRIMITIVE_TYPES.contains(&target) {
|
||||
return Ok((i, WithSpan::new(Self::As(Box::new(lhs), target), start)));
|
||||
} else if target.is_empty() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`as` operator expects the name of a primitive type on its right-hand side",
|
||||
before_keyword.trim_start(),
|
||||
)));
|
||||
} else {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!(
|
||||
"`as` operator expects the name of a primitive type on its right-hand \
|
||||
side, found `{target}`"
|
||||
@ -224,7 +226,7 @@ impl<'a> Expr<'a> {
|
||||
let (i, rhs) = opt(terminated(opt(keyword("not")), ws(keyword("defined"))))(i)?;
|
||||
let ctor = match rhs {
|
||||
None => {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"expected `defined` or `not defined` after `is`",
|
||||
// We use `start` to show the whole `var is` thing instead of the current token.
|
||||
start,
|
||||
@ -236,13 +238,13 @@ impl<'a> Expr<'a> {
|
||||
let var_name = match *lhs {
|
||||
Self::Var(var_name) => var_name,
|
||||
Self::Attr(_, _) => {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`is defined` operator can only be used on variables, not on their fields",
|
||||
start,
|
||||
)));
|
||||
}
|
||||
_ => {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`is defined` operator can only be used on variables",
|
||||
start,
|
||||
)));
|
||||
@ -269,7 +271,7 @@ impl<'a> Expr<'a> {
|
||||
let (_, nested) = level.nest(i)?;
|
||||
let start = i;
|
||||
let (i, (ops, mut expr)) = pair(
|
||||
many0(ws(alt((tag("!"), tag("-"), tag("*"), tag("&"))))),
|
||||
many0(ws(alt((tag("!"), tag("-"), tag("*"), tag("&"))))).map(|v: Vec<_>| v),
|
||||
|i| Suffix::parse(i, nested),
|
||||
)(i)?;
|
||||
|
||||
@ -404,7 +406,7 @@ fn token_xor(i: &str) -> ParseResult<'_> {
|
||||
if good {
|
||||
Ok((i, "^"))
|
||||
} else {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"the binary XOR operator is called `xor` in rinja",
|
||||
i,
|
||||
)))
|
||||
@ -419,7 +421,7 @@ fn token_bitand(i: &str) -> ParseResult<'_> {
|
||||
if good {
|
||||
Ok((i, "&"))
|
||||
} else {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"the binary AND operator is called `bitand` in rinja",
|
||||
i,
|
||||
)))
|
||||
@ -464,7 +466,7 @@ impl<'a> Suffix<'a> {
|
||||
Some(Self::MacroCall(args)) => match expr.inner {
|
||||
Expr::Path(path) => expr = WithSpan::new(Expr::RustMacro(path, args), i),
|
||||
Expr::Var(name) => expr = WithSpan::new(Expr::RustMacro(vec![name], args), i),
|
||||
_ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
|
||||
_ => return Err(winnow::Err::Cut(error_position!(i, ErrorKind::Tag))),
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
|
@ -10,14 +10,15 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, str};
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{escaped, is_not, tag, take_till, take_while_m_n};
|
||||
use nom::character::complete::{anychar, char, one_of, satisfy};
|
||||
use nom::combinator::{consumed, cut, fail, map, not, opt, recognize, value};
|
||||
use nom::error::{ErrorKind, FromExternalError};
|
||||
use nom::multi::{many0_count, many1};
|
||||
use nom::sequence::{delimited, pair, preceded, tuple};
|
||||
use nom::{AsChar, InputTakeAtPosition};
|
||||
use winnow::Parser;
|
||||
use winnow::branch::alt;
|
||||
use winnow::bytes::complete::{escaped, is_not, tag, take_till, take_while_m_n, take_while1};
|
||||
use winnow::character::complete::{anychar, char, one_of, satisfy};
|
||||
use winnow::combinator::{consumed, cut, fail, map, not, opt, recognize, value};
|
||||
use winnow::error::{ErrorKind, FromExternalError};
|
||||
use winnow::multi::{many0_count, many1};
|
||||
use winnow::sequence::{delimited, pair, preceded, tuple};
|
||||
use winnow::stream::AsChar;
|
||||
|
||||
pub mod expr;
|
||||
pub use expr::{Expr, Filter};
|
||||
@ -114,10 +115,10 @@ impl<'a> Ast<'a> {
|
||||
) -> Result<Self, ParseError> {
|
||||
match Node::parse_template(src, &State::new(syntax)) {
|
||||
Ok(("", nodes)) => Ok(Self { nodes }),
|
||||
Ok(_) | Err(nom::Err::Incomplete(_)) => unreachable!(),
|
||||
Ok(_) | Err(winnow::Err::Incomplete(_)) => unreachable!(),
|
||||
Err(
|
||||
nom::Err::Error(ErrorContext { input, message, .. })
|
||||
| nom::Err::Failure(ErrorContext { input, message, .. }),
|
||||
winnow::Err::Backtrack(ErrorContext { input, message, .. })
|
||||
| winnow::Err::Cut(ErrorContext { input, message, .. }),
|
||||
) => Err(ParseError {
|
||||
message,
|
||||
offset: src.len() - input.len(),
|
||||
@ -221,7 +222,7 @@ impl fmt::Display for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ParseErr<'a> = nom::Err<ErrorContext<'a>>;
|
||||
pub(crate) type ParseErr<'a> = winnow::Err<ErrorContext<'a>>;
|
||||
pub(crate) type ParseResult<'a, T = &'a str> = Result<(&'a str, T), ParseErr<'a>>;
|
||||
|
||||
/// This type is used to handle `nom` errors and in particular to add custom error messages.
|
||||
@ -248,7 +249,7 @@ impl<'a> ErrorContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> nom::error::ParseError<&'a str> for ErrorContext<'a> {
|
||||
impl<'a> winnow::error::ParseError<&'a str> for ErrorContext<'a> {
|
||||
fn from_error_kind(input: &'a str, _code: ErrorKind) -> Self {
|
||||
Self {
|
||||
input,
|
||||
@ -256,8 +257,8 @@ impl<'a> nom::error::ParseError<&'a str> for ErrorContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn append(_: &'a str, _: ErrorKind, other: Self) -> Self {
|
||||
other
|
||||
fn append(self, _: &'a str, _: ErrorKind) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,9 +271,9 @@ impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ErrorContext<'a>> for nom::Err<ErrorContext<'a>> {
|
||||
impl<'a> From<ErrorContext<'a>> for winnow::Err<ErrorContext<'a>> {
|
||||
fn from(cx: ErrorContext<'a>) -> Self {
|
||||
Self::Failure(cx)
|
||||
Self::Cut(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,7 +303,12 @@ fn skip_till<'a, 'b, O>(
|
||||
loop {
|
||||
i = match candidate_finder.split(i) {
|
||||
Some((_, j)) => j,
|
||||
None => return Err(nom::Err::Error(ErrorContext::new("`end` not found`", i))),
|
||||
None => {
|
||||
return Err(winnow::Err::Backtrack(ErrorContext::new(
|
||||
"`end` not found`",
|
||||
i,
|
||||
)));
|
||||
}
|
||||
};
|
||||
i = match next(i)? {
|
||||
(j, Some(lookahead)) => return Ok((i, (j, lookahead))),
|
||||
@ -321,17 +327,11 @@ fn keyword<'a>(k: &'a str) -> impl FnMut(&'a str) -> ParseResult<'a> {
|
||||
|
||||
fn identifier(input: &str) -> ParseResult<'_> {
|
||||
fn start(s: &str) -> ParseResult<'_> {
|
||||
s.split_at_position1_complete(
|
||||
|c| !(c.is_alpha() || c == '_' || c >= '\u{0080}'),
|
||||
nom::error::ErrorKind::Alpha,
|
||||
)
|
||||
take_while1(|c: char| c.is_alpha() || c == '_' || c >= '\u{0080}').parse_next(s)
|
||||
}
|
||||
|
||||
fn tail(s: &str) -> ParseResult<'_> {
|
||||
s.split_at_position1_complete(
|
||||
|c| !(c.is_alphanum() || c == '_' || c >= '\u{0080}'),
|
||||
nom::error::ErrorKind::Alpha,
|
||||
)
|
||||
take_while1(|c: char| c.is_alphanum() || c == '_' || c >= '\u{0080}').parse_next(s)
|
||||
}
|
||||
|
||||
recognize(pair(start, opt(tail)))(input)
|
||||
@ -362,7 +362,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
{
|
||||
Ok((i, value))
|
||||
} else {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("unknown {kind} suffix `{suffix}`"),
|
||||
start,
|
||||
)))
|
||||
@ -381,7 +381,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
))(i)?;
|
||||
match opt(separated_digits(base, false))(i)? {
|
||||
(i, Some(_)) => Ok((i, ())),
|
||||
(_, None) => Err(nom::Err::Failure(ErrorContext::new(
|
||||
(_, None) => Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("expected digits after `{kind}`"),
|
||||
start,
|
||||
))),
|
||||
@ -396,7 +396,7 @@ fn num_lit<'a>(start: &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
let (i, (kind, op)) = pair(one_of("eE"), opt(one_of("+-")))(i)?;
|
||||
match opt(separated_digits(10, op.is_none()))(i)? {
|
||||
(i, Some(_)) => Ok((i, ())),
|
||||
(_, None) => Err(nom::Err::Failure(ErrorContext::new(
|
||||
(_, None) => Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("expected decimal digits, `+` or `-` after exponent `{kind}`"),
|
||||
start,
|
||||
))),
|
||||
@ -521,13 +521,13 @@ fn char_lit(i: &str) -> Result<(&str, CharLit<'_>), ParseErr<'_>> {
|
||||
))(i)?;
|
||||
|
||||
let Some(s) = s else {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"empty character literal",
|
||||
start,
|
||||
)));
|
||||
};
|
||||
let Ok(("", c)) = Char::parse(s) else {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"invalid character",
|
||||
start,
|
||||
)));
|
||||
@ -557,10 +557,10 @@ fn char_lit(i: &str) -> Result<(&str, CharLit<'_>), ParseErr<'_>> {
|
||||
};
|
||||
|
||||
let Ok(nb) = u32::from_str_radix(nb, 16) else {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(err1, start)));
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(err1, start)));
|
||||
};
|
||||
if nb > max_value {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(err2, start)));
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(err2, start)));
|
||||
}
|
||||
|
||||
Ok((i, CharLit {
|
||||
@ -627,7 +627,7 @@ enum PathOrIdentifier<'a> {
|
||||
|
||||
fn path_or_identifier(i: &str) -> ParseResult<'_, PathOrIdentifier<'_>> {
|
||||
let root = ws(opt(tag("::")));
|
||||
let tail = opt(many1(preceded(ws(tag("::")), identifier)));
|
||||
let tail = opt(many1(preceded(ws(tag("::")), identifier)).map(|v: Vec<_>| v));
|
||||
|
||||
let (i, (root, start, rest)) = tuple((root, identifier, tail))(i)?;
|
||||
let rest = rest.as_deref().unwrap_or_default();
|
||||
@ -872,7 +872,7 @@ pub(crate) struct Level(u8);
|
||||
impl Level {
|
||||
fn nest(self, i: &str) -> ParseResult<'_, Level> {
|
||||
if self.0 >= Self::MAX_DEPTH {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"your template code is too deeply nested, or last expression is too complex",
|
||||
i,
|
||||
)));
|
||||
@ -899,7 +899,7 @@ fn filter<'a>(
|
||||
opt(|i| Expr::arguments(i, *level, false)),
|
||||
))(j)
|
||||
} else {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"the filter operator `|` must not be preceded by any whitespace characters\n\
|
||||
the binary OR operator is called `bitor` in rinja",
|
||||
i,
|
||||
|
@ -1,13 +1,14 @@
|
||||
use std::str;
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{tag, take_till};
|
||||
use nom::character::complete::{anychar, char};
|
||||
use nom::combinator::{
|
||||
use winnow::Parser;
|
||||
use winnow::branch::alt;
|
||||
use winnow::bytes::complete::{tag, take_till};
|
||||
use winnow::character::complete::{anychar, char};
|
||||
use winnow::combinator::{
|
||||
complete, consumed, cut, eof, fail, map, map_opt, not, opt, peek, recognize, value,
|
||||
};
|
||||
use nom::multi::{many0, separated_list0, separated_list1};
|
||||
use nom::sequence::{delimited, pair, preceded, tuple};
|
||||
use winnow::multi::{many0, separated_list0, separated_list1};
|
||||
use winnow::sequence::{delimited, pair, preceded, tuple};
|
||||
|
||||
use crate::memchr_splitter::{Splitter1, Splitter2, Splitter3};
|
||||
use crate::{
|
||||
@ -41,7 +42,7 @@ impl<'a> Node<'a> {
|
||||
let (i, result) = match complete(|i| Self::many(i, s))(i) {
|
||||
Ok((i, result)) => (i, result),
|
||||
Err(err) => {
|
||||
if let nom::Err::Error(err) | nom::Err::Failure(err) = &err {
|
||||
if let winnow::Err::Backtrack(err) | winnow::Err::Cut(err) = &err {
|
||||
if err.message.is_none() {
|
||||
opt(|i| unexpected_tag(i, s))(err.input)?;
|
||||
}
|
||||
@ -52,7 +53,7 @@ impl<'a> Node<'a> {
|
||||
let (i, _) = opt(|i| unexpected_tag(i, s))(i)?;
|
||||
let (i, is_eof) = opt(eof)(i)?;
|
||||
if is_eof.is_none() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"cannot parse entire template\n\
|
||||
you should never encounter this error\n\
|
||||
please report this error to <https://github.com/rinja-rs/rinja/issues>",
|
||||
@ -68,7 +69,9 @@ impl<'a> Node<'a> {
|
||||
map(|i| Comment::parse(i, s), Self::Comment),
|
||||
|i| Self::expr(i, s),
|
||||
|i| Self::parse(i, s),
|
||||
)))(i)
|
||||
)))
|
||||
.map(|v: Vec<_>| v)
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
|
||||
@ -127,7 +130,7 @@ impl<'a> Node<'a> {
|
||||
));
|
||||
let (j, (pws, _, nws)) = p(i)?;
|
||||
if !s.is_in_loop() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"you can only `break` inside a `for` loop",
|
||||
i,
|
||||
)));
|
||||
@ -143,7 +146,7 @@ impl<'a> Node<'a> {
|
||||
));
|
||||
let (j, (pws, _, nws)) = p(i)?;
|
||||
if !s.is_in_loop() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"you can only `continue` inside a `for` loop",
|
||||
i,
|
||||
)));
|
||||
@ -208,7 +211,7 @@ fn cut_node<'a, O>(
|
||||
let mut inner = cut(inner);
|
||||
move |i: &'a str| {
|
||||
let result = inner(i);
|
||||
if let Err(nom::Err::Failure(err) | nom::Err::Error(err)) = &result {
|
||||
if let Err(winnow::Err::Cut(err) | winnow::Err::Backtrack(err)) = &result {
|
||||
if err.message.is_none() {
|
||||
opt(|i| unexpected_raw_tag(kind, i))(err.input)?;
|
||||
}
|
||||
@ -240,7 +243,7 @@ fn unexpected_raw_tag<'a>(kind: Option<&'static str>, i: &'a str) -> ParseResult
|
||||
tag if tag.starts_with("end") => format!("unexpected closing tag `{tag}`"),
|
||||
tag => format!("unknown node `{tag}`"),
|
||||
};
|
||||
Err(nom::Err::Failure(ErrorContext::new(msg, i)))
|
||||
Err(winnow::Err::Cut(ErrorContext::new(msg, i)))
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -295,7 +298,7 @@ impl<'a> When<'a> {
|
||||
tuple((
|
||||
opt(Whitespace::parse),
|
||||
|i| s.tag_block_end(i),
|
||||
many0(value((), ws(|i| Comment::parse(i, s)))),
|
||||
many0(value((), ws(|i| Comment::parse(i, s)))).map(|()| ()),
|
||||
)),
|
||||
),
|
||||
))),
|
||||
@ -447,7 +450,7 @@ fn check_block_start<'a>(
|
||||
expected: &str,
|
||||
) -> ParseResult<'a> {
|
||||
if i.is_empty() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("expected `{expected}` to terminate `{node}` node, found nothing"),
|
||||
start,
|
||||
)));
|
||||
@ -599,7 +602,7 @@ impl<'a> Macro<'a> {
|
||||
));
|
||||
let (j, (pws1, _, (name, params, nws1, _))) = start(i)?;
|
||||
if is_rust_keyword(name) {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("'{name}' is not a valid name for a macro"),
|
||||
i,
|
||||
)));
|
||||
@ -669,7 +672,8 @@ impl<'a> FilterBlock<'a> {
|
||||
opt(|i| Expr::arguments(i, s.level.get(), false)),
|
||||
many0(|i| {
|
||||
filter(i, &mut level).map(|(j, (name, params))| (j, (name, params, i)))
|
||||
}),
|
||||
})
|
||||
.map(|v: Vec<_>| v),
|
||||
ws(|i| Ok((i, ()))),
|
||||
opt(Whitespace::parse),
|
||||
|i| s.tag_block_end(i),
|
||||
@ -829,8 +833,8 @@ impl<'a> Match<'a> {
|
||||
cut_node(
|
||||
Some("match"),
|
||||
tuple((
|
||||
ws(many0(ws(value((), |i| Comment::parse(i, s))))),
|
||||
many0(|i| When::when(i, s)),
|
||||
ws(many0(ws(value((), |i| Comment::parse(i, s))))).map(|()| ()),
|
||||
many0(|i| When::when(i, s)).map(|v: Vec<_>| v),
|
||||
cut_node(
|
||||
Some("match"),
|
||||
tuple((
|
||||
@ -859,7 +863,7 @@ impl<'a> Match<'a> {
|
||||
arms.push(arm);
|
||||
}
|
||||
if arms.is_empty() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`match` nodes must contain at least one `when` node and/or an `else` case",
|
||||
start,
|
||||
)));
|
||||
@ -955,7 +959,7 @@ fn check_end_name<'a>(
|
||||
return Ok((after, end_name));
|
||||
}
|
||||
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
match name.is_empty() && !end_name.is_empty() {
|
||||
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}`"),
|
||||
@ -1112,7 +1116,7 @@ impl<'a> If<'a> {
|
||||
Some("if"),
|
||||
tuple((
|
||||
|i| Node::many(i, s),
|
||||
many0(|i| Cond::parse(i, s)),
|
||||
many0(|i| Cond::parse(i, s)).map(|v: Vec<_>| v),
|
||||
cut_node(
|
||||
Some("if"),
|
||||
tuple((
|
||||
@ -1202,7 +1206,7 @@ impl<'a> Extends<'a> {
|
||||
))(i)?;
|
||||
match (pws, nws) {
|
||||
(None, None) => Ok((i, WithSpan::new(Self { path }, start))),
|
||||
(_, _) => Err(nom::Err::Failure(ErrorContext::new(
|
||||
(_, _) => Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"whitespace control is not allowed on `extends`",
|
||||
start,
|
||||
))),
|
||||
@ -1246,7 +1250,7 @@ impl<'a> Comment<'a> {
|
||||
Tag::Open => match depth.checked_add(1) {
|
||||
Some(new_depth) => depth = new_depth,
|
||||
None => {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"too deeply nested comments",
|
||||
start,
|
||||
)));
|
||||
@ -1269,7 +1273,7 @@ impl<'a> Comment<'a> {
|
||||
|
||||
let mut ws = Ws(None, None);
|
||||
if content.len() == 1 && matches!(content, "-" | "+" | "~") {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!(
|
||||
"ambiguous whitespace stripping\n\
|
||||
use `{}{content} {content}{}` to apply the same whitespace stripping on both \
|
||||
@ -1302,7 +1306,7 @@ fn end_node<'a, 'g: 'a>(
|
||||
if actual == expected {
|
||||
Ok((i, actual))
|
||||
} else if actual.starts_with("end") {
|
||||
Err(nom::Err::Failure(ErrorContext::new(
|
||||
Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"),
|
||||
start,
|
||||
)))
|
||||
|
@ -1,9 +1,10 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::{char, one_of};
|
||||
use nom::combinator::{consumed, map, map_res, opt};
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::{pair, preceded, tuple};
|
||||
use winnow::Parser;
|
||||
use winnow::branch::alt;
|
||||
use winnow::bytes::complete::tag;
|
||||
use winnow::character::complete::{char, one_of};
|
||||
use winnow::combinator::{consumed, map, map_res, opt};
|
||||
use winnow::multi::separated_list1;
|
||||
use winnow::sequence::{pair, preceded, tuple};
|
||||
|
||||
use crate::{
|
||||
CharLit, ErrorContext, Num, ParseErr, ParseResult, PathOrIdentifier, State, StrLit, WithSpan,
|
||||
@ -31,7 +32,8 @@ impl<'a> Target<'a> {
|
||||
/// Parses multiple targets with `or` separating them
|
||||
pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
|
||||
map(
|
||||
separated_list1(ws(tag("or")), |i| s.nest(i, |i| Self::parse_one(i, s))),
|
||||
separated_list1(ws(tag("or")), |i| s.nest(i, |i| Self::parse_one(i, s)))
|
||||
.map(|v: Vec<_>| v),
|
||||
|mut opts| match opts.len() {
|
||||
1 => opts.pop().unwrap(),
|
||||
_ => Self::OrChain(opts),
|
||||
@ -132,7 +134,7 @@ impl<'a> Target<'a> {
|
||||
if let Some(rest) = rest {
|
||||
let (_, chr) = ws(opt(one_of(",:")))(i)?;
|
||||
if let Some(chr) = chr {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!(
|
||||
"unexpected `{chr}` character after `..`\n\
|
||||
note that in a named struct, `..` must come last to ignore other members"
|
||||
@ -142,7 +144,7 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
if let Target::Rest(ref s) = rest.1 {
|
||||
if s.inner.is_some() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`@ ..` cannot be used in struct",
|
||||
s.span,
|
||||
)));
|
||||
@ -157,7 +159,7 @@ impl<'a> Target<'a> {
|
||||
)(init_i)?;
|
||||
|
||||
if src == "_" {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"cannot use placeholder `_` as source in named struct",
|
||||
init_i,
|
||||
)));
|
||||
@ -182,9 +184,9 @@ impl<'a> Target<'a> {
|
||||
fn verify_name<'a>(
|
||||
input: &'a str,
|
||||
name: &'a str,
|
||||
) -> Result<Target<'a>, nom::Err<ErrorContext<'a>>> {
|
||||
) -> Result<Target<'a>, winnow::Err<ErrorContext<'a>>> {
|
||||
match name {
|
||||
"self" | "writer" => Err(nom::Err::Failure(ErrorContext::new(
|
||||
"self" | "writer" => Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("cannot use `{name}` as a name"),
|
||||
input,
|
||||
))),
|
||||
@ -206,9 +208,9 @@ fn collect_targets<'a, T>(
|
||||
return Ok((i, (false, Vec::new())));
|
||||
}
|
||||
|
||||
let (i, targets) = opt(separated_list1(ws(char(',')), |i| one(i, s)))(i)?;
|
||||
let (i, targets) = opt(separated_list1(ws(char(',')), |i| one(i, s)).map(|v: Vec<_>| v))(i)?;
|
||||
let Some(targets) = targets else {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"expected comma separated list of members",
|
||||
i,
|
||||
)));
|
||||
@ -220,7 +222,7 @@ fn collect_targets<'a, T>(
|
||||
true => format!("expected member, or `{delim}` as terminator"),
|
||||
false => format!("expected `,` for more members, or `{delim}` as terminator"),
|
||||
};
|
||||
return Err(nom::Err::Failure(ErrorContext::new(msg, i)));
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(msg, i)));
|
||||
}
|
||||
|
||||
let singleton = !has_comma && targets.len() == 1;
|
||||
@ -237,13 +239,13 @@ fn only_one_rest_pattern<'a>(
|
||||
for target in &targets {
|
||||
if let Target::Rest(s) = target {
|
||||
if !allow_named_rest && s.inner.is_some() {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
"`@ ..` is only allowed in slice patterns",
|
||||
s.span,
|
||||
)));
|
||||
}
|
||||
if found_rest {
|
||||
return Err(nom::Err::Failure(ErrorContext::new(
|
||||
return Err(winnow::Err::Cut(ErrorContext::new(
|
||||
format!("`..` can only be used once per {type_kind} pattern"),
|
||||
s.span,
|
||||
)));
|
||||
|
Loading…
x
Reference in New Issue
Block a user