mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-27 13:00:57 +00:00
parser: use LocatingSlice<&str>
instead of &str
This will enable better span getting in subsequent PRs.
This commit is contained in:
parent
e9021aa1a5
commit
329771152d
@ -5,31 +5,34 @@ use winnow::ascii::digit1;
|
||||
use winnow::combinator::{
|
||||
alt, cut_err, empty, fail, not, opt, peek, preceded, repeat, separated, terminated,
|
||||
};
|
||||
use winnow::error::{ErrMode, ParserError as _};
|
||||
use winnow::token::{one_of, take_until};
|
||||
use winnow::stream::Stream;
|
||||
use winnow::token::{any, one_of, take, take_until};
|
||||
|
||||
use crate::node::CondTest;
|
||||
use crate::{
|
||||
CharLit, ErrorContext, HashSet, Level, Num, ParseResult, PathOrIdentifier, StrLit, StrPrefix,
|
||||
WithSpan, can_be_variable_name, char_lit, cut_error, filter, identifier, keyword,
|
||||
CharLit, ErrorContext, HashSet, InputStream, Level, Num, ParseResult, PathOrIdentifier, StrLit,
|
||||
StrPrefix, WithSpan, can_be_variable_name, char_lit, cut_error, filter, identifier, keyword,
|
||||
not_suffix_with_hash, num_lit, path_or_identifier, skip_ws0, skip_ws1, str_lit, ws,
|
||||
};
|
||||
|
||||
macro_rules! expr_prec_layer {
|
||||
( $name:ident, $inner:ident, $op:expr ) => {
|
||||
fn $name(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn $name(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
expr_prec_layer(i, level, Expr::$inner, |i: &mut _| $op.parse_next(i))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn expr_prec_layer<'a>(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
inner: fn(&mut &'a str, Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>>,
|
||||
op: fn(&mut &'a str) -> ParseResult<'a>,
|
||||
inner: fn(&mut InputStream<'a>, Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>>,
|
||||
op: fn(&mut InputStream<'a>) -> ParseResult<'a>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let mut expr = inner(i, level)?;
|
||||
|
||||
let mut level_guard = level.guard();
|
||||
@ -172,8 +175,11 @@ impl<'a> PathComponent<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = *i;
|
||||
pub(crate) fn parse(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = ***i;
|
||||
(
|
||||
identifier,
|
||||
opt((ws("::"), |i: &mut _| TyGenerics::args(i, level))),
|
||||
@ -248,12 +254,12 @@ pub struct BinOp<'a> {
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
pub(super) fn arguments(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, Vec<WithSpan<'a, Box<Self>>>> {
|
||||
let _level_guard = level.nest(i)?;
|
||||
let mut named_arguments = HashSet::default();
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
|
||||
preceded(
|
||||
ws('('),
|
||||
@ -286,7 +292,7 @@ impl<'a> Expr<'a> {
|
||||
}
|
||||
|
||||
fn named_argument(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
named_arguments: &mut HashSet<&'a str>,
|
||||
start: &'a str,
|
||||
@ -310,7 +316,7 @@ impl<'a> Expr<'a> {
|
||||
}
|
||||
|
||||
pub(super) fn parse(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
allow_underscore: bool,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
@ -361,13 +367,16 @@ impl<'a> Expr<'a> {
|
||||
expr_prec_layer!(or, and, "||");
|
||||
expr_prec_layer!(and, compare, "&&");
|
||||
|
||||
fn compare(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn compare(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let right = |i: &mut _| {
|
||||
let op = alt(("==", "!=", ">=", ">", "<=", "<"));
|
||||
(ws(op), |i: &mut _| Self::bor(i, level)).parse_next(i)
|
||||
};
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let expr = Self::bor(i, level)?;
|
||||
let Some((op, rhs)) = opt(right).parse_next(i)? else {
|
||||
return Ok(expr);
|
||||
@ -397,14 +406,17 @@ impl<'a> Expr<'a> {
|
||||
expr_prec_layer!(shifts, addsub, alt((">>", "<<")));
|
||||
expr_prec_layer!(addsub, concat, alt(("+", "-")));
|
||||
|
||||
fn concat(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn concat(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn concat_expr<'a>(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, Option<WithSpan<'a, Box<Expr<'a>>>>> {
|
||||
let ws1 = |i: &mut _| opt(skip_ws1).parse_next(i);
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let data = opt((ws1, '~', ws1, |i: &mut _| Expr::muldivmod(i, level))).parse_next(i)?;
|
||||
if let Some((t1, _, t2, expr)) = data {
|
||||
if t1.is_none() || t2.is_none() {
|
||||
@ -419,7 +431,7 @@ impl<'a> Expr<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let expr = Self::muldivmod(i, level)?;
|
||||
let expr2 = concat_expr(i, level)?;
|
||||
if let Some(expr2) = expr2 {
|
||||
@ -435,8 +447,11 @@ impl<'a> Expr<'a> {
|
||||
|
||||
expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%")));
|
||||
|
||||
fn is_as(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn is_as(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let lhs = Self::filtered(i, level)?;
|
||||
let before_keyword = *i;
|
||||
let rhs = opt(ws(identifier)).parse_next(i)?;
|
||||
@ -495,24 +510,30 @@ impl<'a> Expr<'a> {
|
||||
Ok(WithSpan::new(Box::new(ctor(var_name)), start, i))
|
||||
}
|
||||
|
||||
fn filtered(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn filtered(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let mut res = Self::prefix(i, level)?;
|
||||
|
||||
let mut level_guard = level.guard();
|
||||
let mut start = *i;
|
||||
let mut start = ***i;
|
||||
while let Some((mut filter, i_before)) =
|
||||
opt(ws((|i: &mut _| filter(i, level)).with_taken())).parse_next(i)?
|
||||
{
|
||||
level_guard.nest(i_before)?;
|
||||
filter.arguments.insert(0, res);
|
||||
res = WithSpan::new(Box::new(Self::Filter(filter)), start.trim_start(), i);
|
||||
start = *i;
|
||||
start = ***i;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn prefix(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn prefix(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
|
||||
// This is a rare place where we create recursion in the parsed AST
|
||||
// without recursing the parser call stack. However, this can lead
|
||||
@ -534,7 +555,10 @@ impl<'a> Expr<'a> {
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn single(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
fn single(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
alt((
|
||||
Self::num,
|
||||
Self::str,
|
||||
@ -546,8 +570,11 @@ impl<'a> Expr<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn group(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn group(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?;
|
||||
let Some(expr) = expr else {
|
||||
let _ = ')'.parse_next(i)?;
|
||||
@ -576,8 +603,11 @@ impl<'a> Expr<'a> {
|
||||
Ok(WithSpan::new(Box::new(Self::Tuple(exprs)), start, i))
|
||||
}
|
||||
|
||||
fn array(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn array(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let array = preceded(
|
||||
ws('['),
|
||||
cut_err(terminated(
|
||||
@ -597,10 +627,10 @@ impl<'a> Expr<'a> {
|
||||
}
|
||||
|
||||
fn path_var_bool(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let ret = match path_or_identifier(i, level)? {
|
||||
PathOrIdentifier::Path(v) => Box::new(Self::Path(v)),
|
||||
PathOrIdentifier::Identifier("true") => Box::new(Self::BoolLit(true)),
|
||||
@ -610,20 +640,20 @@ impl<'a> Expr<'a> {
|
||||
Ok(WithSpan::new(ret, start, i))
|
||||
}
|
||||
|
||||
fn str(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn str(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let s = str_lit.parse_next(i)?;
|
||||
Ok(WithSpan::new(Box::new(Self::StrLit(s)), start, i))
|
||||
}
|
||||
|
||||
fn num(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn num(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let (num, full) = num_lit.with_taken().parse_next(i)?;
|
||||
Ok(WithSpan::new(Box::new(Expr::NumLit(full, num)), start, i))
|
||||
}
|
||||
|
||||
fn char(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = *i;
|
||||
fn char(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||
let start = ***i;
|
||||
let c = char_lit.parse_next(i)?;
|
||||
Ok(WithSpan::new(Box::new(Self::CharLit(c)), start, i))
|
||||
}
|
||||
@ -661,21 +691,21 @@ impl<'a> Expr<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn token_xor<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
||||
fn token_xor<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> {
|
||||
let good = alt((keyword("xor").value(true), '^'.value(false))).parse_next(i)?;
|
||||
if good {
|
||||
Ok("^")
|
||||
} else {
|
||||
cut_error!("the binary XOR operator is called `xor` in askama", *i)
|
||||
cut_error!("the binary XOR operator is called `xor` in askama", ***i)
|
||||
}
|
||||
}
|
||||
|
||||
fn token_bitand<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
||||
fn token_bitand<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> {
|
||||
let good = alt((keyword("bitand").value(true), ('&', not('&')).value(false))).parse_next(i)?;
|
||||
if good {
|
||||
Ok("&")
|
||||
} else {
|
||||
cut_error!("the binary AND operator is called `bitand` in askama", *i)
|
||||
cut_error!("the binary AND operator is called `bitand` in askama", ***i)
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,7 +716,7 @@ pub struct Filter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Filter<'a> {
|
||||
pub(crate) fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
pub(crate) fn parse(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
let (name, arguments) = (
|
||||
ws(|i: &mut _| path_or_identifier(i, level)),
|
||||
opt(|i: &mut _| Expr::arguments(i, level)),
|
||||
@ -717,8 +747,11 @@ enum Suffix<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Suffix<'a> {
|
||||
fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
||||
let i_start = *i;
|
||||
fn parse(
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
||||
let i_start = ***i;
|
||||
let mut level_guard = level.guard();
|
||||
let mut expr = Expr::single(i, level)?;
|
||||
let mut right = alt((
|
||||
@ -728,9 +761,11 @@ impl<'a> Suffix<'a> {
|
||||
Self::r#try,
|
||||
Self::r#macro,
|
||||
));
|
||||
let start = *i;
|
||||
while let Some((suffix, i_before)) = opt(right.by_ref().with_taken()).parse_next(i)? {
|
||||
level_guard.nest(i_before)?;
|
||||
|
||||
let start = ***i;
|
||||
let mut i_before = i.checkpoint();
|
||||
while let Some(suffix) = opt(right.by_ref()).parse_next(i)? {
|
||||
level_guard.nest(i)?;
|
||||
match suffix {
|
||||
Self::AssociatedItem(associated_item) => {
|
||||
expr = WithSpan::new(
|
||||
@ -770,15 +805,17 @@ impl<'a> Suffix<'a> {
|
||||
expr = WithSpan::new(Box::new(Expr::RustMacro(vec![name], args)), start, i)
|
||||
}
|
||||
_ => {
|
||||
return Err(ErrMode::from_input(&i_before).cut());
|
||||
i.reset(&i_before);
|
||||
return fail(i);
|
||||
}
|
||||
},
|
||||
}
|
||||
i_before = i.checkpoint();
|
||||
}
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
fn r#macro(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||
fn r#macro(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Token {
|
||||
SomeOther,
|
||||
@ -803,8 +840,11 @@ impl<'a> Suffix<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn macro_arguments<'a>(i: &mut &'a str, open_token: Group) -> ParseResult<'a, Suffix<'a>> {
|
||||
let start = *i;
|
||||
fn macro_arguments<'a>(
|
||||
i: &mut InputStream<'a>,
|
||||
open_token: Group,
|
||||
) -> ParseResult<'a, Suffix<'a>> {
|
||||
let start = ***i;
|
||||
let mut open_list: Vec<Group> = vec![open_token];
|
||||
loop {
|
||||
let before = *i;
|
||||
@ -837,7 +877,7 @@ impl<'a> Suffix<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn token<'a>(i: &mut &'a str) -> ParseResult<'a, Token> {
|
||||
fn token<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Token> {
|
||||
// <https://doc.rust-lang.org/reference/tokens.html>
|
||||
let some_other = alt((
|
||||
// literals
|
||||
@ -858,8 +898,8 @@ impl<'a> Suffix<'a> {
|
||||
alt((open.map(Token::Open), close.map(Token::Close), some_other)).parse_next(i)
|
||||
}
|
||||
|
||||
fn line_comment<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn inner<'a>(i: &mut &'a str) -> ParseResult<'a, bool> {
|
||||
fn line_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> {
|
||||
let start = "//".parse_next(i)?;
|
||||
let is_doc_comment = alt((
|
||||
('/', not(peek('/'))).value(true),
|
||||
@ -882,8 +922,8 @@ impl<'a> Suffix<'a> {
|
||||
doc_comment_no_bare_cr(i, inner)
|
||||
}
|
||||
|
||||
fn block_comment<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn inner<'a>(i: &mut &'a str) -> ParseResult<'a, bool> {
|
||||
fn block_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> {
|
||||
let start = "/*".parse_next(i)?;
|
||||
let is_doc_comment = alt((
|
||||
('*', not(peek(one_of(['*', '/'])))).value(true),
|
||||
@ -916,7 +956,7 @@ impl<'a> Suffix<'a> {
|
||||
doc_comment_no_bare_cr(i, inner)
|
||||
}
|
||||
|
||||
fn identifier_or_prefixed_string<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn identifier_or_prefixed_string<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
// <https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str-raw.syntax>
|
||||
|
||||
let prefix = identifier.parse_next(i)?;
|
||||
@ -948,10 +988,12 @@ impl<'a> Suffix<'a> {
|
||||
if opt('"').parse_next(i)?.is_some() {
|
||||
// got a raw string
|
||||
|
||||
let Some((inner, j)) = i.split_once(&format!("\"{:#<hashes$}", "")) else {
|
||||
let delim = format!("\"{:#<hashes$}", "");
|
||||
let Some(inner) = opt(terminated(take_until(.., delim.as_str()), delim.as_str()))
|
||||
.parse_next(i)?
|
||||
else {
|
||||
return cut_error!("unterminated raw string", prefix);
|
||||
};
|
||||
*i = j;
|
||||
|
||||
if inner.split('\r').skip(1).any(|s| !s.starts_with('\n')) {
|
||||
return cut_error!(
|
||||
@ -1021,8 +1063,8 @@ impl<'a> Suffix<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn hash<'a>(i: &mut &'a str) -> ParseResult<'a, Token> {
|
||||
let start = *i;
|
||||
fn hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Token> {
|
||||
let start = ***i;
|
||||
'#'.parse_next(i)?;
|
||||
if opt('"').parse_next(i)?.is_some() {
|
||||
return cut_error!(
|
||||
@ -1033,7 +1075,7 @@ impl<'a> Suffix<'a> {
|
||||
Ok(Token::SomeOther)
|
||||
}
|
||||
|
||||
fn punctuation<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn punctuation<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
// <https://doc.rust-lang.org/reference/tokens.html#punctuation>
|
||||
// hash '#' omitted
|
||||
|
||||
@ -1045,29 +1087,39 @@ impl<'a> Suffix<'a> {
|
||||
];
|
||||
const THREE_CHARS: &[[u8; 3]] = &[*b"<<=", *b">>=", *b"...", *b"..="];
|
||||
|
||||
let three_chars = take(3usize).verify_map(|head: &str| {
|
||||
if let Ok(head) = head.as_bytes().try_into()
|
||||
&& THREE_CHARS.contains(head)
|
||||
{
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let two_chars = take(2usize).verify_map(|head: &str| {
|
||||
if let Ok(head) = head.as_bytes().try_into()
|
||||
&& TWO_CHARS.contains(head)
|
||||
{
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let one_char = any.verify_map(|head: char| {
|
||||
if let Ok(head) = head.try_into()
|
||||
&& ONE_CHAR.contains(&head)
|
||||
{
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
// need to check long to short
|
||||
*i = if let Some((head, tail)) = i.split_at_checked(3)
|
||||
&& let Ok(head) = head.as_bytes().try_into()
|
||||
&& THREE_CHARS.contains(head)
|
||||
{
|
||||
tail
|
||||
} else if let Some((head, tail)) = i.split_at_checked(2)
|
||||
&& let Ok(head) = head.as_bytes().try_into()
|
||||
&& TWO_CHARS.contains(&head)
|
||||
{
|
||||
tail
|
||||
} else if let Some((head, tail)) = i.split_at_checked(1)
|
||||
&& let [head] = head.as_bytes()
|
||||
&& ONE_CHAR.contains(head)
|
||||
{
|
||||
tail
|
||||
} else {
|
||||
return fail(i);
|
||||
};
|
||||
Ok(())
|
||||
alt((three_chars, two_chars, one_char)).parse_next(i)
|
||||
}
|
||||
|
||||
fn open<'a>(i: &mut &'a str) -> ParseResult<'a, Group> {
|
||||
fn open<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> {
|
||||
alt((
|
||||
'('.value(Group::Paren),
|
||||
'{'.value(Group::Brace),
|
||||
@ -1076,7 +1128,7 @@ impl<'a> Suffix<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn close<'a>(i: &mut &'a str) -> ParseResult<'a, Group> {
|
||||
fn close<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> {
|
||||
alt((
|
||||
')'.value(Group::Paren),
|
||||
'}'.value(Group::Brace),
|
||||
@ -1089,7 +1141,7 @@ impl<'a> Suffix<'a> {
|
||||
(|i: &mut _| macro_arguments(i, open_token)).parse_next(i)
|
||||
}
|
||||
|
||||
fn associated_item(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
fn associated_item(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
preceded(
|
||||
ws(('.', not('.'))),
|
||||
cut_err((
|
||||
@ -1113,7 +1165,7 @@ impl<'a> Suffix<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn index(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
fn index(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
preceded(
|
||||
ws('['),
|
||||
cut_err(terminated(
|
||||
@ -1125,7 +1177,7 @@ impl<'a> Suffix<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn call(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
fn call(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Self> {
|
||||
(opt(|i: &mut _| call_generics(i, level)), |i: &mut _| {
|
||||
Expr::arguments(i, level)
|
||||
})
|
||||
@ -1133,14 +1185,14 @@ impl<'a> Suffix<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn r#try(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||
fn r#try(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||
preceded(skip_ws0, '?').map(|_| Self::Try).parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_comment_no_bare_cr<'a>(
|
||||
i: &mut &'a str,
|
||||
inner: fn(i: &mut &'a str) -> ParseResult<'a, bool>,
|
||||
i: &mut InputStream<'a>,
|
||||
inner: fn(i: &mut InputStream<'a>) -> ParseResult<'a, bool>,
|
||||
) -> ParseResult<'a, ()> {
|
||||
let (is_doc_comment, comment) = inner.with_taken().parse_next(i)?;
|
||||
if is_doc_comment && comment.split('\r').skip(1).any(|s| !s.starts_with('\n')) {
|
||||
@ -1171,8 +1223,8 @@ pub struct TyGenerics<'a> {
|
||||
}
|
||||
|
||||
impl<'i> TyGenerics<'i> {
|
||||
fn parse(i: &mut &'i str, level: Level<'_>) -> ParseResult<'i, WithSpan<'i, Self>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'i>, level: Level<'_>) -> ParseResult<'i, WithSpan<'i, Self>> {
|
||||
let start = ***i;
|
||||
let (refs, path, args): (_, Vec<_>, _) = (
|
||||
repeat(0.., ws('&')),
|
||||
separated(1.., ws(identifier), "::"),
|
||||
@ -1202,7 +1254,7 @@ impl<'i> TyGenerics<'i> {
|
||||
}
|
||||
|
||||
fn args(
|
||||
i: &mut &'i str,
|
||||
i: &mut InputStream<'i>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||
ws('<').parse_next(i)?;
|
||||
@ -1219,7 +1271,7 @@ impl<'i> TyGenerics<'i> {
|
||||
}
|
||||
|
||||
pub(crate) fn call_generics<'i>(
|
||||
i: &mut &'i str,
|
||||
i: &mut InputStream<'i>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||
preceded(ws("::"), cut_err(|i: &mut _| TyGenerics::args(i, level))).parse_next(i)
|
||||
|
@ -21,12 +21,12 @@ use std::{fmt, str};
|
||||
use rustc_hash::FxBuildHasher;
|
||||
use winnow::ascii::take_escaped;
|
||||
use winnow::combinator::{
|
||||
alt, cut_err, delimited, empty, fail, not, opt, peek, preceded, repeat, terminated,
|
||||
alt, cond, cut_err, delimited, empty, fail, not, opt, peek, preceded, repeat, terminated,
|
||||
};
|
||||
use winnow::error::{ErrMode, FromExternalError};
|
||||
use winnow::stream::AsChar;
|
||||
use winnow::token::{any, none_of, one_of, take_while};
|
||||
use winnow::{ModalParser, Parser};
|
||||
use winnow::{LocatingSlice, ModalParser, ModalResult, Parser, Stateful};
|
||||
|
||||
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||
pub use crate::expr::{AssociatedItem, Expr, Filter, PathComponent, TyGenerics};
|
||||
@ -102,6 +102,8 @@ mod _parsed {
|
||||
|
||||
pub use _parsed::Parsed;
|
||||
|
||||
type InputStream<'a> = Stateful<LocatingSlice<&'a str>, ()>;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Ast<'a> {
|
||||
nodes: Vec<Box<Node<'a>>>,
|
||||
@ -111,7 +113,7 @@ impl<'a> Ast<'a> {
|
||||
/// If `file_path` is `None`, it means the `source` is an inline template. Therefore, if
|
||||
/// a parsing error occurs, we won't display the path as it wouldn't be useful.
|
||||
pub fn from_str(
|
||||
mut src: &'a str,
|
||||
src: &'a str,
|
||||
file_path: Option<Arc<Path>>,
|
||||
syntax: &Syntax<'_>,
|
||||
) -> Result<Self, ParseError> {
|
||||
@ -122,6 +124,10 @@ impl<'a> Ast<'a> {
|
||||
loop_depth: Cell::new(0),
|
||||
level: Level(&level),
|
||||
};
|
||||
let mut src = InputStream {
|
||||
input: LocatingSlice::new(src),
|
||||
state: (),
|
||||
};
|
||||
match Node::parse_template(&mut src, &state) {
|
||||
Ok(nodes) if src.is_empty() => Ok(Self { nodes }),
|
||||
Ok(_) | Err(ErrMode::Incomplete(_)) => unreachable!(),
|
||||
@ -348,12 +354,13 @@ impl<'a> ErrorContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> winnow::error::ParserError<&'a str> for ErrorContext<'a> {
|
||||
impl<'a> winnow::error::ParserError<InputStream<'a>> for ErrorContext<'a> {
|
||||
type Inner = Self;
|
||||
|
||||
fn from_input(input: &&'a str) -> Self {
|
||||
#[inline]
|
||||
fn from_input(input: &InputStream<'a>) -> Self {
|
||||
Self {
|
||||
span: (*input).into(),
|
||||
span: Span(***input),
|
||||
message: None,
|
||||
}
|
||||
}
|
||||
@ -373,40 +380,35 @@ impl<'a, E: std::fmt::Display> FromExternalError<&'a str, E> for ErrorContext<'a
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_ws0<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
*i = i.trim_ascii_start();
|
||||
Ok(())
|
||||
fn skip_ws0<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
take_while(0.., |c: char| c.is_ascii_whitespace())
|
||||
.void()
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_ws1<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
let j = i.trim_ascii_start();
|
||||
if i.len() != j.len() {
|
||||
*i = i.trim_ascii_start();
|
||||
Ok(())
|
||||
} else {
|
||||
fail.parse_next(i)
|
||||
}
|
||||
fn skip_ws1<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
take_while(1.., |c: char| c.is_ascii_whitespace())
|
||||
.void()
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn ws<'a, O>(
|
||||
inner: impl ModalParser<&'a str, O, ErrorContext<'a>>,
|
||||
) -> impl ModalParser<&'a str, O, ErrorContext<'a>> {
|
||||
inner: impl ModalParser<InputStream<'a>, O, ErrorContext<'a>>,
|
||||
) -> impl ModalParser<InputStream<'a>, O, ErrorContext<'a>> {
|
||||
delimited(skip_ws0, inner, skip_ws0)
|
||||
}
|
||||
|
||||
fn keyword(k: &str) -> impl ModalParser<&str, &str, ErrorContext<'_>> {
|
||||
fn keyword<'a>(k: &str) -> impl ModalParser<InputStream<'a>, &'a str, ErrorContext<'a>> {
|
||||
identifier.verify(move |v: &str| v == k)
|
||||
}
|
||||
|
||||
fn identifier<'i>(input: &mut &'i str) -> ParseResult<'i> {
|
||||
fn identifier<'i>(input: &mut InputStream<'i>) -> ParseResult<'i> {
|
||||
let head = any.verify(|&c| c == '_' || unicode_ident::is_xid_start(c));
|
||||
let tail = take_while(.., unicode_ident::is_xid_continue);
|
||||
(head, tail).take().parse_next(input)
|
||||
}
|
||||
|
||||
fn bool_lit<'i>(i: &mut &'i str) -> ParseResult<'i> {
|
||||
fn bool_lit<'i>(i: &mut InputStream<'i>) -> ParseResult<'i> {
|
||||
alt((keyword("false"), keyword("true"))).parse_next(i)
|
||||
}
|
||||
|
||||
@ -416,12 +418,12 @@ pub enum Num<'a> {
|
||||
Float(&'a str, Option<FloatKind>),
|
||||
}
|
||||
|
||||
fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
fn num_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Num<'a>> {
|
||||
fn num_lit_suffix<'a, T: Copy>(
|
||||
kind: &'a str,
|
||||
list: &[(&str, T)],
|
||||
start: &'a str,
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
) -> ParseResult<'a, T> {
|
||||
let suffix = identifier.parse_next(i)?;
|
||||
if let Some(value) = list
|
||||
@ -435,7 +437,7 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
|
||||
// Equivalent to <https://github.com/rust-lang/rust/blob/e3f909b2bbd0b10db6f164d466db237c582d3045/compiler/rustc_lexer/src/lib.rs#L587-L620>.
|
||||
let int_with_base = (opt('-'), |i: &mut _| {
|
||||
@ -450,7 +452,7 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
|
||||
// Equivalent to <https://github.com/rust-lang/rust/blob/e3f909b2bbd0b10db6f164d466db237c582d3045/compiler/rustc_lexer/src/lib.rs#L626-L653>:
|
||||
// no `_` directly after the decimal point `.`, or between `e` and `+/-`.
|
||||
let float = |i: &mut &'a str| -> ParseResult<'a, ()> {
|
||||
let float = |i: &mut InputStream<'a>| -> ParseResult<'a, ()> {
|
||||
let has_dot = opt(('.', separated_digits(10, true))).parse_next(i)?;
|
||||
let has_exp = opt(|i: &mut _| {
|
||||
let (kind, op) = (one_of(['e', 'E']), opt(one_of(['+', '-']))).parse_next(i)?;
|
||||
@ -467,10 +469,7 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
.parse_next(i)?;
|
||||
match (has_dot, has_exp) {
|
||||
(Some(_), _) | (_, Some(())) => Ok(()),
|
||||
_ => {
|
||||
*i = start;
|
||||
fail.parse_next(i)
|
||||
}
|
||||
_ => fail(i),
|
||||
}
|
||||
};
|
||||
|
||||
@ -504,12 +503,9 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
||||
fn separated_digits<'a>(
|
||||
radix: u32,
|
||||
start: bool,
|
||||
) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> {
|
||||
) -> impl ModalParser<InputStream<'a>, &'a str, ErrorContext<'a>> {
|
||||
(
|
||||
move |i: &mut &'a _| match start {
|
||||
true => Ok(()),
|
||||
false => repeat(0.., '_').parse_next(i),
|
||||
},
|
||||
cond(!start, repeat(0.., '_').map(|()| ())),
|
||||
one_of(move |ch: char| ch.is_digit(radix)),
|
||||
repeat(0.., one_of(move |ch: char| ch == '_' || ch.is_digit(radix))).map(|()| ()),
|
||||
)
|
||||
@ -560,10 +556,10 @@ pub struct StrLit<'a> {
|
||||
pub contains_high_ascii: bool,
|
||||
}
|
||||
|
||||
fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
||||
fn str_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, StrLit<'a>> {
|
||||
// <https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str.syntax>
|
||||
|
||||
fn inner<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
||||
fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, StrLit<'a>> {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Sequence<'a> {
|
||||
Text(&'a str),
|
||||
@ -572,7 +568,7 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
||||
Cr(bool),
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let mut contains_null = false;
|
||||
let mut contains_unicode_character = false;
|
||||
let mut contains_unicode_escape = false;
|
||||
@ -656,7 +652,7 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
||||
})
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
|
||||
let prefix = terminated(
|
||||
opt(alt((
|
||||
@ -697,7 +693,7 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
||||
Ok(lit)
|
||||
}
|
||||
|
||||
fn not_suffix_with_hash<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn not_suffix_with_hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||
if let Some(suffix) = opt(identifier.take()).parse_next(i)? {
|
||||
return cut_error!(
|
||||
"you are missing a space to separate two string literals",
|
||||
@ -707,8 +703,8 @@ fn not_suffix_with_hash<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn str_lit_without_prefix<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
||||
let start = *i;
|
||||
fn str_lit_without_prefix<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> {
|
||||
let start = ***i;
|
||||
let lit = str_lit.parse_next(i)?;
|
||||
|
||||
let kind = match lit.prefix {
|
||||
@ -739,42 +735,62 @@ pub struct CharLit<'a> {
|
||||
|
||||
// Information about allowed character escapes is available at:
|
||||
// <https://doc.rust-lang.org/reference/tokens.html#character-literals>.
|
||||
fn char_lit<'a>(i: &mut &'a str) -> ParseResult<'a, CharLit<'a>> {
|
||||
let start = *i;
|
||||
|
||||
let prefix = terminated(
|
||||
fn char_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, CharLit<'a>> {
|
||||
let ((prefix, _, content, is_closed), span) = (
|
||||
alt(('b'.value(Some(CharPrefix::Binary)), empty.value(None))),
|
||||
'\'',
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
let content = opt(terminated(
|
||||
opt(take_escaped(none_of(['\\', '\'']), '\\', any)),
|
||||
'\'',
|
||||
))
|
||||
.parse_next(i)?;
|
||||
opt('\''),
|
||||
)
|
||||
.with_taken()
|
||||
.parse_next(i)?;
|
||||
|
||||
let Some(content) = content else {
|
||||
if is_closed.is_none() {
|
||||
if let Some(prefix) = prefix {
|
||||
return cut_error!(
|
||||
match prefix {
|
||||
CharPrefix::Binary => "unterminated byte constant",
|
||||
CharPrefix::Binary => "unterminated byte literal",
|
||||
},
|
||||
start,
|
||||
span,
|
||||
);
|
||||
} else {
|
||||
return fail(i);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let content = match content.unwrap_or_default() {
|
||||
"" => return cut_error!("empty character literal", start),
|
||||
"" => {
|
||||
return cut_error!(
|
||||
match prefix {
|
||||
Some(CharPrefix::Binary) => "empty byte literal",
|
||||
None => "empty character literal",
|
||||
},
|
||||
span,
|
||||
);
|
||||
}
|
||||
content => content,
|
||||
};
|
||||
|
||||
let mut is = content;
|
||||
let Ok(c) = Char::parse(&mut is) else {
|
||||
return cut_error!("invalid character", start);
|
||||
let mut content_i = content;
|
||||
let Ok(c) = Char::parse(&mut content_i) else {
|
||||
return cut_error!("invalid character", span);
|
||||
};
|
||||
if !content_i.is_empty() {
|
||||
let (c, s) = match prefix {
|
||||
Some(CharPrefix::Binary) => ("byte", "binary string"),
|
||||
None => ("character", "string"),
|
||||
};
|
||||
return cut_error!(
|
||||
format!(
|
||||
"cannot have multiple characters in a {c} literal, use `{}\"...\"` to write a {s}",
|
||||
match prefix {
|
||||
Some(CharPrefix::Binary) => "b",
|
||||
None => "",
|
||||
}
|
||||
),
|
||||
span
|
||||
);
|
||||
}
|
||||
|
||||
let (nb, max_value, err1, err2) = match c {
|
||||
Char::Literal | Char::Escaped => {
|
||||
@ -792,7 +808,7 @@ fn char_lit<'a>(i: &mut &'a str) -> ParseResult<'a, CharLit<'a>> {
|
||||
Some(CharPrefix::Binary) => {
|
||||
return cut_error!(
|
||||
"cannot use unicode escape in byte string in byte literal",
|
||||
start,
|
||||
span,
|
||||
);
|
||||
}
|
||||
None => (
|
||||
@ -807,10 +823,10 @@ fn char_lit<'a>(i: &mut &'a str) -> ParseResult<'a, CharLit<'a>> {
|
||||
};
|
||||
|
||||
let Ok(nb) = u32::from_str_radix(nb, 16) else {
|
||||
return cut_error!(err1, start);
|
||||
return cut_error!(err1, span);
|
||||
};
|
||||
if nb > max_value {
|
||||
return cut_error!(err2, start);
|
||||
return cut_error!(err2, span);
|
||||
}
|
||||
|
||||
Ok(CharLit { prefix, content })
|
||||
@ -830,11 +846,9 @@ enum Char<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Char<'a> {
|
||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||
if i.chars().count() == 1 {
|
||||
return any.value(Self::Literal).parse_next(i);
|
||||
}
|
||||
(
|
||||
fn parse(i: &mut &'a str) -> ModalResult<Self, ()> {
|
||||
let unescaped = none_of(('\\', '\'')).value(Self::Literal);
|
||||
let escaped = preceded(
|
||||
'\\',
|
||||
alt((
|
||||
'n'.value(Self::Escaped),
|
||||
@ -854,9 +868,8 @@ impl<'a> Char<'a> {
|
||||
)
|
||||
.map(|(_, s, _)| Self::UnicodeEscape(s)),
|
||||
)),
|
||||
)
|
||||
.map(|(_, ch)| ch)
|
||||
.parse_next(i)
|
||||
);
|
||||
alt((unescaped, escaped)).parse_next(i)
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,10 +880,10 @@ pub enum PathOrIdentifier<'a> {
|
||||
}
|
||||
|
||||
fn path_or_identifier<'a>(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
level: Level<'_>,
|
||||
) -> ParseResult<'a, PathOrIdentifier<'a>> {
|
||||
let start_i = *i;
|
||||
let start_i = ***i;
|
||||
let root = ws(opt("::"));
|
||||
let tail = opt(repeat(
|
||||
1..,
|
||||
@ -927,11 +940,11 @@ struct State<'a, 'l> {
|
||||
}
|
||||
|
||||
impl State<'_, '_> {
|
||||
fn tag_block_start<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_block_start<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
self.syntax.block_start.value(()).parse_next(i)
|
||||
}
|
||||
|
||||
fn tag_block_end<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_block_end<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
let control = alt((
|
||||
self.syntax.block_end.value(None),
|
||||
peek(delimited('%', alt(('-', '~', '+')).map(Some), '}')),
|
||||
@ -945,7 +958,7 @@ impl State<'_, '_> {
|
||||
control.escape_default(),
|
||||
self.syntax.block_end.escape_default(),
|
||||
),
|
||||
*i,
|
||||
***i,
|
||||
);
|
||||
Err(err.backtrack())
|
||||
} else {
|
||||
@ -953,19 +966,19 @@ impl State<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn tag_comment_start<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_comment_start<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
self.syntax.comment_start.value(()).parse_next(i)
|
||||
}
|
||||
|
||||
fn tag_comment_end<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_comment_end<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
self.syntax.comment_end.value(()).parse_next(i)
|
||||
}
|
||||
|
||||
fn tag_expr_start<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_expr_start<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
self.syntax.expr_start.value(()).parse_next(i)
|
||||
}
|
||||
|
||||
fn tag_expr_end<'i>(&self, i: &mut &'i str) -> ParseResult<'i, ()> {
|
||||
fn tag_expr_end<'i>(&self, i: &mut InputStream<'i>) -> ParseResult<'i, ()> {
|
||||
self.syntax.expr_end.value(()).parse_next(i)
|
||||
}
|
||||
|
||||
@ -1205,7 +1218,7 @@ impl LevelGuard<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn filter<'a>(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, Filter<'a>> {
|
||||
fn filter<'a>(i: &mut InputStream<'a>, level: Level<'_>) -> ParseResult<'a, Filter<'a>> {
|
||||
preceded(
|
||||
('|', not('|')),
|
||||
cut_err(|i: &mut _| Filter::parse(i, level)),
|
||||
@ -1466,13 +1479,13 @@ fn cut_context_err<'a, T>(gen_err: impl FnOnce() -> ErrorContext<'a>) -> ParseRe
|
||||
|
||||
type HashSet<T> = std::collections::hash_set::HashSet<T, FxBuildHasher>;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_strip_common() {
|
||||
// Full path is returned instead of empty when the entire path is in common.
|
||||
@ -1499,66 +1512,78 @@ mod test {
|
||||
assert_eq!(strip_common(&cwd, Path::new("/a/b/c")), "/a/b/c");
|
||||
}
|
||||
|
||||
fn parse_peek<'a, T>(
|
||||
mut parser: impl ModalParser<InputStream<'a>, T, ErrorContext<'a>>,
|
||||
input: &'a str,
|
||||
) -> ParseResult<'a, (&'a str, T)> {
|
||||
let mut i = InputStream {
|
||||
input: LocatingSlice::new(input),
|
||||
state: (),
|
||||
};
|
||||
let value = parser.parse_next(&mut i)?;
|
||||
Ok((**i, value))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_num_lit() {
|
||||
// Should fail.
|
||||
assert!(num_lit.parse_peek(".").is_err());
|
||||
assert!(parse_peek(num_lit, ".").is_err());
|
||||
// Should succeed.
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1.2E-02").unwrap(),
|
||||
parse_peek(num_lit, "1.2E-02").unwrap(),
|
||||
("", Num::Float("1.2E-02", None))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("4e3").unwrap(),
|
||||
parse_peek(num_lit, "4e3").unwrap(),
|
||||
("", Num::Float("4e3", None)),
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("4e+_3").unwrap(),
|
||||
parse_peek(num_lit, "4e+_3").unwrap(),
|
||||
("", Num::Float("4e+_3", None)),
|
||||
);
|
||||
// Not supported because Rust wants a number before the `.`.
|
||||
assert!(num_lit.parse_peek(".1").is_err());
|
||||
assert!(num_lit.parse_peek(".1E-02").is_err());
|
||||
assert!(parse_peek(num_lit, ".1").is_err());
|
||||
assert!(parse_peek(num_lit, ".1E-02").is_err());
|
||||
// A `_` directly after the `.` denotes a field.
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1._0").unwrap(),
|
||||
parse_peek(num_lit, "1._0").unwrap(),
|
||||
("._0", Num::Int("1", None))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1_.0").unwrap(),
|
||||
parse_peek(num_lit, "1_.0").unwrap(),
|
||||
("", Num::Float("1_.0", None))
|
||||
);
|
||||
// Not supported (voluntarily because of `1..` syntax).
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1.").unwrap(),
|
||||
parse_peek(num_lit, "1.").unwrap(),
|
||||
(".", Num::Int("1", None))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1_.").unwrap(),
|
||||
parse_peek(num_lit, "1_.").unwrap(),
|
||||
(".", Num::Int("1_", None))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1_2.").unwrap(),
|
||||
parse_peek(num_lit, "1_2.").unwrap(),
|
||||
(".", Num::Int("1_2", None))
|
||||
);
|
||||
// Numbers with suffixes
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("-1usize").unwrap(),
|
||||
parse_peek(num_lit, "-1usize").unwrap(),
|
||||
("", Num::Int("-1", Some(IntKind::Usize)))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("123_f32").unwrap(),
|
||||
parse_peek(num_lit, "123_f32").unwrap(),
|
||||
("", Num::Float("123_", Some(FloatKind::F32)))
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("1_.2_e+_3_f64|into_isize").unwrap(),
|
||||
parse_peek(num_lit, "1_.2_e+_3_f64|into_isize").unwrap(),
|
||||
(
|
||||
"|into_isize",
|
||||
Num::Float("1_.2_e+_3_", Some(FloatKind::F64))
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
num_lit.parse_peek("4e3f128").unwrap(),
|
||||
parse_peek(num_lit, "4e3f128").unwrap(),
|
||||
("", Num::Float("4e3", Some(FloatKind::F128))),
|
||||
);
|
||||
}
|
||||
@ -1570,42 +1595,42 @@ mod test {
|
||||
content: s,
|
||||
};
|
||||
|
||||
assert_eq!(char_lit.parse_peek("'a'").unwrap(), ("", lit("a")));
|
||||
assert_eq!(char_lit.parse_peek("'字'").unwrap(), ("", lit("字")));
|
||||
assert_eq!(parse_peek(char_lit, "'a'").unwrap(), ("", lit("a")));
|
||||
assert_eq!(parse_peek(char_lit, "'字'").unwrap(), ("", lit("字")));
|
||||
|
||||
// Escaped single characters.
|
||||
assert_eq!(char_lit.parse_peek("'\\\"'").unwrap(), ("", lit("\\\"")));
|
||||
assert_eq!(char_lit.parse_peek("'\\''").unwrap(), ("", lit("\\'")));
|
||||
assert_eq!(char_lit.parse_peek("'\\t'").unwrap(), ("", lit("\\t")));
|
||||
assert_eq!(char_lit.parse_peek("'\\n'").unwrap(), ("", lit("\\n")));
|
||||
assert_eq!(char_lit.parse_peek("'\\r'").unwrap(), ("", lit("\\r")));
|
||||
assert_eq!(char_lit.parse_peek("'\\0'").unwrap(), ("", lit("\\0")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\\"'").unwrap(), ("", lit("\\\"")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\''").unwrap(), ("", lit("\\'")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\t'").unwrap(), ("", lit("\\t")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\n'").unwrap(), ("", lit("\\n")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\r'").unwrap(), ("", lit("\\r")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\0'").unwrap(), ("", lit("\\0")));
|
||||
// Escaped ascii characters (up to `0x7F`).
|
||||
assert_eq!(char_lit.parse_peek("'\\x12'").unwrap(), ("", lit("\\x12")));
|
||||
assert_eq!(char_lit.parse_peek("'\\x02'").unwrap(), ("", lit("\\x02")));
|
||||
assert_eq!(char_lit.parse_peek("'\\x6a'").unwrap(), ("", lit("\\x6a")));
|
||||
assert_eq!(char_lit.parse_peek("'\\x7F'").unwrap(), ("", lit("\\x7F")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\x12'").unwrap(), ("", lit("\\x12")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\x02'").unwrap(), ("", lit("\\x02")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\x6a'").unwrap(), ("", lit("\\x6a")));
|
||||
assert_eq!(parse_peek(char_lit, "'\\x7F'").unwrap(), ("", lit("\\x7F")));
|
||||
// Escaped unicode characters (up to `0x10FFFF`).
|
||||
assert_eq!(
|
||||
char_lit.parse_peek("'\\u{A}'").unwrap(),
|
||||
parse_peek(char_lit, "'\\u{A}'").unwrap(),
|
||||
("", lit("\\u{A}"))
|
||||
);
|
||||
assert_eq!(
|
||||
char_lit.parse_peek("'\\u{10}'").unwrap(),
|
||||
parse_peek(char_lit, "'\\u{10}'").unwrap(),
|
||||
("", lit("\\u{10}"))
|
||||
);
|
||||
assert_eq!(
|
||||
char_lit.parse_peek("'\\u{aa}'").unwrap(),
|
||||
parse_peek(char_lit, "'\\u{aa}'").unwrap(),
|
||||
("", lit("\\u{aa}"))
|
||||
);
|
||||
assert_eq!(
|
||||
char_lit.parse_peek("'\\u{10FFFF}'").unwrap(),
|
||||
parse_peek(char_lit, "'\\u{10FFFF}'").unwrap(),
|
||||
("", lit("\\u{10FFFF}"))
|
||||
);
|
||||
|
||||
// Check with `b` prefix.
|
||||
assert_eq!(
|
||||
char_lit.parse_peek("b'a'").unwrap(),
|
||||
parse_peek(char_lit, "b'a'").unwrap(),
|
||||
(
|
||||
"",
|
||||
crate::CharLit {
|
||||
@ -1616,20 +1641,20 @@ mod test {
|
||||
);
|
||||
|
||||
// Should fail.
|
||||
assert!(char_lit.parse_peek("''").is_err());
|
||||
assert!(char_lit.parse_peek("'\\o'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\x'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\x1'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\x80'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\u'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\u{}'").is_err());
|
||||
assert!(char_lit.parse_peek("'\\u{110000}'").is_err());
|
||||
assert!(parse_peek(char_lit, "''").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\o'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\x'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\x1'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\x80'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\u'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\u{}'").is_err());
|
||||
assert!(parse_peek(char_lit, "'\\u{110000}'").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_lit() {
|
||||
assert_eq!(
|
||||
str_lit.parse_peek(r#"b"hello""#).unwrap(),
|
||||
parse_peek(str_lit, r#"b"hello""#).unwrap(),
|
||||
(
|
||||
"",
|
||||
StrLit {
|
||||
@ -1643,7 +1668,7 @@ mod test {
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
str_lit.parse_peek(r#"c"hello""#).unwrap(),
|
||||
parse_peek(str_lit, r#"c"hello""#).unwrap(),
|
||||
(
|
||||
"",
|
||||
StrLit {
|
||||
@ -1656,7 +1681,7 @@ mod test {
|
||||
}
|
||||
)
|
||||
);
|
||||
assert!(str_lit.parse_peek(r#"d"hello""#).is_err());
|
||||
assert!(parse_peek(str_lit, r#"d"hello""#).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -5,13 +5,13 @@ use winnow::combinator::{
|
||||
separated_pair, terminated,
|
||||
};
|
||||
use winnow::error::ErrMode;
|
||||
use winnow::stream::Stream as _;
|
||||
use winnow::token::{any, rest, take_until};
|
||||
use winnow::stream::Stream;
|
||||
use winnow::token::{any, rest, take, take_until};
|
||||
use winnow::{ModalParser, Parser};
|
||||
|
||||
use crate::{
|
||||
ErrorContext, Expr, Filter, HashSet, ParseResult, Span, State, Target, WithSpan, cut_error,
|
||||
filter, identifier, is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws,
|
||||
ErrorContext, Expr, Filter, HashSet, InputStream, ParseResult, Span, State, Target, WithSpan,
|
||||
cut_error, filter, identifier, is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -37,38 +37,28 @@ pub enum Node<'a> {
|
||||
|
||||
impl<'a> Node<'a> {
|
||||
pub(super) fn parse_template(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
s: &State<'_, '_>,
|
||||
) -> ParseResult<'a, Vec<Box<Self>>> {
|
||||
let start = *i;
|
||||
let result = match (|i: &mut _| Self::many(i, s)).parse_next(i) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
if let ErrMode::Backtrack(err) | ErrMode::Cut(err) = &err
|
||||
&& err.message.is_none()
|
||||
{
|
||||
*i = start;
|
||||
if let Some(mut span) = err.span.as_suffix_of(i) {
|
||||
opt(|i: &mut _| unexpected_tag(i, s)).parse_next(&mut span)?;
|
||||
}
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
let nodes = parse_with_unexpected_fallback(
|
||||
|i: &mut _| Self::many(i, s),
|
||||
|i: &mut _| unexpected_tag(i, s),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
opt(|i: &mut _| unexpected_tag(i, s)).parse_next(i)?;
|
||||
let is_eof = opt(eof).parse_next(i)?;
|
||||
if is_eof.is_none() {
|
||||
if !i.is_empty() {
|
||||
return cut_error!(
|
||||
"cannot parse entire template\n\
|
||||
you should never encounter this error\n\
|
||||
please report this error to <https://github.com/askama-rs/askama/issues>",
|
||||
*i,
|
||||
you should never encounter this error\n\
|
||||
please report this error to <https://github.com/askama-rs/askama/issues>",
|
||||
***i,
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
fn many(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Box<Self>>> {
|
||||
fn many(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Vec<Box<Self>>> {
|
||||
repeat(
|
||||
0..,
|
||||
alt((
|
||||
@ -82,13 +72,13 @@ impl<'a> Node<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||
let mut start = *i;
|
||||
let tag = preceded(
|
||||
|i: &mut _| s.tag_block_start(i),
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||
let start = i.checkpoint();
|
||||
let (span, tag) = (
|
||||
s.syntax.block_start,
|
||||
peek(preceded((opt(Whitespace::parse), skip_ws0), identifier)),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
.parse_next(i)?;
|
||||
|
||||
let func = match tag {
|
||||
"call" => Call::parse,
|
||||
@ -96,16 +86,19 @@ impl<'a> Node<'a> {
|
||||
"if" => If::parse,
|
||||
"for" => Loop::parse,
|
||||
"match" => Match::parse,
|
||||
"extends" => |i: &mut &'a str, _s: &State<'_, '_>| Extends::parse(i),
|
||||
"include" => |i: &mut &'a str, _s: &State<'_, '_>| Include::parse(i),
|
||||
"import" => |i: &mut &'a str, _s: &State<'_, '_>| Import::parse(i),
|
||||
"extends" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Extends::parse(i),
|
||||
"include" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Include::parse(i),
|
||||
"import" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Import::parse(i),
|
||||
"block" => BlockDef::parse,
|
||||
"macro" => Macro::parse,
|
||||
"raw" => Raw::parse,
|
||||
"break" => Self::r#break,
|
||||
"continue" => Self::r#continue,
|
||||
"filter" => FilterBlock::parse,
|
||||
_ => return fail.parse_next(&mut start),
|
||||
_ => {
|
||||
i.reset(&start);
|
||||
return fail.parse_next(i);
|
||||
}
|
||||
};
|
||||
|
||||
let _level_guard = s.level.nest(i)?;
|
||||
@ -120,18 +113,18 @@ impl<'a> Node<'a> {
|
||||
.parse_next(i)?;
|
||||
match closed {
|
||||
true => Ok(node),
|
||||
false => Err(ErrorContext::unclosed("block", s.syntax.block_end, start).cut()),
|
||||
false => Err(ErrorContext::unclosed("block", s.syntax.block_end, span).cut()),
|
||||
}
|
||||
}
|
||||
|
||||
fn r#break(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn r#break(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("break")),
|
||||
opt(Whitespace::parse),
|
||||
);
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let (pws, _, nws) = p.parse_next(i)?;
|
||||
if !s.is_in_loop() {
|
||||
return cut_error!("you can only `break` inside a `for` loop", start);
|
||||
@ -139,14 +132,14 @@ impl<'a> Node<'a> {
|
||||
Ok(Box::new(Self::Break(WithSpan::new(Ws(pws, nws), start, i))))
|
||||
}
|
||||
|
||||
fn r#continue(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn r#continue(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("continue")),
|
||||
opt(Whitespace::parse),
|
||||
);
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let (pws, _, nws) = p.parse_next(i)?;
|
||||
if !s.is_in_loop() {
|
||||
return cut_error!("you can only `continue` inside a `for` loop", start);
|
||||
@ -158,8 +151,8 @@ impl<'a> Node<'a> {
|
||||
))))
|
||||
}
|
||||
|
||||
fn expr(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||
let start = *i;
|
||||
fn expr(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||
let start = ***i;
|
||||
let level = s.level;
|
||||
let (pws, expr) = preceded(
|
||||
|i: &mut _| s.tag_expr_start(i),
|
||||
@ -214,27 +207,53 @@ impl<'a> Node<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn cut_node<'a, O>(
|
||||
kind: Option<&'static str>,
|
||||
inner: impl ModalParser<&'a str, O, ErrorContext<'a>>,
|
||||
) -> impl ModalParser<&'a str, O, ErrorContext<'a>> {
|
||||
let mut inner = cut_err(inner);
|
||||
move |i: &mut &'a str| {
|
||||
let start = *i;
|
||||
let result = inner.parse_next(i);
|
||||
if let Err(ErrMode::Cut(err) | ErrMode::Backtrack(err)) = &result
|
||||
&& err.message.is_none()
|
||||
#[inline]
|
||||
fn parse_with_unexpected_fallback<'a, O>(
|
||||
mut parser: impl ModalParser<InputStream<'a>, O, ErrorContext<'a>>,
|
||||
mut unexpected_parser: impl FnMut(&mut InputStream<'a>) -> ParseResult<'a, ()>,
|
||||
) -> impl ModalParser<InputStream<'a>, O, ErrorContext<'a>> {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn try_assign_fallback_error<'a>(
|
||||
i: &mut InputStream<'a>,
|
||||
start_checkpoint: <InputStream<'a> as Stream>::Checkpoint,
|
||||
unexpected_parser: &mut dyn FnMut(&mut InputStream<'a>) -> ParseResult<'a, ()>,
|
||||
err: &mut ErrMode<ErrorContext<'a>>,
|
||||
) {
|
||||
if let ErrMode::Backtrack(err_ctx) | ErrMode::Cut(err_ctx) = &err
|
||||
&& err_ctx.message.is_none()
|
||||
{
|
||||
*i = start;
|
||||
if let Some(mut span) = err.span.as_suffix_of(i) {
|
||||
opt(|i: &mut _| unexpected_raw_tag(kind, i)).parse_next(&mut span)?;
|
||||
let err_checkpoint = i.checkpoint();
|
||||
i.reset(&start_checkpoint);
|
||||
if let Some(offset) = err_ctx.span.offset_from(***i)
|
||||
&& let Err(better_err) =
|
||||
opt(preceded(take(offset), unexpected_parser)).parse_next(i)
|
||||
{
|
||||
*err = better_err;
|
||||
}
|
||||
i.reset(&err_checkpoint);
|
||||
}
|
||||
}
|
||||
|
||||
move |i: &mut InputStream<'a>| {
|
||||
let start_checkpoint = i.checkpoint();
|
||||
let mut result = parser.parse_next(i);
|
||||
if let Err(err) = &mut result {
|
||||
try_assign_fallback_error(i, start_checkpoint, &mut unexpected_parser, err);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn unexpected_tag<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
||||
#[inline]
|
||||
fn cut_node<'a, O>(
|
||||
kind: Option<&'static str>,
|
||||
inner: impl ModalParser<InputStream<'a>, O, ErrorContext<'a>>,
|
||||
) -> impl ModalParser<InputStream<'a>, O, ErrorContext<'a>> {
|
||||
parse_with_unexpected_fallback(cut_err(inner), move |i: &mut _| unexpected_raw_tag(kind, i))
|
||||
}
|
||||
|
||||
fn unexpected_tag<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
||||
(
|
||||
|i: &mut _| s.tag_block_start(i),
|
||||
opt(Whitespace::parse),
|
||||
@ -244,7 +263,10 @@ fn unexpected_tag<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()>
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn unexpected_raw_tag<'a>(kind: Option<&'static str>, i: &mut &'a str) -> ParseResult<'a, ()> {
|
||||
fn unexpected_raw_tag<'a>(
|
||||
kind: Option<&'static str>,
|
||||
i: &mut InputStream<'a>,
|
||||
) -> ParseResult<'a, ()> {
|
||||
let tag = peek(ws(identifier)).parse_next(i)?;
|
||||
let msg = match tag {
|
||||
"end" | "elif" | "else" | "when" => match kind {
|
||||
@ -256,7 +278,7 @@ fn unexpected_raw_tag<'a>(kind: Option<&'static str>, i: &mut &'a str) -> ParseR
|
||||
tag if tag.starts_with("end") => format!("unexpected closing tag `{tag}`"),
|
||||
tag => format!("unknown node `{tag}`"),
|
||||
};
|
||||
cut_error!(msg, *i)
|
||||
cut_error!(msg, tag)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@ -267,7 +289,7 @@ pub struct When<'a> {
|
||||
}
|
||||
|
||||
impl<'a> When<'a> {
|
||||
fn r#else(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
fn r#else(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let mut p = (
|
||||
|i: &mut _| s.tag_block_start(i),
|
||||
opt(Whitespace::parse),
|
||||
@ -282,7 +304,7 @@ impl<'a> When<'a> {
|
||||
),
|
||||
);
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let (_, pws, _, (nws, _, nodes)) = p.parse_next(i)?;
|
||||
Ok(WithSpan::new(
|
||||
Self {
|
||||
@ -296,9 +318,9 @@ impl<'a> When<'a> {
|
||||
}
|
||||
|
||||
#[allow(clippy::self_named_constructors)]
|
||||
fn when(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = *i;
|
||||
let endwhen = ws((
|
||||
fn when(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = ***i;
|
||||
let endwhen = ws(terminated(
|
||||
delimited(
|
||||
|i: &mut _| s.tag_block_start(i),
|
||||
opt(Whitespace::parse),
|
||||
@ -312,19 +334,19 @@ impl<'a> When<'a> {
|
||||
repeat(0.., ws(|i: &mut _| Comment::parse(i, s))).map(|()| ()),
|
||||
),
|
||||
),
|
||||
))
|
||||
.map(|(pws, _)| {
|
||||
)
|
||||
.with_taken())
|
||||
.map(|(pws, span)| {
|
||||
// A comment node is used to pass the whitespace suppressing information to the
|
||||
// generator. This way we don't have to fix up the next `when` node or the closing
|
||||
// `endmatch`. Any whitespaces after `endwhen` are to be suppressed. Actually, they
|
||||
// don't wind up in the AST anyway.
|
||||
Box::new(Node::Comment(WithSpan::new(
|
||||
Box::new(Node::Comment(WithSpan::new_with_full(
|
||||
Comment {
|
||||
ws: Ws(pws, Some(Whitespace::Suppress)),
|
||||
content: "",
|
||||
},
|
||||
start,
|
||||
i,
|
||||
span,
|
||||
)))
|
||||
});
|
||||
let mut p = (
|
||||
@ -366,8 +388,8 @@ pub struct Cond<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Cond<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let start = ***i;
|
||||
let (_, pws, cond, nws, _, nodes) = (
|
||||
|i: &mut _| s.tag_block_start(i),
|
||||
opt(Whitespace::parse),
|
||||
@ -405,7 +427,7 @@ pub struct CondTest<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CondTest<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
preceded(
|
||||
ws(keyword("if")),
|
||||
cut_node(Some("if"), |i: &mut _| Self::parse_cond(i, s)),
|
||||
@ -413,22 +435,32 @@ impl<'a> CondTest<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn parse_cond(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
fn parse_cond(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
let (target, expr) = (
|
||||
opt(delimited(
|
||||
ws(alt((keyword("let"), keyword("set")))),
|
||||
ws(|i: &mut _| Target::parse(i, s)),
|
||||
ws('='),
|
||||
)),
|
||||
ws(|i: &mut _| {
|
||||
let start = *i;
|
||||
ws(|i: &mut InputStream<'a>| {
|
||||
let checkpoint = i.checkpoint();
|
||||
let start = ***i;
|
||||
|
||||
let mut expr = Expr::parse(i, s.level, false)?;
|
||||
if let Expr::BinOp(v) = &mut *expr.inner
|
||||
&& matches!(*v.rhs.inner, Expr::Var("set" | "let"))
|
||||
{
|
||||
let _level_guard = s.level.nest(i)?;
|
||||
*i = v.rhs.span.as_suffix_of(start).unwrap();
|
||||
let start_span = *i;
|
||||
|
||||
i.reset(&checkpoint);
|
||||
i.next_slice(
|
||||
v.rhs
|
||||
.span
|
||||
.offset_from(start)
|
||||
.expect("rhs offset cannot be invalid"),
|
||||
);
|
||||
|
||||
let start_span = ***i;
|
||||
let new_right = Self::parse_cond(i, s)?;
|
||||
v.rhs.inner = Box::new(Expr::LetCond(WithSpan::new(new_right, start_span, i)));
|
||||
}
|
||||
@ -456,7 +488,7 @@ pub enum Whitespace {
|
||||
}
|
||||
|
||||
impl Whitespace {
|
||||
fn parse<'i>(i: &mut &'i str) -> ParseResult<'i, Self> {
|
||||
fn parse<'i>(i: &mut InputStream<'i>) -> ParseResult<'i, Self> {
|
||||
any.verify_map(Self::parse_char).parse_next(i)
|
||||
}
|
||||
|
||||
@ -484,7 +516,7 @@ impl FromStr for Whitespace {
|
||||
}
|
||||
|
||||
fn check_block_start<'a>(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
start: &'a str,
|
||||
s: &State<'_, '_>,
|
||||
node: &str,
|
||||
@ -512,15 +544,18 @@ pub struct Loop<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Loop<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Box<Node<'a>>>> {
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn content<'a>(
|
||||
i: &mut InputStream<'a>,
|
||||
s: &State<'_, '_>,
|
||||
) -> ParseResult<'a, Vec<Box<Node<'a>>>> {
|
||||
s.enter_loop();
|
||||
let result = (|i: &mut _| Node::many(i, s)).parse_next(i);
|
||||
s.leave_loop();
|
||||
result
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let if_cond = preceded(
|
||||
ws(keyword("if")),
|
||||
cut_node(
|
||||
@ -632,8 +667,9 @@ fn check_duplicated_name<'a>(
|
||||
}
|
||||
|
||||
impl<'a> Macro<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let level = s.level;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let parameters = |i: &mut _| -> ParseResult<
|
||||
'_,
|
||||
@ -659,12 +695,12 @@ impl<'a> Macro<'a> {
|
||||
.parse_next(i)?;
|
||||
match args {
|
||||
Some((args, Some(_))) => Ok(args),
|
||||
Some((_, None)) => cut_error!("expected `)` to close macro argument list", *i),
|
||||
Some((_, None)) => cut_error!("expected `)` to close macro argument list", ***i),
|
||||
None => Ok(None),
|
||||
}
|
||||
};
|
||||
|
||||
let start_s = *i;
|
||||
let start_s = ***i;
|
||||
let mut start = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("macro")),
|
||||
@ -719,8 +755,8 @@ impl<'a> Macro<'a> {
|
||||
cut_node(
|
||||
Some("macro"),
|
||||
preceded(
|
||||
opt(|i: &mut _| {
|
||||
let before = *i;
|
||||
opt(|i: &mut InputStream<'a>| {
|
||||
let before = ***i;
|
||||
let end_name = ws(identifier).parse_next(i)?;
|
||||
check_end_name(before, name, end_name, "macro")
|
||||
}),
|
||||
@ -756,9 +792,9 @@ pub struct FilterBlock<'a> {
|
||||
}
|
||||
|
||||
impl<'a> FilterBlock<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn filters<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Filter<'a>> {
|
||||
let mut start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn filters<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Filter<'a>> {
|
||||
let mut start = ***i;
|
||||
let mut res = Filter::parse(i, s.level)?;
|
||||
res.arguments
|
||||
.insert(0, WithSpan::new(Box::new(Expr::FilterSource), start, i));
|
||||
@ -773,12 +809,12 @@ impl<'a> FilterBlock<'a> {
|
||||
WithSpan::new(Box::new(Expr::Filter(res)), start.trim_start(), i),
|
||||
);
|
||||
res = filter;
|
||||
start = *i;
|
||||
start = ***i;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let (pws1, _, (filters, nws1, _), nodes, (_, pws2, _, nws2)) = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("filter")),
|
||||
@ -824,8 +860,8 @@ pub struct Import<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Import<'a> {
|
||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("import")),
|
||||
@ -863,8 +899,8 @@ pub struct Call<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Call<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start_s = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start_s = ***i;
|
||||
let parameters = |i: &mut _| -> ParseResult<'_, Option<Vec<&str>>> {
|
||||
let args = opt(preceded(
|
||||
'(',
|
||||
@ -877,7 +913,7 @@ impl<'a> Call<'a> {
|
||||
|
||||
match args {
|
||||
Some((args, Some(_))) => Ok(args),
|
||||
Some((_, None)) => cut_error!("expected `)` to close call argument list", *i),
|
||||
Some((_, None)) => cut_error!("expected `)` to close call argument list", ***i),
|
||||
None => Ok(None),
|
||||
}
|
||||
};
|
||||
@ -942,8 +978,8 @@ pub struct Match<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Match<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("match")),
|
||||
@ -1015,8 +1051,8 @@ pub struct BlockDef<'a> {
|
||||
}
|
||||
|
||||
impl<'a> BlockDef<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start_s = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start_s = ***i;
|
||||
let mut start = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("block")),
|
||||
@ -1042,8 +1078,8 @@ impl<'a> BlockDef<'a> {
|
||||
cut_node(
|
||||
Some("block"),
|
||||
(
|
||||
opt(|i: &mut _| {
|
||||
let before = *i;
|
||||
opt(|i: &mut InputStream<'a>| {
|
||||
let before = ***i;
|
||||
let end_name = ws(identifier).parse_next(i)?;
|
||||
check_end_name(before, name, end_name, "block")
|
||||
}),
|
||||
@ -1096,9 +1132,9 @@ pub struct Lit<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Lit<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
not(eof).parse_next(i)?;
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let mut content = opt(take_until(
|
||||
..,
|
||||
(
|
||||
@ -1139,9 +1175,12 @@ pub struct Raw<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Raw<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn endraw<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, (Ws, &'a str)> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn endraw<'a>(
|
||||
i: &mut InputStream<'a>,
|
||||
s: &State<'_, '_>,
|
||||
) -> ParseResult<'a, (Ws, &'a str)> {
|
||||
let start = ***i;
|
||||
loop {
|
||||
// find the string "endraw", strip any spaces before it, and look if there is a `{%`
|
||||
let inner = take_until(.., "endraw").parse_next(i)?;
|
||||
@ -1157,7 +1196,7 @@ impl<'a> Raw<'a> {
|
||||
};
|
||||
|
||||
// We found `{% endraw`. Do we find `%}`, too?
|
||||
*i = i.trim_ascii_start();
|
||||
skip_ws0(i)?;
|
||||
let i_before_nws = *i;
|
||||
let nws = opt(Whitespace::parse).parse_next(i)?;
|
||||
if opt(peek(s.syntax.block_end)).parse_next(i)?.is_none() {
|
||||
@ -1172,7 +1211,7 @@ impl<'a> Raw<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
terminated(opt(Whitespace::parse), ws(keyword("raw"))),
|
||||
cut_node(
|
||||
@ -1205,8 +1244,8 @@ pub struct Let<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Let<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(alt((keyword("let"), keyword("set")))),
|
||||
@ -1276,8 +1315,8 @@ pub struct If<'a> {
|
||||
}
|
||||
|
||||
impl<'a> If<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
|i: &mut _| CondTest::parse(i, s),
|
||||
@ -1336,8 +1375,8 @@ pub struct Include<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Include<'a> {
|
||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = ***i;
|
||||
let mut p = (
|
||||
opt(Whitespace::parse),
|
||||
ws(keyword("include")),
|
||||
@ -1364,8 +1403,7 @@ pub struct Extends<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Extends<'a> {
|
||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
let start = *i;
|
||||
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
preceded(
|
||||
(opt(Whitespace::parse), ws(keyword("extends"))),
|
||||
cut_node(
|
||||
@ -1373,7 +1411,8 @@ impl<'a> Extends<'a> {
|
||||
terminated(ws(str_lit_without_prefix), opt(Whitespace::parse)),
|
||||
),
|
||||
)
|
||||
.map(|path| Box::new(Node::Extends(WithSpan::new(Self { path }, start, i))))
|
||||
.with_taken()
|
||||
.map(|(path, span)| Box::new(Node::Extends(WithSpan::new_with_full(Self { path }, span))))
|
||||
.parse_next(i)
|
||||
}
|
||||
}
|
||||
@ -1385,8 +1424,8 @@ pub struct Comment<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Comment<'a> {
|
||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
||||
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||
fn content<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
||||
let mut depth = 0usize;
|
||||
loop {
|
||||
take_until(.., (s.syntax.comment_start, s.syntax.comment_end)).parse_next(i)?;
|
||||
@ -1403,7 +1442,7 @@ impl<'a> Comment<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let start = *i;
|
||||
let start = ***i;
|
||||
let mut content = preceded(
|
||||
|i: &mut _| s.tag_comment_start(i),
|
||||
opt(terminated(
|
||||
@ -1448,8 +1487,8 @@ pub struct Ws(pub Option<Whitespace>, pub Option<Whitespace>);
|
||||
fn end_node<'a, 'g: 'a>(
|
||||
node: &'g str,
|
||||
expected: &'g str,
|
||||
) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> + 'g {
|
||||
move |i: &mut &'a str| {
|
||||
) -> impl ModalParser<InputStream<'a>, &'a str, ErrorContext<'a>> + 'g {
|
||||
move |i: &mut InputStream<'a>| {
|
||||
let start = i.checkpoint();
|
||||
let actual = ws(identifier).parse_next(i)?;
|
||||
if actual == expected {
|
||||
@ -1458,7 +1497,7 @@ fn end_node<'a, 'g: 'a>(
|
||||
i.reset(&start);
|
||||
cut_error!(
|
||||
format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"),
|
||||
*i,
|
||||
***i,
|
||||
)
|
||||
} else {
|
||||
i.reset(&start);
|
||||
|
@ -4,9 +4,9 @@ use winnow::token::one_of;
|
||||
use winnow::{ModalParser, Parser};
|
||||
|
||||
use crate::{
|
||||
CharLit, ErrorContext, Num, ParseErr, ParseResult, PathComponent, PathOrIdentifier, State,
|
||||
StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, cut_error, identifier,
|
||||
is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws,
|
||||
CharLit, ErrorContext, InputStream, Num, ParseErr, ParseResult, PathComponent,
|
||||
PathOrIdentifier, State, StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, cut_error,
|
||||
identifier, is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@ -31,7 +31,7 @@ pub enum Target<'a> {
|
||||
|
||||
impl<'a> Target<'a> {
|
||||
/// Parses multiple targets with `or` separating them
|
||||
pub(super) fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
pub(super) fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
let _level_guard = s.level.nest(i)?;
|
||||
let mut p = opt(preceded(ws(keyword("or")), |i: &mut _| {
|
||||
Self::parse_one(i, s)
|
||||
@ -50,7 +50,7 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
|
||||
/// Parses a single target without an `or`, unless it is wrapped in parentheses.
|
||||
fn parse_one(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
fn parse_one(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
let mut opt_opening_paren = opt(ws('(')).map(|o| o.is_some());
|
||||
let mut opt_opening_brace = opt(ws('{')).map(|o| o.is_some());
|
||||
let mut opt_opening_bracket = opt(ws('[')).map(|o| o.is_some());
|
||||
@ -87,9 +87,9 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
|
||||
let path = |i: &mut _| path_or_identifier(i, s.level);
|
||||
let path = path.try_map(|r| match r {
|
||||
PathOrIdentifier::Path(v) => Ok(v),
|
||||
PathOrIdentifier::Identifier(v) => Err(v),
|
||||
let path = path.verify_map(|r| match r {
|
||||
PathOrIdentifier::Path(v) => Some(v),
|
||||
PathOrIdentifier::Identifier(_) => None,
|
||||
});
|
||||
|
||||
// match structs
|
||||
@ -136,16 +136,15 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
|
||||
// neither literal nor struct nor path
|
||||
let i_before_identifier = *i;
|
||||
let name = identifier.parse_next(i)?;
|
||||
let target = match name {
|
||||
"_" => Self::Placeholder(WithSpan::new((), i_before_identifier, i)),
|
||||
_ => verify_name(i_before_identifier, name)?,
|
||||
"_" => Self::Placeholder(WithSpan::new_with_full((), name)),
|
||||
_ => verify_name(name)?,
|
||||
};
|
||||
Ok(target)
|
||||
}
|
||||
|
||||
fn lit(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||
fn lit(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||
alt((
|
||||
str_lit.map(Self::StrLit),
|
||||
char_lit.map(Self::CharLit),
|
||||
@ -157,14 +156,12 @@ impl<'a> Target<'a> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn unnamed(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
fn unnamed(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Self> {
|
||||
alt((Self::rest, |i: &mut _| Self::parse(i, s))).parse_next(i)
|
||||
}
|
||||
|
||||
fn named(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, (&'a str, Self)> {
|
||||
let start = *i;
|
||||
let rest = opt(Self::rest.with_taken()).parse_next(i)?;
|
||||
if let Some(rest) = rest {
|
||||
fn named(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, (&'a str, Self)> {
|
||||
if let Some(rest) = opt(Self::rest.with_taken()).parse_next(i)? {
|
||||
let chr = peek(ws(opt(one_of([',', ':'])))).parse_next(i)?;
|
||||
if let Some(chr) = chr {
|
||||
return cut_error!(
|
||||
@ -172,7 +169,7 @@ impl<'a> Target<'a> {
|
||||
"unexpected `{chr}` character after `..`\n\
|
||||
note that in a named struct, `..` must come last to ignore other members"
|
||||
),
|
||||
*i,
|
||||
***i,
|
||||
);
|
||||
}
|
||||
if let Target::Rest(ref s) = rest.0
|
||||
@ -180,30 +177,28 @@ impl<'a> Target<'a> {
|
||||
{
|
||||
return cut_error!("`@ ..` cannot be used in struct", s.span);
|
||||
}
|
||||
return Ok((rest.1, rest.0));
|
||||
Ok((rest.1, rest.0))
|
||||
} else {
|
||||
let (src, target) = (
|
||||
identifier,
|
||||
opt(preceded(ws(':'), |i: &mut _| Self::parse(i, s))),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
if src == "_" {
|
||||
return cut_error!("cannot use placeholder `_` as source in named struct", src);
|
||||
}
|
||||
|
||||
let target = match target {
|
||||
Some(target) => target,
|
||||
None => verify_name(src)?,
|
||||
};
|
||||
Ok((src, target))
|
||||
}
|
||||
|
||||
*i = start;
|
||||
let (src, target) = (
|
||||
identifier,
|
||||
opt(preceded(ws(':'), |i: &mut _| Self::parse(i, s))),
|
||||
)
|
||||
.parse_next(i)?;
|
||||
|
||||
if src == "_" {
|
||||
*i = start;
|
||||
return cut_error!("cannot use placeholder `_` as source in named struct", *i);
|
||||
}
|
||||
|
||||
let target = match target {
|
||||
Some(target) => target,
|
||||
None => verify_name(start, src)?,
|
||||
};
|
||||
Ok((src, target))
|
||||
}
|
||||
|
||||
fn rest(i: &mut &'a str) -> ParseResult<'a, Self> {
|
||||
let start = *i;
|
||||
fn rest(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||
let start = ***i;
|
||||
let (ident, _) = (opt((identifier, ws('@'))), "..").parse_next(i)?;
|
||||
Ok(Self::Rest(WithSpan::new(
|
||||
ident.map(|(ident, _)| ident),
|
||||
@ -213,18 +208,18 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_name<'a>(input: &'a str, name: &'a str) -> Result<Target<'a>, ErrMode<ErrorContext<'a>>> {
|
||||
fn verify_name<'a>(name: &'a str) -> Result<Target<'a>, ErrMode<ErrorContext<'a>>> {
|
||||
if is_rust_keyword(name) {
|
||||
cut_error!(
|
||||
format!("cannot use `{name}` as a name: it is a rust keyword"),
|
||||
input,
|
||||
name,
|
||||
)
|
||||
} else if !can_be_variable_name(name) {
|
||||
cut_error!(format!("`{name}` cannot be used as an identifier"), input)
|
||||
cut_error!(format!("`{name}` cannot be used as an identifier"), name)
|
||||
} else if name.starts_with("__askama") {
|
||||
cut_error!(
|
||||
format!("cannot use `{name}` as a name: it is reserved for `askama`"),
|
||||
input,
|
||||
name,
|
||||
)
|
||||
} else {
|
||||
Ok(Target::Name(name))
|
||||
@ -232,9 +227,9 @@ fn verify_name<'a>(input: &'a str, name: &'a str) -> Result<Target<'a>, ErrMode<
|
||||
}
|
||||
|
||||
fn collect_targets<'a, T>(
|
||||
i: &mut &'a str,
|
||||
i: &mut InputStream<'a>,
|
||||
delim: char,
|
||||
one: impl ModalParser<&'a str, T, ErrorContext<'a>>,
|
||||
one: impl ModalParser<InputStream<'a>, T, ErrorContext<'a>>,
|
||||
) -> ParseResult<'a, (bool, Vec<T>)> {
|
||||
let opt_comma = ws(opt(',')).map(|o| o.is_some());
|
||||
let mut opt_end = ws(opt(one_of(delim))).map(|o| o.is_some());
|
||||
@ -246,7 +241,7 @@ fn collect_targets<'a, T>(
|
||||
|
||||
let targets = opt(separated(1.., one, ws(',')).map(|v: Vec<_>| v)).parse_next(i)?;
|
||||
let Some(targets) = targets else {
|
||||
return cut_error!("expected comma separated list of members", *i);
|
||||
return cut_error!("expected comma separated list of members", ***i);
|
||||
};
|
||||
|
||||
let (has_comma, has_end) = (opt_comma, opt_end).parse_next(i)?;
|
||||
@ -256,7 +251,7 @@ fn collect_targets<'a, T>(
|
||||
true => format!("expected member, or `{delim}` as terminator"),
|
||||
false => format!("expected `,` for more members, or `{delim}` as terminator"),
|
||||
},
|
||||
*i
|
||||
***i
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use winnow::Parser;
|
||||
use winnow::{LocatingSlice, Parser};
|
||||
|
||||
use crate::node::{Lit, Raw, Whitespace, Ws};
|
||||
use crate::{
|
||||
Ast, Expr, Filter, InnerSyntax, Node, Num, PathComponent, PathOrIdentifier, Span, StrLit,
|
||||
Syntax, SyntaxBuilder, WithSpan,
|
||||
Ast, Expr, Filter, InnerSyntax, InputStream, Node, Num, PathComponent, PathOrIdentifier, Span,
|
||||
StrLit, Syntax, SyntaxBuilder, WithSpan,
|
||||
};
|
||||
|
||||
impl<T> WithSpan<'static, T> {
|
||||
@ -1349,10 +1349,13 @@ fn test_filter_with_path() {
|
||||
|
||||
#[test]
|
||||
fn underscore_is_an_identifier() {
|
||||
let mut input = "_";
|
||||
let mut input = InputStream {
|
||||
input: LocatingSlice::new("_"),
|
||||
state: (),
|
||||
};
|
||||
let result = crate::identifier.parse_next(&mut input);
|
||||
assert_eq!(result.unwrap(), "_");
|
||||
assert_eq!(input, "");
|
||||
assert_eq!(**input, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,6 +1,6 @@
|
||||
error: unknown node `fail`
|
||||
--> <source attribute>:2:6
|
||||
" fail %}\n{% endif %}"
|
||||
--> <source attribute>:2:7
|
||||
"fail %}\n{% endif %}"
|
||||
--> tests/ui/askama-block.rs:5:1
|
||||
|
|
||||
5 | /// Some documentation
|
||||
|
@ -54,7 +54,7 @@ error: unicode escape must be at most 10FFFF
|
||||
28 | #[template(path = "char-literals/char-literal-7.txt")]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: invalid character
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:11
|
||||
"'aaa' %}"
|
||||
--> tests/ui/char_literal.rs:32:21
|
||||
@ -62,7 +62,7 @@ error: invalid character
|
||||
32 | #[template(source = "{% let s = 'aaa' %}", ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unterminated byte constant
|
||||
error: unterminated byte literal
|
||||
--> <source attribute>:1:3
|
||||
"b'c }}"
|
||||
--> tests/ui/char_literal.rs:36:21
|
||||
@ -70,7 +70,7 @@ error: unterminated byte constant
|
||||
36 | #[template(source = r#"{{ b'c }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: empty character literal
|
||||
error: empty byte literal
|
||||
--> <source attribute>:1:3
|
||||
"b'' }}"
|
||||
--> tests/ui/char_literal.rs:40:21
|
||||
@ -134,7 +134,7 @@ error: cannot use unicode escape in byte string in byte literal
|
||||
68 | #[template(source = r#"{{ b'\u{10ffff}' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unterminated byte constant
|
||||
error: unterminated byte literal
|
||||
--> <source attribute>:1:6
|
||||
"b'c) }}"
|
||||
--> tests/ui/char_literal.rs:72:21
|
||||
@ -142,7 +142,7 @@ error: unterminated byte constant
|
||||
72 | #[template(source = r#"{{ a!(b'c) }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: empty character literal
|
||||
error: empty byte literal
|
||||
--> <source attribute>:1:3
|
||||
"b'' }}"
|
||||
--> tests/ui/char_literal.rs:76:21
|
||||
|
115
testing/tests/ui/character_literal-multiple-chars.rs
Normal file
115
testing/tests/ui/character_literal-multiple-chars.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use askama::Template;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ 'ab' }}"#, ext = "html")]
|
||||
struct Multiple1;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ '\0b' }}"#, ext = "html")]
|
||||
struct Multiple2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ 'b\0' }}"#, ext = "html")]
|
||||
struct Multiple3;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ '\\0' }}"#, ext = "html")]
|
||||
struct Multiple4;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ '\u{1234}b' }}"#, ext = "html")]
|
||||
struct Multiple5;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ '\u{1234}b' }}"#, ext = "html")]
|
||||
struct Multiple6;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ '\u{1234}\u{1234}' }}"#, ext = "html")]
|
||||
struct Multiple7;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'ab' }}"#, ext = "html")]
|
||||
struct ByteMultiple1;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'\0b' }}"#, ext = "html")]
|
||||
struct ByteMultiple2;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'b\0' }}"#, ext = "html")]
|
||||
struct ByteMultiple3;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'\\0' }}"#, ext = "html")]
|
||||
struct ByteMultiple4;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'\u{1234}b' }}"#, ext = "html")]
|
||||
struct ByteMultiple5;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'\u{1234}b' }}"#, ext = "html")]
|
||||
struct ByteMultiple6;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ b'\u{1234}\u{1234}' }}"#, ext = "html")]
|
||||
struct ByteMultiple7;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('ab') }}"#, ext = "html")]
|
||||
struct Multiple1InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('\0b') }}"#, ext = "html")]
|
||||
struct Multiple2InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('b\0') }}"#, ext = "html")]
|
||||
struct Multiple3InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('\\0') }}"#, ext = "html")]
|
||||
struct Multiple4InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('\u{1234}b') }}"#, ext = "html")]
|
||||
struct Multiple5InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('\u{1234}b') }}"#, ext = "html")]
|
||||
struct Multiple6InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!('\u{1234}\u{1234}') }}"#, ext = "html")]
|
||||
struct Multiple7InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'ab') }}"#, ext = "html")]
|
||||
struct ByteMultiple1InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'\0b') }}"#, ext = "html")]
|
||||
struct ByteMultiple2InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'b\0') }}"#, ext = "html")]
|
||||
struct ByteMultiple3InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'\\0') }}"#, ext = "html")]
|
||||
struct ByteMultiple4InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'\u{1234}b') }}"#, ext = "html")]
|
||||
struct ByteMultiple5InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'\u{1234}b') }}"#, ext = "html")]
|
||||
struct ByteMultiple6InMacro;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(source = r#"{{ x!(b'\u{1234}\u{1234}') }}"#, ext = "html")]
|
||||
struct ByteMultiple7InMacro;
|
||||
|
||||
fn main() {}
|
223
testing/tests/ui/character_literal-multiple-chars.stderr
Normal file
223
testing/tests/ui/character_literal-multiple-chars.stderr
Normal file
@ -0,0 +1,223 @@
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'ab' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:4:21
|
||||
|
|
||||
4 | #[template(source = r#"{{ 'ab' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'\\0b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:8:21
|
||||
|
|
||||
8 | #[template(source = r#"{{ '\0b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'b\\0' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:12:21
|
||||
|
|
||||
12 | #[template(source = r#"{{ 'b\0' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'\\\\0' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:16:21
|
||||
|
|
||||
16 | #[template(source = r#"{{ '\\0' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'\\u{1234}b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:20:21
|
||||
|
|
||||
20 | #[template(source = r#"{{ '\u{1234}b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'\\u{1234}b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:24:21
|
||||
|
|
||||
24 | #[template(source = r#"{{ '\u{1234}b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:3
|
||||
"'\\u{1234}\\u{1234}' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:28:21
|
||||
|
|
||||
28 | #[template(source = r#"{{ '\u{1234}\u{1234}' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'ab' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:32:21
|
||||
|
|
||||
32 | #[template(source = r#"{{ b'ab' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'\\0b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:36:21
|
||||
|
|
||||
36 | #[template(source = r#"{{ b'\0b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'b\\0' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:40:21
|
||||
|
|
||||
40 | #[template(source = r#"{{ b'b\0' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'\\\\0' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:44:21
|
||||
|
|
||||
44 | #[template(source = r#"{{ b'\\0' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'\\u{1234}b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:48:21
|
||||
|
|
||||
48 | #[template(source = r#"{{ b'\u{1234}b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'\\u{1234}b' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:52:21
|
||||
|
|
||||
52 | #[template(source = r#"{{ b'\u{1234}b' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:3
|
||||
"b'\\u{1234}\\u{1234}' }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:56:21
|
||||
|
|
||||
56 | #[template(source = r#"{{ b'\u{1234}\u{1234}' }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'ab') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:60:21
|
||||
|
|
||||
60 | #[template(source = r#"{{ x!('ab') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'\\0b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:64:21
|
||||
|
|
||||
64 | #[template(source = r#"{{ x!('\0b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'b\\0') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:68:21
|
||||
|
|
||||
68 | #[template(source = r#"{{ x!('b\0') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'\\\\0') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:72:21
|
||||
|
|
||||
72 | #[template(source = r#"{{ x!('\\0') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'\\u{1234}b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:76:21
|
||||
|
|
||||
76 | #[template(source = r#"{{ x!('\u{1234}b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'\\u{1234}b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:80:21
|
||||
|
|
||||
80 | #[template(source = r#"{{ x!('\u{1234}b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a character literal, use `"..."` to write a string
|
||||
--> <source attribute>:1:6
|
||||
"'\\u{1234}\\u{1234}') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:84:21
|
||||
|
|
||||
84 | #[template(source = r#"{{ x!('\u{1234}\u{1234}') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'ab') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:88:21
|
||||
|
|
||||
88 | #[template(source = r#"{{ x!(b'ab') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'\\0b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:92:21
|
||||
|
|
||||
92 | #[template(source = r#"{{ x!(b'\0b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'b\\0') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:96:21
|
||||
|
|
||||
96 | #[template(source = r#"{{ x!(b'b\0') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'\\\\0') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:100:21
|
||||
|
|
||||
100 | #[template(source = r#"{{ x!(b'\\0') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'\\u{1234}b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:104:21
|
||||
|
|
||||
104 | #[template(source = r#"{{ x!(b'\u{1234}b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'\\u{1234}b') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:108:21
|
||||
|
|
||||
108 | #[template(source = r#"{{ x!(b'\u{1234}b') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have multiple characters in a byte literal, use `b"..."` to write a binary string
|
||||
--> <source attribute>:1:6
|
||||
"b'\\u{1234}\\u{1234}') }}"
|
||||
--> tests/ui/character_literal-multiple-chars.rs:112:21
|
||||
|
|
||||
112 | #[template(source = r#"{{ x!(b'\u{1234}\u{1234}') }}"#, ext = "html")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
@ -1,46 +1,46 @@
|
||||
error: node `end` was not expected in the current context
|
||||
--> <source attribute>:1:2
|
||||
" end %}"
|
||||
--> <source attribute>:1:3
|
||||
"end %}"
|
||||
--> tests/ui/unexpected-tag.rs:5:1
|
||||
|
|
||||
5 | /// ```askama
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: node `elif` was not expected in the current context: `for` block
|
||||
--> <source attribute>:3:2
|
||||
" elif %}\n what?\n{% endfor %}"
|
||||
--> <source attribute>:3:3
|
||||
"elif %}\n what?\n{% endfor %}"
|
||||
--> tests/ui/unexpected-tag.rs:12:1
|
||||
|
|
||||
12 | /// ```askama
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: node `else` was not expected in the current context: `block` block
|
||||
--> <source attribute>:3:2
|
||||
" else %}\n else\n{% endblock meta %}"
|
||||
--> <source attribute>:3:3
|
||||
"else %}\n else\n{% endblock meta %}"
|
||||
--> tests/ui/unexpected-tag.rs:23:1
|
||||
|
|
||||
23 | /// ```askama
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: node `when` was not expected in the current context
|
||||
--> <source attribute>:1:2
|
||||
" when condition %}\n true\n{% endwhen %}"
|
||||
--> <source attribute>:1:3
|
||||
"when condition %}\n true\n{% endwhen %}"
|
||||
--> tests/ui/unexpected-tag.rs:34:1
|
||||
|
|
||||
34 | /// ```askama
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: unexpected closing tag `endlet`
|
||||
--> <source attribute>:1:20
|
||||
" endlet %}"
|
||||
--> <source attribute>:1:21
|
||||
"endlet %}"
|
||||
--> tests/ui/unexpected-tag.rs:43:1
|
||||
|
|
||||
43 | /// ```askama
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: unknown node `syntax`
|
||||
--> <source attribute>:1:2
|
||||
" syntax error %}"
|
||||
--> <source attribute>:1:3
|
||||
"syntax error %}"
|
||||
--> tests/ui/unexpected-tag.rs:50:1
|
||||
|
|
||||
50 | /// ```askama
|
||||
|
Loading…
x
Reference in New Issue
Block a user