mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-28 05:21:14 +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::{
|
use winnow::combinator::{
|
||||||
alt, cut_err, empty, fail, not, opt, peek, preceded, repeat, separated, terminated,
|
alt, cut_err, empty, fail, not, opt, peek, preceded, repeat, separated, terminated,
|
||||||
};
|
};
|
||||||
use winnow::error::{ErrMode, ParserError as _};
|
use winnow::stream::Stream;
|
||||||
use winnow::token::{one_of, take_until};
|
use winnow::token::{any, one_of, take, take_until};
|
||||||
|
|
||||||
use crate::node::CondTest;
|
use crate::node::CondTest;
|
||||||
use crate::{
|
use crate::{
|
||||||
CharLit, ErrorContext, HashSet, Level, Num, ParseResult, PathOrIdentifier, StrLit, StrPrefix,
|
CharLit, ErrorContext, HashSet, InputStream, Level, Num, ParseResult, PathOrIdentifier, StrLit,
|
||||||
WithSpan, can_be_variable_name, char_lit, cut_error, filter, identifier, keyword,
|
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,
|
not_suffix_with_hash, num_lit, path_or_identifier, skip_ws0, skip_ws1, str_lit, ws,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! expr_prec_layer {
|
macro_rules! expr_prec_layer {
|
||||||
( $name:ident, $inner:ident, $op:expr ) => {
|
( $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))
|
expr_prec_layer(i, level, Expr::$inner, |i: &mut _| $op.parse_next(i))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_prec_layer<'a>(
|
fn expr_prec_layer<'a>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
inner: fn(&mut &'a str, Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>>,
|
inner: fn(&mut InputStream<'a>, Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>>,
|
||||||
op: fn(&mut &'a str) -> ParseResult<'a>,
|
op: fn(&mut InputStream<'a>) -> ParseResult<'a>,
|
||||||
) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let mut expr = inner(i, level)?;
|
let mut expr = inner(i, level)?;
|
||||||
|
|
||||||
let mut level_guard = level.guard();
|
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>> {
|
pub(crate) fn parse(
|
||||||
let start = *i;
|
i: &mut InputStream<'a>,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
|
let start = ***i;
|
||||||
(
|
(
|
||||||
identifier,
|
identifier,
|
||||||
opt((ws("::"), |i: &mut _| TyGenerics::args(i, level))),
|
opt((ws("::"), |i: &mut _| TyGenerics::args(i, level))),
|
||||||
@ -248,12 +254,12 @@ pub struct BinOp<'a> {
|
|||||||
|
|
||||||
impl<'a> Expr<'a> {
|
impl<'a> Expr<'a> {
|
||||||
pub(super) fn arguments(
|
pub(super) fn arguments(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'a, Vec<WithSpan<'a, Box<Self>>>> {
|
) -> ParseResult<'a, Vec<WithSpan<'a, Box<Self>>>> {
|
||||||
let _level_guard = level.nest(i)?;
|
let _level_guard = level.nest(i)?;
|
||||||
let mut named_arguments = HashSet::default();
|
let mut named_arguments = HashSet::default();
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
|
|
||||||
preceded(
|
preceded(
|
||||||
ws('('),
|
ws('('),
|
||||||
@ -286,7 +292,7 @@ impl<'a> Expr<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn named_argument(
|
fn named_argument(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
named_arguments: &mut HashSet<&'a str>,
|
named_arguments: &mut HashSet<&'a str>,
|
||||||
start: &'a str,
|
start: &'a str,
|
||||||
@ -310,7 +316,7 @@ impl<'a> Expr<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse(
|
pub(super) fn parse(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
allow_underscore: bool,
|
allow_underscore: bool,
|
||||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
@ -361,13 +367,16 @@ impl<'a> Expr<'a> {
|
|||||||
expr_prec_layer!(or, and, "||");
|
expr_prec_layer!(or, and, "||");
|
||||||
expr_prec_layer!(and, compare, "&&");
|
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 right = |i: &mut _| {
|
||||||
let op = alt(("==", "!=", ">=", ">", "<=", "<"));
|
let op = alt(("==", "!=", ">=", ">", "<=", "<"));
|
||||||
(ws(op), |i: &mut _| Self::bor(i, level)).parse_next(i)
|
(ws(op), |i: &mut _| Self::bor(i, level)).parse_next(i)
|
||||||
};
|
};
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let expr = Self::bor(i, level)?;
|
let expr = Self::bor(i, level)?;
|
||||||
let Some((op, rhs)) = opt(right).parse_next(i)? else {
|
let Some((op, rhs)) = opt(right).parse_next(i)? else {
|
||||||
return Ok(expr);
|
return Ok(expr);
|
||||||
@ -397,14 +406,17 @@ impl<'a> Expr<'a> {
|
|||||||
expr_prec_layer!(shifts, addsub, alt((">>", "<<")));
|
expr_prec_layer!(shifts, addsub, alt((">>", "<<")));
|
||||||
expr_prec_layer!(addsub, concat, 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>(
|
fn concat_expr<'a>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'a, Option<WithSpan<'a, Box<Expr<'a>>>>> {
|
) -> ParseResult<'a, Option<WithSpan<'a, Box<Expr<'a>>>>> {
|
||||||
let ws1 = |i: &mut _| opt(skip_ws1).parse_next(i);
|
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)?;
|
let data = opt((ws1, '~', ws1, |i: &mut _| Expr::muldivmod(i, level))).parse_next(i)?;
|
||||||
if let Some((t1, _, t2, expr)) = data {
|
if let Some((t1, _, t2, expr)) = data {
|
||||||
if t1.is_none() || t2.is_none() {
|
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 expr = Self::muldivmod(i, level)?;
|
||||||
let expr2 = concat_expr(i, level)?;
|
let expr2 = concat_expr(i, level)?;
|
||||||
if let Some(expr2) = expr2 {
|
if let Some(expr2) = expr2 {
|
||||||
@ -435,8 +447,11 @@ impl<'a> Expr<'a> {
|
|||||||
|
|
||||||
expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%")));
|
expr_prec_layer!(muldivmod, is_as, alt(("*", "/", "%")));
|
||||||
|
|
||||||
fn is_as(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn is_as(
|
||||||
let start = *i;
|
i: &mut InputStream<'a>,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
|
let start = ***i;
|
||||||
let lhs = Self::filtered(i, level)?;
|
let lhs = Self::filtered(i, level)?;
|
||||||
let before_keyword = *i;
|
let before_keyword = *i;
|
||||||
let rhs = opt(ws(identifier)).parse_next(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))
|
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 res = Self::prefix(i, level)?;
|
||||||
|
|
||||||
let mut level_guard = level.guard();
|
let mut level_guard = level.guard();
|
||||||
let mut start = *i;
|
let mut start = ***i;
|
||||||
while let Some((mut filter, i_before)) =
|
while let Some((mut filter, i_before)) =
|
||||||
opt(ws((|i: &mut _| filter(i, level)).with_taken())).parse_next(i)?
|
opt(ws((|i: &mut _| filter(i, level)).with_taken())).parse_next(i)?
|
||||||
{
|
{
|
||||||
level_guard.nest(i_before)?;
|
level_guard.nest(i_before)?;
|
||||||
filter.arguments.insert(0, res);
|
filter.arguments.insert(0, res);
|
||||||
res = WithSpan::new(Box::new(Self::Filter(filter)), start.trim_start(), i);
|
res = WithSpan::new(Box::new(Self::Filter(filter)), start.trim_start(), i);
|
||||||
start = *i;
|
start = ***i;
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prefix(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn prefix(
|
||||||
let start = *i;
|
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
|
// This is a rare place where we create recursion in the parsed AST
|
||||||
// without recursing the parser call stack. However, this can lead
|
// without recursing the parser call stack. However, this can lead
|
||||||
@ -534,7 +555,10 @@ impl<'a> Expr<'a> {
|
|||||||
Ok(expr)
|
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((
|
alt((
|
||||||
Self::num,
|
Self::num,
|
||||||
Self::str,
|
Self::str,
|
||||||
@ -546,8 +570,11 @@ impl<'a> Expr<'a> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn group(
|
||||||
let start = *i;
|
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 expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?;
|
||||||
let Some(expr) = expr else {
|
let Some(expr) = expr else {
|
||||||
let _ = ')'.parse_next(i)?;
|
let _ = ')'.parse_next(i)?;
|
||||||
@ -576,8 +603,11 @@ impl<'a> Expr<'a> {
|
|||||||
Ok(WithSpan::new(Box::new(Self::Tuple(exprs)), start, i))
|
Ok(WithSpan::new(Box::new(Self::Tuple(exprs)), start, i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn array(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn array(
|
||||||
let start = *i;
|
i: &mut InputStream<'a>,
|
||||||
|
level: Level<'_>,
|
||||||
|
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
|
let start = ***i;
|
||||||
let array = preceded(
|
let array = preceded(
|
||||||
ws('['),
|
ws('['),
|
||||||
cut_err(terminated(
|
cut_err(terminated(
|
||||||
@ -597,10 +627,10 @@ impl<'a> Expr<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path_var_bool(
|
fn path_var_bool(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let ret = match path_or_identifier(i, level)? {
|
let ret = match path_or_identifier(i, level)? {
|
||||||
PathOrIdentifier::Path(v) => Box::new(Self::Path(v)),
|
PathOrIdentifier::Path(v) => Box::new(Self::Path(v)),
|
||||||
PathOrIdentifier::Identifier("true") => Box::new(Self::BoolLit(true)),
|
PathOrIdentifier::Identifier("true") => Box::new(Self::BoolLit(true)),
|
||||||
@ -610,20 +640,20 @@ impl<'a> Expr<'a> {
|
|||||||
Ok(WithSpan::new(ret, start, i))
|
Ok(WithSpan::new(ret, start, i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn str(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let s = str_lit.parse_next(i)?;
|
let s = str_lit.parse_next(i)?;
|
||||||
Ok(WithSpan::new(Box::new(Self::StrLit(s)), start, i))
|
Ok(WithSpan::new(Box::new(Self::StrLit(s)), start, i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn num(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn num(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (num, full) = num_lit.with_taken().parse_next(i)?;
|
let (num, full) = num_lit.with_taken().parse_next(i)?;
|
||||||
Ok(WithSpan::new(Box::new(Expr::NumLit(full, num)), start, i))
|
Ok(WithSpan::new(Box::new(Expr::NumLit(full, num)), start, i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn char(i: &mut &'a str) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
fn char(i: &mut InputStream<'a>) -> ParseResult<'a, WithSpan<'a, Box<Self>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let c = char_lit.parse_next(i)?;
|
let c = char_lit.parse_next(i)?;
|
||||||
Ok(WithSpan::new(Box::new(Self::CharLit(c)), start, 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)?;
|
let good = alt((keyword("xor").value(true), '^'.value(false))).parse_next(i)?;
|
||||||
if good {
|
if good {
|
||||||
Ok("^")
|
Ok("^")
|
||||||
} else {
|
} 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)?;
|
let good = alt((keyword("bitand").value(true), ('&', not('&')).value(false))).parse_next(i)?;
|
||||||
if good {
|
if good {
|
||||||
Ok("&")
|
Ok("&")
|
||||||
} else {
|
} 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> {
|
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) = (
|
let (name, arguments) = (
|
||||||
ws(|i: &mut _| path_or_identifier(i, level)),
|
ws(|i: &mut _| path_or_identifier(i, level)),
|
||||||
opt(|i: &mut _| Expr::arguments(i, level)),
|
opt(|i: &mut _| Expr::arguments(i, level)),
|
||||||
@ -717,8 +747,11 @@ enum Suffix<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Suffix<'a> {
|
impl<'a> Suffix<'a> {
|
||||||
fn parse(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Box<Expr<'a>>>> {
|
fn parse(
|
||||||
let i_start = *i;
|
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 level_guard = level.guard();
|
||||||
let mut expr = Expr::single(i, level)?;
|
let mut expr = Expr::single(i, level)?;
|
||||||
let mut right = alt((
|
let mut right = alt((
|
||||||
@ -728,9 +761,11 @@ impl<'a> Suffix<'a> {
|
|||||||
Self::r#try,
|
Self::r#try,
|
||||||
Self::r#macro,
|
Self::r#macro,
|
||||||
));
|
));
|
||||||
let start = *i;
|
|
||||||
while let Some((suffix, i_before)) = opt(right.by_ref().with_taken()).parse_next(i)? {
|
let start = ***i;
|
||||||
level_guard.nest(i_before)?;
|
let mut i_before = i.checkpoint();
|
||||||
|
while let Some(suffix) = opt(right.by_ref()).parse_next(i)? {
|
||||||
|
level_guard.nest(i)?;
|
||||||
match suffix {
|
match suffix {
|
||||||
Self::AssociatedItem(associated_item) => {
|
Self::AssociatedItem(associated_item) => {
|
||||||
expr = WithSpan::new(
|
expr = WithSpan::new(
|
||||||
@ -770,15 +805,17 @@ impl<'a> Suffix<'a> {
|
|||||||
expr = WithSpan::new(Box::new(Expr::RustMacro(vec![name], args)), start, i)
|
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)
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum Token {
|
enum Token {
|
||||||
SomeOther,
|
SomeOther,
|
||||||
@ -803,8 +840,11 @@ impl<'a> Suffix<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn macro_arguments<'a>(i: &mut &'a str, open_token: Group) -> ParseResult<'a, Suffix<'a>> {
|
fn macro_arguments<'a>(
|
||||||
let start = *i;
|
i: &mut InputStream<'a>,
|
||||||
|
open_token: Group,
|
||||||
|
) -> ParseResult<'a, Suffix<'a>> {
|
||||||
|
let start = ***i;
|
||||||
let mut open_list: Vec<Group> = vec![open_token];
|
let mut open_list: Vec<Group> = vec![open_token];
|
||||||
loop {
|
loop {
|
||||||
let before = *i;
|
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>
|
// <https://doc.rust-lang.org/reference/tokens.html>
|
||||||
let some_other = alt((
|
let some_other = alt((
|
||||||
// literals
|
// literals
|
||||||
@ -858,8 +898,8 @@ impl<'a> Suffix<'a> {
|
|||||||
alt((open.map(Token::Open), close.map(Token::Close), some_other)).parse_next(i)
|
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 line_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||||
fn inner<'a>(i: &mut &'a str) -> ParseResult<'a, bool> {
|
fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> {
|
||||||
let start = "//".parse_next(i)?;
|
let start = "//".parse_next(i)?;
|
||||||
let is_doc_comment = alt((
|
let is_doc_comment = alt((
|
||||||
('/', not(peek('/'))).value(true),
|
('/', not(peek('/'))).value(true),
|
||||||
@ -882,8 +922,8 @@ impl<'a> Suffix<'a> {
|
|||||||
doc_comment_no_bare_cr(i, inner)
|
doc_comment_no_bare_cr(i, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_comment<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
fn block_comment<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||||
fn inner<'a>(i: &mut &'a str) -> ParseResult<'a, bool> {
|
fn inner<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, bool> {
|
||||||
let start = "/*".parse_next(i)?;
|
let start = "/*".parse_next(i)?;
|
||||||
let is_doc_comment = alt((
|
let is_doc_comment = alt((
|
||||||
('*', not(peek(one_of(['*', '/'])))).value(true),
|
('*', not(peek(one_of(['*', '/'])))).value(true),
|
||||||
@ -916,7 +956,7 @@ impl<'a> Suffix<'a> {
|
|||||||
doc_comment_no_bare_cr(i, inner)
|
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>
|
// <https://doc.rust-lang.org/reference/tokens.html#r-lex.token.literal.str-raw.syntax>
|
||||||
|
|
||||||
let prefix = identifier.parse_next(i)?;
|
let prefix = identifier.parse_next(i)?;
|
||||||
@ -948,10 +988,12 @@ impl<'a> Suffix<'a> {
|
|||||||
if opt('"').parse_next(i)?.is_some() {
|
if opt('"').parse_next(i)?.is_some() {
|
||||||
// got a raw string
|
// 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);
|
return cut_error!("unterminated raw string", prefix);
|
||||||
};
|
};
|
||||||
*i = j;
|
|
||||||
|
|
||||||
if inner.split('\r').skip(1).any(|s| !s.starts_with('\n')) {
|
if inner.split('\r').skip(1).any(|s| !s.starts_with('\n')) {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
@ -1021,8 +1063,8 @@ impl<'a> Suffix<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash<'a>(i: &mut &'a str) -> ParseResult<'a, Token> {
|
fn hash<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Token> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
'#'.parse_next(i)?;
|
'#'.parse_next(i)?;
|
||||||
if opt('"').parse_next(i)?.is_some() {
|
if opt('"').parse_next(i)?.is_some() {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
@ -1033,7 +1075,7 @@ impl<'a> Suffix<'a> {
|
|||||||
Ok(Token::SomeOther)
|
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>
|
// <https://doc.rust-lang.org/reference/tokens.html#punctuation>
|
||||||
// hash '#' omitted
|
// hash '#' omitted
|
||||||
|
|
||||||
@ -1045,29 +1087,39 @@ impl<'a> Suffix<'a> {
|
|||||||
];
|
];
|
||||||
const THREE_CHARS: &[[u8; 3]] = &[*b"<<=", *b">>=", *b"...", *b"..="];
|
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
|
// need to check long to short
|
||||||
*i = if let Some((head, tail)) = i.split_at_checked(3)
|
alt((three_chars, two_chars, one_char)).parse_next(i)
|
||||||
&& 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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open<'a>(i: &mut &'a str) -> ParseResult<'a, Group> {
|
fn open<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> {
|
||||||
alt((
|
alt((
|
||||||
'('.value(Group::Paren),
|
'('.value(Group::Paren),
|
||||||
'{'.value(Group::Brace),
|
'{'.value(Group::Brace),
|
||||||
@ -1076,7 +1128,7 @@ impl<'a> Suffix<'a> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close<'a>(i: &mut &'a str) -> ParseResult<'a, Group> {
|
fn close<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, Group> {
|
||||||
alt((
|
alt((
|
||||||
')'.value(Group::Paren),
|
')'.value(Group::Paren),
|
||||||
'}'.value(Group::Brace),
|
'}'.value(Group::Brace),
|
||||||
@ -1089,7 +1141,7 @@ impl<'a> Suffix<'a> {
|
|||||||
(|i: &mut _| macro_arguments(i, open_token)).parse_next(i)
|
(|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(
|
preceded(
|
||||||
ws(('.', not('.'))),
|
ws(('.', not('.'))),
|
||||||
cut_err((
|
cut_err((
|
||||||
@ -1113,7 +1165,7 @@ impl<'a> Suffix<'a> {
|
|||||||
.parse_next(i)
|
.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(
|
preceded(
|
||||||
ws('['),
|
ws('['),
|
||||||
cut_err(terminated(
|
cut_err(terminated(
|
||||||
@ -1125,7 +1177,7 @@ impl<'a> Suffix<'a> {
|
|||||||
.parse_next(i)
|
.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 _| {
|
(opt(|i: &mut _| call_generics(i, level)), |i: &mut _| {
|
||||||
Expr::arguments(i, level)
|
Expr::arguments(i, level)
|
||||||
})
|
})
|
||||||
@ -1133,14 +1185,14 @@ impl<'a> Suffix<'a> {
|
|||||||
.parse_next(i)
|
.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)
|
preceded(skip_ws0, '?').map(|_| Self::Try).parse_next(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doc_comment_no_bare_cr<'a>(
|
fn doc_comment_no_bare_cr<'a>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
inner: fn(i: &mut &'a str) -> ParseResult<'a, bool>,
|
inner: fn(i: &mut InputStream<'a>) -> ParseResult<'a, bool>,
|
||||||
) -> ParseResult<'a, ()> {
|
) -> ParseResult<'a, ()> {
|
||||||
let (is_doc_comment, comment) = inner.with_taken().parse_next(i)?;
|
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')) {
|
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> {
|
impl<'i> TyGenerics<'i> {
|
||||||
fn parse(i: &mut &'i str, level: Level<'_>) -> ParseResult<'i, WithSpan<'i, Self>> {
|
fn parse(i: &mut InputStream<'i>, level: Level<'_>) -> ParseResult<'i, WithSpan<'i, Self>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (refs, path, args): (_, Vec<_>, _) = (
|
let (refs, path, args): (_, Vec<_>, _) = (
|
||||||
repeat(0.., ws('&')),
|
repeat(0.., ws('&')),
|
||||||
separated(1.., ws(identifier), "::"),
|
separated(1.., ws(identifier), "::"),
|
||||||
@ -1202,7 +1254,7 @@ impl<'i> TyGenerics<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn args(
|
fn args(
|
||||||
i: &mut &'i str,
|
i: &mut InputStream<'i>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||||
ws('<').parse_next(i)?;
|
ws('<').parse_next(i)?;
|
||||||
@ -1219,7 +1271,7 @@ impl<'i> TyGenerics<'i> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn call_generics<'i>(
|
pub(crate) fn call_generics<'i>(
|
||||||
i: &mut &'i str,
|
i: &mut InputStream<'i>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
) -> ParseResult<'i, Vec<WithSpan<'i, TyGenerics<'i>>>> {
|
||||||
preceded(ws("::"), cut_err(|i: &mut _| TyGenerics::args(i, level))).parse_next(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 rustc_hash::FxBuildHasher;
|
||||||
use winnow::ascii::take_escaped;
|
use winnow::ascii::take_escaped;
|
||||||
use winnow::combinator::{
|
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::error::{ErrMode, FromExternalError};
|
||||||
use winnow::stream::AsChar;
|
use winnow::stream::AsChar;
|
||||||
use winnow::token::{any, none_of, one_of, take_while};
|
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};
|
use crate::ascii_str::{AsciiChar, AsciiStr};
|
||||||
pub use crate::expr::{AssociatedItem, Expr, Filter, PathComponent, TyGenerics};
|
pub use crate::expr::{AssociatedItem, Expr, Filter, PathComponent, TyGenerics};
|
||||||
@ -102,6 +102,8 @@ mod _parsed {
|
|||||||
|
|
||||||
pub use _parsed::Parsed;
|
pub use _parsed::Parsed;
|
||||||
|
|
||||||
|
type InputStream<'a> = Stateful<LocatingSlice<&'a str>, ()>;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Ast<'a> {
|
pub struct Ast<'a> {
|
||||||
nodes: Vec<Box<Node<'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
|
/// 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.
|
/// a parsing error occurs, we won't display the path as it wouldn't be useful.
|
||||||
pub fn from_str(
|
pub fn from_str(
|
||||||
mut src: &'a str,
|
src: &'a str,
|
||||||
file_path: Option<Arc<Path>>,
|
file_path: Option<Arc<Path>>,
|
||||||
syntax: &Syntax<'_>,
|
syntax: &Syntax<'_>,
|
||||||
) -> Result<Self, ParseError> {
|
) -> Result<Self, ParseError> {
|
||||||
@ -122,6 +124,10 @@ impl<'a> Ast<'a> {
|
|||||||
loop_depth: Cell::new(0),
|
loop_depth: Cell::new(0),
|
||||||
level: Level(&level),
|
level: Level(&level),
|
||||||
};
|
};
|
||||||
|
let mut src = InputStream {
|
||||||
|
input: LocatingSlice::new(src),
|
||||||
|
state: (),
|
||||||
|
};
|
||||||
match Node::parse_template(&mut src, &state) {
|
match Node::parse_template(&mut src, &state) {
|
||||||
Ok(nodes) if src.is_empty() => Ok(Self { nodes }),
|
Ok(nodes) if src.is_empty() => Ok(Self { nodes }),
|
||||||
Ok(_) | Err(ErrMode::Incomplete(_)) => unreachable!(),
|
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;
|
type Inner = Self;
|
||||||
|
|
||||||
fn from_input(input: &&'a str) -> Self {
|
#[inline]
|
||||||
|
fn from_input(input: &InputStream<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
span: (*input).into(),
|
span: Span(***input),
|
||||||
message: None,
|
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 InputStream<'a>) -> ParseResult<'a, ()> {
|
||||||
fn skip_ws0<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
take_while(0.., |c: char| c.is_ascii_whitespace())
|
||||||
*i = i.trim_ascii_start();
|
.void()
|
||||||
Ok(())
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn skip_ws1<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, ()> {
|
||||||
fn skip_ws1<'a>(i: &mut &'a str) -> ParseResult<'a, ()> {
|
take_while(1.., |c: char| c.is_ascii_whitespace())
|
||||||
let j = i.trim_ascii_start();
|
.void()
|
||||||
if i.len() != j.len() {
|
.parse_next(i)
|
||||||
*i = i.trim_ascii_start();
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
fail.parse_next(i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws<'a, O>(
|
fn ws<'a, O>(
|
||||||
inner: impl ModalParser<&'a str, O, ErrorContext<'a>>,
|
inner: impl ModalParser<InputStream<'a>, O, ErrorContext<'a>>,
|
||||||
) -> impl ModalParser<&'a str, O, ErrorContext<'a>> {
|
) -> impl ModalParser<InputStream<'a>, O, ErrorContext<'a>> {
|
||||||
delimited(skip_ws0, inner, skip_ws0)
|
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)
|
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 head = any.verify(|&c| c == '_' || unicode_ident::is_xid_start(c));
|
||||||
let tail = take_while(.., unicode_ident::is_xid_continue);
|
let tail = take_while(.., unicode_ident::is_xid_continue);
|
||||||
(head, tail).take().parse_next(input)
|
(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)
|
alt((keyword("false"), keyword("true"))).parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,12 +418,12 @@ pub enum Num<'a> {
|
|||||||
Float(&'a str, Option<FloatKind>),
|
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>(
|
fn num_lit_suffix<'a, T: Copy>(
|
||||||
kind: &'a str,
|
kind: &'a str,
|
||||||
list: &[(&str, T)],
|
list: &[(&str, T)],
|
||||||
start: &'a str,
|
start: &'a str,
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
) -> ParseResult<'a, T> {
|
) -> ParseResult<'a, T> {
|
||||||
let suffix = identifier.parse_next(i)?;
|
let suffix = identifier.parse_next(i)?;
|
||||||
if let Some(value) = list
|
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>.
|
// 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 _| {
|
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>:
|
// 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 `+/-`.
|
// 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_dot = opt(('.', separated_digits(10, true))).parse_next(i)?;
|
||||||
let has_exp = opt(|i: &mut _| {
|
let has_exp = opt(|i: &mut _| {
|
||||||
let (kind, op) = (one_of(['e', 'E']), opt(one_of(['+', '-']))).parse_next(i)?;
|
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)?;
|
.parse_next(i)?;
|
||||||
match (has_dot, has_exp) {
|
match (has_dot, has_exp) {
|
||||||
(Some(_), _) | (_, Some(())) => Ok(()),
|
(Some(_), _) | (_, Some(())) => Ok(()),
|
||||||
_ => {
|
_ => fail(i),
|
||||||
*i = start;
|
|
||||||
fail.parse_next(i)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -504,12 +503,9 @@ fn num_lit<'a>(i: &mut &'a str) -> ParseResult<'a, Num<'a>> {
|
|||||||
fn separated_digits<'a>(
|
fn separated_digits<'a>(
|
||||||
radix: u32,
|
radix: u32,
|
||||||
start: bool,
|
start: bool,
|
||||||
) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> {
|
) -> impl ModalParser<InputStream<'a>, &'a str, ErrorContext<'a>> {
|
||||||
(
|
(
|
||||||
move |i: &mut &'a _| match start {
|
cond(!start, repeat(0.., '_').map(|()| ())),
|
||||||
true => Ok(()),
|
|
||||||
false => repeat(0.., '_').parse_next(i),
|
|
||||||
},
|
|
||||||
one_of(move |ch: char| ch.is_digit(radix)),
|
one_of(move |ch: char| ch.is_digit(radix)),
|
||||||
repeat(0.., one_of(move |ch: char| ch == '_' || ch.is_digit(radix))).map(|()| ()),
|
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,
|
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>
|
// <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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum Sequence<'a> {
|
enum Sequence<'a> {
|
||||||
Text(&'a str),
|
Text(&'a str),
|
||||||
@ -572,7 +568,7 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
|||||||
Cr(bool),
|
Cr(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let mut contains_null = false;
|
let mut contains_null = false;
|
||||||
let mut contains_unicode_character = false;
|
let mut contains_unicode_character = false;
|
||||||
let mut contains_unicode_escape = 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(
|
let prefix = terminated(
|
||||||
opt(alt((
|
opt(alt((
|
||||||
@ -697,7 +693,7 @@ fn str_lit<'a>(i: &mut &'a str) -> ParseResult<'a, StrLit<'a>> {
|
|||||||
Ok(lit)
|
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)? {
|
if let Some(suffix) = opt(identifier.take()).parse_next(i)? {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
"you are missing a space to separate two string literals",
|
"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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_lit_without_prefix<'a>(i: &mut &'a str) -> ParseResult<'a> {
|
fn str_lit_without_prefix<'a>(i: &mut InputStream<'a>) -> ParseResult<'a> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let lit = str_lit.parse_next(i)?;
|
let lit = str_lit.parse_next(i)?;
|
||||||
|
|
||||||
let kind = match lit.prefix {
|
let kind = match lit.prefix {
|
||||||
@ -739,42 +735,62 @@ pub struct CharLit<'a> {
|
|||||||
|
|
||||||
// Information about allowed character escapes is available at:
|
// Information about allowed character escapes is available at:
|
||||||
// <https://doc.rust-lang.org/reference/tokens.html#character-literals>.
|
// <https://doc.rust-lang.org/reference/tokens.html#character-literals>.
|
||||||
fn char_lit<'a>(i: &mut &'a str) -> ParseResult<'a, CharLit<'a>> {
|
fn char_lit<'a>(i: &mut InputStream<'a>) -> ParseResult<'a, CharLit<'a>> {
|
||||||
let start = *i;
|
let ((prefix, _, content, is_closed), span) = (
|
||||||
|
|
||||||
let prefix = terminated(
|
|
||||||
alt(('b'.value(Some(CharPrefix::Binary)), empty.value(None))),
|
alt(('b'.value(Some(CharPrefix::Binary)), empty.value(None))),
|
||||||
'\'',
|
'\'',
|
||||||
)
|
|
||||||
.parse_next(i)?;
|
|
||||||
|
|
||||||
let content = opt(terminated(
|
|
||||||
opt(take_escaped(none_of(['\\', '\'']), '\\', any)),
|
opt(take_escaped(none_of(['\\', '\'']), '\\', any)),
|
||||||
'\'',
|
opt('\''),
|
||||||
))
|
)
|
||||||
.parse_next(i)?;
|
.with_taken()
|
||||||
|
.parse_next(i)?;
|
||||||
|
|
||||||
let Some(content) = content else {
|
if is_closed.is_none() {
|
||||||
if let Some(prefix) = prefix {
|
if let Some(prefix) = prefix {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
match prefix {
|
match prefix {
|
||||||
CharPrefix::Binary => "unterminated byte constant",
|
CharPrefix::Binary => "unterminated byte literal",
|
||||||
},
|
},
|
||||||
start,
|
span,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return fail(i);
|
return fail(i);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let content = match content.unwrap_or_default() {
|
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,
|
content => content,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut is = content;
|
let mut content_i = content;
|
||||||
let Ok(c) = Char::parse(&mut is) else {
|
let Ok(c) = Char::parse(&mut content_i) else {
|
||||||
return cut_error!("invalid character", start);
|
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 {
|
let (nb, max_value, err1, err2) = match c {
|
||||||
Char::Literal | Char::Escaped => {
|
Char::Literal | Char::Escaped => {
|
||||||
@ -792,7 +808,7 @@ fn char_lit<'a>(i: &mut &'a str) -> ParseResult<'a, CharLit<'a>> {
|
|||||||
Some(CharPrefix::Binary) => {
|
Some(CharPrefix::Binary) => {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
"cannot use unicode escape in byte string in byte literal",
|
"cannot use unicode escape in byte string in byte literal",
|
||||||
start,
|
span,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None => (
|
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 {
|
let Ok(nb) = u32::from_str_radix(nb, 16) else {
|
||||||
return cut_error!(err1, start);
|
return cut_error!(err1, span);
|
||||||
};
|
};
|
||||||
if nb > max_value {
|
if nb > max_value {
|
||||||
return cut_error!(err2, start);
|
return cut_error!(err2, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CharLit { prefix, content })
|
Ok(CharLit { prefix, content })
|
||||||
@ -830,11 +846,9 @@ enum Char<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Char<'a> {
|
impl<'a> Char<'a> {
|
||||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Self> {
|
fn parse(i: &mut &'a str) -> ModalResult<Self, ()> {
|
||||||
if i.chars().count() == 1 {
|
let unescaped = none_of(('\\', '\'')).value(Self::Literal);
|
||||||
return any.value(Self::Literal).parse_next(i);
|
let escaped = preceded(
|
||||||
}
|
|
||||||
(
|
|
||||||
'\\',
|
'\\',
|
||||||
alt((
|
alt((
|
||||||
'n'.value(Self::Escaped),
|
'n'.value(Self::Escaped),
|
||||||
@ -854,9 +868,8 @@ impl<'a> Char<'a> {
|
|||||||
)
|
)
|
||||||
.map(|(_, s, _)| Self::UnicodeEscape(s)),
|
.map(|(_, s, _)| Self::UnicodeEscape(s)),
|
||||||
)),
|
)),
|
||||||
)
|
);
|
||||||
.map(|(_, ch)| ch)
|
alt((unescaped, escaped)).parse_next(i)
|
||||||
.parse_next(i)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,10 +880,10 @@ pub enum PathOrIdentifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn path_or_identifier<'a>(
|
fn path_or_identifier<'a>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
level: Level<'_>,
|
level: Level<'_>,
|
||||||
) -> ParseResult<'a, PathOrIdentifier<'a>> {
|
) -> ParseResult<'a, PathOrIdentifier<'a>> {
|
||||||
let start_i = *i;
|
let start_i = ***i;
|
||||||
let root = ws(opt("::"));
|
let root = ws(opt("::"));
|
||||||
let tail = opt(repeat(
|
let tail = opt(repeat(
|
||||||
1..,
|
1..,
|
||||||
@ -927,11 +940,11 @@ struct State<'a, 'l> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State<'_, '_> {
|
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)
|
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((
|
let control = alt((
|
||||||
self.syntax.block_end.value(None),
|
self.syntax.block_end.value(None),
|
||||||
peek(delimited('%', alt(('-', '~', '+')).map(Some), '}')),
|
peek(delimited('%', alt(('-', '~', '+')).map(Some), '}')),
|
||||||
@ -945,7 +958,7 @@ impl State<'_, '_> {
|
|||||||
control.escape_default(),
|
control.escape_default(),
|
||||||
self.syntax.block_end.escape_default(),
|
self.syntax.block_end.escape_default(),
|
||||||
),
|
),
|
||||||
*i,
|
***i,
|
||||||
);
|
);
|
||||||
Err(err.backtrack())
|
Err(err.backtrack())
|
||||||
} else {
|
} 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)
|
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)
|
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)
|
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)
|
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(
|
preceded(
|
||||||
('|', not('|')),
|
('|', not('|')),
|
||||||
cut_err(|i: &mut _| Filter::parse(i, level)),
|
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>;
|
type HashSet<T> = std::collections::hash_set::HashSet<T, FxBuildHasher>;
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_strip_common() {
|
fn test_strip_common() {
|
||||||
// Full path is returned instead of empty when the entire path is in 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");
|
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]
|
#[test]
|
||||||
fn test_num_lit() {
|
fn test_num_lit() {
|
||||||
// Should fail.
|
// Should fail.
|
||||||
assert!(num_lit.parse_peek(".").is_err());
|
assert!(parse_peek(num_lit, ".").is_err());
|
||||||
// Should succeed.
|
// Should succeed.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1.2E-02").unwrap(),
|
parse_peek(num_lit, "1.2E-02").unwrap(),
|
||||||
("", Num::Float("1.2E-02", None))
|
("", Num::Float("1.2E-02", None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("4e3").unwrap(),
|
parse_peek(num_lit, "4e3").unwrap(),
|
||||||
("", Num::Float("4e3", None)),
|
("", Num::Float("4e3", None)),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("4e+_3").unwrap(),
|
parse_peek(num_lit, "4e+_3").unwrap(),
|
||||||
("", Num::Float("4e+_3", None)),
|
("", Num::Float("4e+_3", None)),
|
||||||
);
|
);
|
||||||
// Not supported because Rust wants a number before the `.`.
|
// Not supported because Rust wants a number before the `.`.
|
||||||
assert!(num_lit.parse_peek(".1").is_err());
|
assert!(parse_peek(num_lit, ".1").is_err());
|
||||||
assert!(num_lit.parse_peek(".1E-02").is_err());
|
assert!(parse_peek(num_lit, ".1E-02").is_err());
|
||||||
// A `_` directly after the `.` denotes a field.
|
// A `_` directly after the `.` denotes a field.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1._0").unwrap(),
|
parse_peek(num_lit, "1._0").unwrap(),
|
||||||
("._0", Num::Int("1", None))
|
("._0", Num::Int("1", None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1_.0").unwrap(),
|
parse_peek(num_lit, "1_.0").unwrap(),
|
||||||
("", Num::Float("1_.0", None))
|
("", Num::Float("1_.0", None))
|
||||||
);
|
);
|
||||||
// Not supported (voluntarily because of `1..` syntax).
|
// Not supported (voluntarily because of `1..` syntax).
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1.").unwrap(),
|
parse_peek(num_lit, "1.").unwrap(),
|
||||||
(".", Num::Int("1", None))
|
(".", Num::Int("1", None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1_.").unwrap(),
|
parse_peek(num_lit, "1_.").unwrap(),
|
||||||
(".", Num::Int("1_", None))
|
(".", Num::Int("1_", None))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("1_2.").unwrap(),
|
parse_peek(num_lit, "1_2.").unwrap(),
|
||||||
(".", Num::Int("1_2", None))
|
(".", Num::Int("1_2", None))
|
||||||
);
|
);
|
||||||
// Numbers with suffixes
|
// Numbers with suffixes
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("-1usize").unwrap(),
|
parse_peek(num_lit, "-1usize").unwrap(),
|
||||||
("", Num::Int("-1", Some(IntKind::Usize)))
|
("", Num::Int("-1", Some(IntKind::Usize)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("123_f32").unwrap(),
|
parse_peek(num_lit, "123_f32").unwrap(),
|
||||||
("", Num::Float("123_", Some(FloatKind::F32)))
|
("", Num::Float("123_", Some(FloatKind::F32)))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
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",
|
"|into_isize",
|
||||||
Num::Float("1_.2_e+_3_", Some(FloatKind::F64))
|
Num::Float("1_.2_e+_3_", Some(FloatKind::F64))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_lit.parse_peek("4e3f128").unwrap(),
|
parse_peek(num_lit, "4e3f128").unwrap(),
|
||||||
("", Num::Float("4e3", Some(FloatKind::F128))),
|
("", Num::Float("4e3", Some(FloatKind::F128))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1570,42 +1595,42 @@ mod test {
|
|||||||
content: s,
|
content: s,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(char_lit.parse_peek("'a'").unwrap(), ("", lit("a")));
|
assert_eq!(parse_peek(char_lit, "'a'").unwrap(), ("", lit("a")));
|
||||||
assert_eq!(char_lit.parse_peek("'字'").unwrap(), ("", lit("字")));
|
assert_eq!(parse_peek(char_lit, "'字'").unwrap(), ("", lit("字")));
|
||||||
|
|
||||||
// Escaped single characters.
|
// Escaped single characters.
|
||||||
assert_eq!(char_lit.parse_peek("'\\\"'").unwrap(), ("", lit("\\\"")));
|
assert_eq!(parse_peek(char_lit, "'\\\"'").unwrap(), ("", lit("\\\"")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\''").unwrap(), ("", lit("\\'")));
|
assert_eq!(parse_peek(char_lit, "'\\''").unwrap(), ("", lit("\\'")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\t'").unwrap(), ("", lit("\\t")));
|
assert_eq!(parse_peek(char_lit, "'\\t'").unwrap(), ("", lit("\\t")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\n'").unwrap(), ("", lit("\\n")));
|
assert_eq!(parse_peek(char_lit, "'\\n'").unwrap(), ("", lit("\\n")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\r'").unwrap(), ("", lit("\\r")));
|
assert_eq!(parse_peek(char_lit, "'\\r'").unwrap(), ("", lit("\\r")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\0'").unwrap(), ("", lit("\\0")));
|
assert_eq!(parse_peek(char_lit, "'\\0'").unwrap(), ("", lit("\\0")));
|
||||||
// Escaped ascii characters (up to `0x7F`).
|
// Escaped ascii characters (up to `0x7F`).
|
||||||
assert_eq!(char_lit.parse_peek("'\\x12'").unwrap(), ("", lit("\\x12")));
|
assert_eq!(parse_peek(char_lit, "'\\x12'").unwrap(), ("", lit("\\x12")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\x02'").unwrap(), ("", lit("\\x02")));
|
assert_eq!(parse_peek(char_lit, "'\\x02'").unwrap(), ("", lit("\\x02")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\x6a'").unwrap(), ("", lit("\\x6a")));
|
assert_eq!(parse_peek(char_lit, "'\\x6a'").unwrap(), ("", lit("\\x6a")));
|
||||||
assert_eq!(char_lit.parse_peek("'\\x7F'").unwrap(), ("", lit("\\x7F")));
|
assert_eq!(parse_peek(char_lit, "'\\x7F'").unwrap(), ("", lit("\\x7F")));
|
||||||
// Escaped unicode characters (up to `0x10FFFF`).
|
// Escaped unicode characters (up to `0x10FFFF`).
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
char_lit.parse_peek("'\\u{A}'").unwrap(),
|
parse_peek(char_lit, "'\\u{A}'").unwrap(),
|
||||||
("", lit("\\u{A}"))
|
("", lit("\\u{A}"))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
char_lit.parse_peek("'\\u{10}'").unwrap(),
|
parse_peek(char_lit, "'\\u{10}'").unwrap(),
|
||||||
("", lit("\\u{10}"))
|
("", lit("\\u{10}"))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
char_lit.parse_peek("'\\u{aa}'").unwrap(),
|
parse_peek(char_lit, "'\\u{aa}'").unwrap(),
|
||||||
("", lit("\\u{aa}"))
|
("", lit("\\u{aa}"))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
char_lit.parse_peek("'\\u{10FFFF}'").unwrap(),
|
parse_peek(char_lit, "'\\u{10FFFF}'").unwrap(),
|
||||||
("", lit("\\u{10FFFF}"))
|
("", lit("\\u{10FFFF}"))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check with `b` prefix.
|
// Check with `b` prefix.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
char_lit.parse_peek("b'a'").unwrap(),
|
parse_peek(char_lit, "b'a'").unwrap(),
|
||||||
(
|
(
|
||||||
"",
|
"",
|
||||||
crate::CharLit {
|
crate::CharLit {
|
||||||
@ -1616,20 +1641,20 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Should fail.
|
// Should fail.
|
||||||
assert!(char_lit.parse_peek("''").is_err());
|
assert!(parse_peek(char_lit, "''").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\o'").is_err());
|
assert!(parse_peek(char_lit, "'\\o'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\x'").is_err());
|
assert!(parse_peek(char_lit, "'\\x'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\x1'").is_err());
|
assert!(parse_peek(char_lit, "'\\x1'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\x80'").is_err());
|
assert!(parse_peek(char_lit, "'\\x80'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\u'").is_err());
|
assert!(parse_peek(char_lit, "'\\u'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\u{}'").is_err());
|
assert!(parse_peek(char_lit, "'\\u{}'").is_err());
|
||||||
assert!(char_lit.parse_peek("'\\u{110000}'").is_err());
|
assert!(parse_peek(char_lit, "'\\u{110000}'").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_str_lit() {
|
fn test_str_lit() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
str_lit.parse_peek(r#"b"hello""#).unwrap(),
|
parse_peek(str_lit, r#"b"hello""#).unwrap(),
|
||||||
(
|
(
|
||||||
"",
|
"",
|
||||||
StrLit {
|
StrLit {
|
||||||
@ -1643,7 +1668,7 @@ mod test {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
str_lit.parse_peek(r#"c"hello""#).unwrap(),
|
parse_peek(str_lit, r#"c"hello""#).unwrap(),
|
||||||
(
|
(
|
||||||
"",
|
"",
|
||||||
StrLit {
|
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]
|
#[test]
|
||||||
|
@ -5,13 +5,13 @@ use winnow::combinator::{
|
|||||||
separated_pair, terminated,
|
separated_pair, terminated,
|
||||||
};
|
};
|
||||||
use winnow::error::ErrMode;
|
use winnow::error::ErrMode;
|
||||||
use winnow::stream::Stream as _;
|
use winnow::stream::Stream;
|
||||||
use winnow::token::{any, rest, take_until};
|
use winnow::token::{any, rest, take, take_until};
|
||||||
use winnow::{ModalParser, Parser};
|
use winnow::{ModalParser, Parser};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ErrorContext, Expr, Filter, HashSet, ParseResult, Span, State, Target, WithSpan, cut_error,
|
ErrorContext, Expr, Filter, HashSet, InputStream, ParseResult, Span, State, Target, WithSpan,
|
||||||
filter, identifier, is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws,
|
cut_error, filter, identifier, is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -37,38 +37,28 @@ pub enum Node<'a> {
|
|||||||
|
|
||||||
impl<'a> Node<'a> {
|
impl<'a> Node<'a> {
|
||||||
pub(super) fn parse_template(
|
pub(super) fn parse_template(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
s: &State<'_, '_>,
|
s: &State<'_, '_>,
|
||||||
) -> ParseResult<'a, Vec<Box<Self>>> {
|
) -> ParseResult<'a, Vec<Box<Self>>> {
|
||||||
let start = *i;
|
let nodes = parse_with_unexpected_fallback(
|
||||||
let result = match (|i: &mut _| Self::many(i, s)).parse_next(i) {
|
|i: &mut _| Self::many(i, s),
|
||||||
Ok(result) => result,
|
|i: &mut _| unexpected_tag(i, s),
|
||||||
Err(err) => {
|
)
|
||||||
if let ErrMode::Backtrack(err) | ErrMode::Cut(err) = &err
|
.parse_next(i)?;
|
||||||
&& 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
opt(|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 !i.is_empty() {
|
||||||
if is_eof.is_none() {
|
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
"cannot parse entire template\n\
|
"cannot parse entire template\n\
|
||||||
you should never encounter this error\n\
|
you should never encounter this error\n\
|
||||||
please report this error to <https://github.com/askama-rs/askama/issues>",
|
please report this error to <https://github.com/askama-rs/askama/issues>",
|
||||||
*i,
|
***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(
|
repeat(
|
||||||
0..,
|
0..,
|
||||||
alt((
|
alt((
|
||||||
@ -82,13 +72,13 @@ impl<'a> Node<'a> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||||
let mut start = *i;
|
let start = i.checkpoint();
|
||||||
let tag = preceded(
|
let (span, tag) = (
|
||||||
|i: &mut _| s.tag_block_start(i),
|
s.syntax.block_start,
|
||||||
peek(preceded((opt(Whitespace::parse), skip_ws0), identifier)),
|
peek(preceded((opt(Whitespace::parse), skip_ws0), identifier)),
|
||||||
)
|
)
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
|
|
||||||
let func = match tag {
|
let func = match tag {
|
||||||
"call" => Call::parse,
|
"call" => Call::parse,
|
||||||
@ -96,16 +86,19 @@ impl<'a> Node<'a> {
|
|||||||
"if" => If::parse,
|
"if" => If::parse,
|
||||||
"for" => Loop::parse,
|
"for" => Loop::parse,
|
||||||
"match" => Match::parse,
|
"match" => Match::parse,
|
||||||
"extends" => |i: &mut &'a str, _s: &State<'_, '_>| Extends::parse(i),
|
"extends" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Extends::parse(i),
|
||||||
"include" => |i: &mut &'a str, _s: &State<'_, '_>| Include::parse(i),
|
"include" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Include::parse(i),
|
||||||
"import" => |i: &mut &'a str, _s: &State<'_, '_>| Import::parse(i),
|
"import" => |i: &mut InputStream<'a>, _s: &State<'_, '_>| Import::parse(i),
|
||||||
"block" => BlockDef::parse,
|
"block" => BlockDef::parse,
|
||||||
"macro" => Macro::parse,
|
"macro" => Macro::parse,
|
||||||
"raw" => Raw::parse,
|
"raw" => Raw::parse,
|
||||||
"break" => Self::r#break,
|
"break" => Self::r#break,
|
||||||
"continue" => Self::r#continue,
|
"continue" => Self::r#continue,
|
||||||
"filter" => FilterBlock::parse,
|
"filter" => FilterBlock::parse,
|
||||||
_ => return fail.parse_next(&mut start),
|
_ => {
|
||||||
|
i.reset(&start);
|
||||||
|
return fail.parse_next(i);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _level_guard = s.level.nest(i)?;
|
let _level_guard = s.level.nest(i)?;
|
||||||
@ -120,18 +113,18 @@ impl<'a> Node<'a> {
|
|||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
match closed {
|
match closed {
|
||||||
true => Ok(node),
|
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 = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("break")),
|
ws(keyword("break")),
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
);
|
);
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (pws, _, nws) = p.parse_next(i)?;
|
let (pws, _, nws) = p.parse_next(i)?;
|
||||||
if !s.is_in_loop() {
|
if !s.is_in_loop() {
|
||||||
return cut_error!("you can only `break` inside a `for` loop", start);
|
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))))
|
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 = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("continue")),
|
ws(keyword("continue")),
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
);
|
);
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (pws, _, nws) = p.parse_next(i)?;
|
let (pws, _, nws) = p.parse_next(i)?;
|
||||||
if !s.is_in_loop() {
|
if !s.is_in_loop() {
|
||||||
return cut_error!("you can only `continue` inside a `for` loop", start);
|
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>> {
|
fn expr(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Box<Self>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let level = s.level;
|
let level = s.level;
|
||||||
let (pws, expr) = preceded(
|
let (pws, expr) = preceded(
|
||||||
|i: &mut _| s.tag_expr_start(i),
|
|i: &mut _| s.tag_expr_start(i),
|
||||||
@ -214,27 +207,53 @@ impl<'a> Node<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cut_node<'a, O>(
|
#[inline]
|
||||||
kind: Option<&'static str>,
|
fn parse_with_unexpected_fallback<'a, O>(
|
||||||
inner: impl ModalParser<&'a str, O, ErrorContext<'a>>,
|
mut parser: impl ModalParser<InputStream<'a>, O, ErrorContext<'a>>,
|
||||||
) -> impl ModalParser<&'a str, O, ErrorContext<'a>> {
|
mut unexpected_parser: impl FnMut(&mut InputStream<'a>) -> ParseResult<'a, ()>,
|
||||||
let mut inner = cut_err(inner);
|
) -> impl ModalParser<InputStream<'a>, O, ErrorContext<'a>> {
|
||||||
move |i: &mut &'a str| {
|
#[cold]
|
||||||
let start = *i;
|
#[inline(never)]
|
||||||
let result = inner.parse_next(i);
|
fn try_assign_fallback_error<'a>(
|
||||||
if let Err(ErrMode::Cut(err) | ErrMode::Backtrack(err)) = &result
|
i: &mut InputStream<'a>,
|
||||||
&& err.message.is_none()
|
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;
|
let err_checkpoint = i.checkpoint();
|
||||||
if let Some(mut span) = err.span.as_suffix_of(i) {
|
i.reset(&start_checkpoint);
|
||||||
opt(|i: &mut _| unexpected_raw_tag(kind, i)).parse_next(&mut span)?;
|
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
|
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),
|
|i: &mut _| s.tag_block_start(i),
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
@ -244,7 +263,10 @@ fn unexpected_tag<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()>
|
|||||||
.parse_next(i)
|
.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 tag = peek(ws(identifier)).parse_next(i)?;
|
||||||
let msg = match tag {
|
let msg = match tag {
|
||||||
"end" | "elif" | "else" | "when" => match kind {
|
"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 if tag.starts_with("end") => format!("unexpected closing tag `{tag}`"),
|
||||||
tag => format!("unknown node `{tag}`"),
|
tag => format!("unknown node `{tag}`"),
|
||||||
};
|
};
|
||||||
cut_error!(msg, *i)
|
cut_error!(msg, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -267,7 +289,7 @@ pub struct When<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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 = (
|
let mut p = (
|
||||||
|i: &mut _| s.tag_block_start(i),
|
|i: &mut _| s.tag_block_start(i),
|
||||||
opt(Whitespace::parse),
|
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)?;
|
let (_, pws, _, (nws, _, nodes)) = p.parse_next(i)?;
|
||||||
Ok(WithSpan::new(
|
Ok(WithSpan::new(
|
||||||
Self {
|
Self {
|
||||||
@ -296,9 +318,9 @@ impl<'a> When<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::self_named_constructors)]
|
#[allow(clippy::self_named_constructors)]
|
||||||
fn when(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
fn when(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let endwhen = ws((
|
let endwhen = ws(terminated(
|
||||||
delimited(
|
delimited(
|
||||||
|i: &mut _| s.tag_block_start(i),
|
|i: &mut _| s.tag_block_start(i),
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
@ -312,19 +334,19 @@ impl<'a> When<'a> {
|
|||||||
repeat(0.., ws(|i: &mut _| Comment::parse(i, s))).map(|()| ()),
|
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
|
// 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
|
// 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
|
// `endmatch`. Any whitespaces after `endwhen` are to be suppressed. Actually, they
|
||||||
// don't wind up in the AST anyway.
|
// don't wind up in the AST anyway.
|
||||||
Box::new(Node::Comment(WithSpan::new(
|
Box::new(Node::Comment(WithSpan::new_with_full(
|
||||||
Comment {
|
Comment {
|
||||||
ws: Ws(pws, Some(Whitespace::Suppress)),
|
ws: Ws(pws, Some(Whitespace::Suppress)),
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
start,
|
span,
|
||||||
i,
|
|
||||||
)))
|
)))
|
||||||
});
|
});
|
||||||
let mut p = (
|
let mut p = (
|
||||||
@ -366,8 +388,8 @@ pub struct Cond<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Cond<'a> {
|
impl<'a> Cond<'a> {
|
||||||
fn parse(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
fn parse(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (_, pws, cond, nws, _, nodes) = (
|
let (_, pws, cond, nws, _, nodes) = (
|
||||||
|i: &mut _| s.tag_block_start(i),
|
|i: &mut _| s.tag_block_start(i),
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
@ -405,7 +427,7 @@ pub struct CondTest<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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(
|
preceded(
|
||||||
ws(keyword("if")),
|
ws(keyword("if")),
|
||||||
cut_node(Some("if"), |i: &mut _| Self::parse_cond(i, s)),
|
cut_node(Some("if"), |i: &mut _| Self::parse_cond(i, s)),
|
||||||
@ -413,22 +435,32 @@ impl<'a> CondTest<'a> {
|
|||||||
.parse_next(i)
|
.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) = (
|
let (target, expr) = (
|
||||||
opt(delimited(
|
opt(delimited(
|
||||||
ws(alt((keyword("let"), keyword("set")))),
|
ws(alt((keyword("let"), keyword("set")))),
|
||||||
ws(|i: &mut _| Target::parse(i, s)),
|
ws(|i: &mut _| Target::parse(i, s)),
|
||||||
ws('='),
|
ws('='),
|
||||||
)),
|
)),
|
||||||
ws(|i: &mut _| {
|
ws(|i: &mut InputStream<'a>| {
|
||||||
let start = *i;
|
let checkpoint = i.checkpoint();
|
||||||
|
let start = ***i;
|
||||||
|
|
||||||
let mut expr = Expr::parse(i, s.level, false)?;
|
let mut expr = Expr::parse(i, s.level, false)?;
|
||||||
if let Expr::BinOp(v) = &mut *expr.inner
|
if let Expr::BinOp(v) = &mut *expr.inner
|
||||||
&& matches!(*v.rhs.inner, Expr::Var("set" | "let"))
|
&& matches!(*v.rhs.inner, Expr::Var("set" | "let"))
|
||||||
{
|
{
|
||||||
let _level_guard = s.level.nest(i)?;
|
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)?;
|
let new_right = Self::parse_cond(i, s)?;
|
||||||
v.rhs.inner = Box::new(Expr::LetCond(WithSpan::new(new_right, start_span, i)));
|
v.rhs.inner = Box::new(Expr::LetCond(WithSpan::new(new_right, start_span, i)));
|
||||||
}
|
}
|
||||||
@ -456,7 +488,7 @@ pub enum Whitespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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)
|
any.verify_map(Self::parse_char).parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +516,7 @@ impl FromStr for Whitespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_block_start<'a>(
|
fn check_block_start<'a>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
start: &'a str,
|
start: &'a str,
|
||||||
s: &State<'_, '_>,
|
s: &State<'_, '_>,
|
||||||
node: &str,
|
node: &str,
|
||||||
@ -512,15 +544,18 @@ pub struct Loop<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Loop<'a> {
|
impl<'a> Loop<'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>>> {
|
||||||
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Vec<Box<Node<'a>>>> {
|
fn content<'a>(
|
||||||
|
i: &mut InputStream<'a>,
|
||||||
|
s: &State<'_, '_>,
|
||||||
|
) -> ParseResult<'a, Vec<Box<Node<'a>>>> {
|
||||||
s.enter_loop();
|
s.enter_loop();
|
||||||
let result = (|i: &mut _| Node::many(i, s)).parse_next(i);
|
let result = (|i: &mut _| Node::many(i, s)).parse_next(i);
|
||||||
s.leave_loop();
|
s.leave_loop();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let if_cond = preceded(
|
let if_cond = preceded(
|
||||||
ws(keyword("if")),
|
ws(keyword("if")),
|
||||||
cut_node(
|
cut_node(
|
||||||
@ -632,8 +667,9 @@ fn check_duplicated_name<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Macro<'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;
|
let level = s.level;
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
let parameters = |i: &mut _| -> ParseResult<
|
let parameters = |i: &mut _| -> ParseResult<
|
||||||
'_,
|
'_,
|
||||||
@ -659,12 +695,12 @@ impl<'a> Macro<'a> {
|
|||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
match args {
|
match args {
|
||||||
Some((args, Some(_))) => Ok(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),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let start_s = *i;
|
let start_s = ***i;
|
||||||
let mut start = (
|
let mut start = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("macro")),
|
ws(keyword("macro")),
|
||||||
@ -719,8 +755,8 @@ impl<'a> Macro<'a> {
|
|||||||
cut_node(
|
cut_node(
|
||||||
Some("macro"),
|
Some("macro"),
|
||||||
preceded(
|
preceded(
|
||||||
opt(|i: &mut _| {
|
opt(|i: &mut InputStream<'a>| {
|
||||||
let before = *i;
|
let before = ***i;
|
||||||
let end_name = ws(identifier).parse_next(i)?;
|
let end_name = ws(identifier).parse_next(i)?;
|
||||||
check_end_name(before, name, end_name, "macro")
|
check_end_name(before, name, end_name, "macro")
|
||||||
}),
|
}),
|
||||||
@ -756,9 +792,9 @@ pub struct FilterBlock<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FilterBlock<'a> {
|
impl<'a> FilterBlock<'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>>> {
|
||||||
fn filters<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, Filter<'a>> {
|
fn filters<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, Filter<'a>> {
|
||||||
let mut start = *i;
|
let mut start = ***i;
|
||||||
let mut res = Filter::parse(i, s.level)?;
|
let mut res = Filter::parse(i, s.level)?;
|
||||||
res.arguments
|
res.arguments
|
||||||
.insert(0, WithSpan::new(Box::new(Expr::FilterSource), start, i));
|
.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),
|
WithSpan::new(Box::new(Expr::Filter(res)), start.trim_start(), i),
|
||||||
);
|
);
|
||||||
res = filter;
|
res = filter;
|
||||||
start = *i;
|
start = ***i;
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (pws1, _, (filters, nws1, _), nodes, (_, pws2, _, nws2)) = (
|
let (pws1, _, (filters, nws1, _), nodes, (_, pws2, _, nws2)) = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("filter")),
|
ws(keyword("filter")),
|
||||||
@ -824,8 +860,8 @@ pub struct Import<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Import<'a> {
|
impl<'a> Import<'a> {
|
||||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let mut p = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("import")),
|
ws(keyword("import")),
|
||||||
@ -863,8 +899,8 @@ pub struct Call<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Call<'a> {
|
impl<'a> Call<'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 start_s = *i;
|
let start_s = ***i;
|
||||||
let parameters = |i: &mut _| -> ParseResult<'_, Option<Vec<&str>>> {
|
let parameters = |i: &mut _| -> ParseResult<'_, Option<Vec<&str>>> {
|
||||||
let args = opt(preceded(
|
let args = opt(preceded(
|
||||||
'(',
|
'(',
|
||||||
@ -877,7 +913,7 @@ impl<'a> Call<'a> {
|
|||||||
|
|
||||||
match args {
|
match args {
|
||||||
Some((args, Some(_))) => Ok(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),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -942,8 +978,8 @@ pub struct Match<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Match<'a> {
|
impl<'a> Match<'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 start = *i;
|
let start = ***i;
|
||||||
let mut p = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("match")),
|
ws(keyword("match")),
|
||||||
@ -1015,8 +1051,8 @@ pub struct BlockDef<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockDef<'a> {
|
impl<'a> BlockDef<'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 start_s = *i;
|
let start_s = ***i;
|
||||||
let mut start = (
|
let mut start = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("block")),
|
ws(keyword("block")),
|
||||||
@ -1042,8 +1078,8 @@ impl<'a> BlockDef<'a> {
|
|||||||
cut_node(
|
cut_node(
|
||||||
Some("block"),
|
Some("block"),
|
||||||
(
|
(
|
||||||
opt(|i: &mut _| {
|
opt(|i: &mut InputStream<'a>| {
|
||||||
let before = *i;
|
let before = ***i;
|
||||||
let end_name = ws(identifier).parse_next(i)?;
|
let end_name = ws(identifier).parse_next(i)?;
|
||||||
check_end_name(before, name, end_name, "block")
|
check_end_name(before, name, end_name, "block")
|
||||||
}),
|
}),
|
||||||
@ -1096,9 +1132,9 @@ pub struct Lit<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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)?;
|
not(eof).parse_next(i)?;
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let mut content = opt(take_until(
|
let mut content = opt(take_until(
|
||||||
..,
|
..,
|
||||||
(
|
(
|
||||||
@ -1139,9 +1175,12 @@ pub struct Raw<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Raw<'a> {
|
impl<'a> Raw<'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>>> {
|
||||||
fn endraw<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, (Ws, &'a str)> {
|
fn endraw<'a>(
|
||||||
let start = *i;
|
i: &mut InputStream<'a>,
|
||||||
|
s: &State<'_, '_>,
|
||||||
|
) -> ParseResult<'a, (Ws, &'a str)> {
|
||||||
|
let start = ***i;
|
||||||
loop {
|
loop {
|
||||||
// find the string "endraw", strip any spaces before it, and look if there is a `{%`
|
// find the string "endraw", strip any spaces before it, and look if there is a `{%`
|
||||||
let inner = take_until(.., "endraw").parse_next(i)?;
|
let inner = take_until(.., "endraw").parse_next(i)?;
|
||||||
@ -1157,7 +1196,7 @@ impl<'a> Raw<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// We found `{% endraw`. Do we find `%}`, too?
|
// We found `{% endraw`. Do we find `%}`, too?
|
||||||
*i = i.trim_ascii_start();
|
skip_ws0(i)?;
|
||||||
let i_before_nws = *i;
|
let i_before_nws = *i;
|
||||||
let nws = opt(Whitespace::parse).parse_next(i)?;
|
let nws = opt(Whitespace::parse).parse_next(i)?;
|
||||||
if opt(peek(s.syntax.block_end)).parse_next(i)?.is_none() {
|
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 = (
|
let mut p = (
|
||||||
terminated(opt(Whitespace::parse), ws(keyword("raw"))),
|
terminated(opt(Whitespace::parse), ws(keyword("raw"))),
|
||||||
cut_node(
|
cut_node(
|
||||||
@ -1205,8 +1244,8 @@ pub struct Let<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Let<'a> {
|
impl<'a> Let<'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 start = *i;
|
let start = ***i;
|
||||||
let mut p = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(alt((keyword("let"), keyword("set")))),
|
ws(alt((keyword("let"), keyword("set")))),
|
||||||
@ -1276,8 +1315,8 @@ pub struct If<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> If<'a> {
|
impl<'a> If<'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 start = *i;
|
let start = ***i;
|
||||||
let mut p = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
|i: &mut _| CondTest::parse(i, s),
|
|i: &mut _| CondTest::parse(i, s),
|
||||||
@ -1336,8 +1375,8 @@ pub struct Include<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Include<'a> {
|
impl<'a> Include<'a> {
|
||||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let mut p = (
|
let mut p = (
|
||||||
opt(Whitespace::parse),
|
opt(Whitespace::parse),
|
||||||
ws(keyword("include")),
|
ws(keyword("include")),
|
||||||
@ -1364,8 +1403,7 @@ pub struct Extends<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Extends<'a> {
|
impl<'a> Extends<'a> {
|
||||||
fn parse(i: &mut &'a str) -> ParseResult<'a, Box<Node<'a>>> {
|
fn parse(i: &mut InputStream<'a>) -> ParseResult<'a, Box<Node<'a>>> {
|
||||||
let start = *i;
|
|
||||||
preceded(
|
preceded(
|
||||||
(opt(Whitespace::parse), ws(keyword("extends"))),
|
(opt(Whitespace::parse), ws(keyword("extends"))),
|
||||||
cut_node(
|
cut_node(
|
||||||
@ -1373,7 +1411,8 @@ impl<'a> Extends<'a> {
|
|||||||
terminated(ws(str_lit_without_prefix), opt(Whitespace::parse)),
|
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)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1385,8 +1424,8 @@ pub struct Comment<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Comment<'a> {
|
impl<'a> Comment<'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>>> {
|
||||||
fn content<'a>(i: &mut &'a str, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
fn content<'a>(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, ()> {
|
||||||
let mut depth = 0usize;
|
let mut depth = 0usize;
|
||||||
loop {
|
loop {
|
||||||
take_until(.., (s.syntax.comment_start, s.syntax.comment_end)).parse_next(i)?;
|
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(
|
let mut content = preceded(
|
||||||
|i: &mut _| s.tag_comment_start(i),
|
|i: &mut _| s.tag_comment_start(i),
|
||||||
opt(terminated(
|
opt(terminated(
|
||||||
@ -1448,8 +1487,8 @@ pub struct Ws(pub Option<Whitespace>, pub Option<Whitespace>);
|
|||||||
fn end_node<'a, 'g: 'a>(
|
fn end_node<'a, 'g: 'a>(
|
||||||
node: &'g str,
|
node: &'g str,
|
||||||
expected: &'g str,
|
expected: &'g str,
|
||||||
) -> impl ModalParser<&'a str, &'a str, ErrorContext<'a>> + 'g {
|
) -> impl ModalParser<InputStream<'a>, &'a str, ErrorContext<'a>> + 'g {
|
||||||
move |i: &mut &'a str| {
|
move |i: &mut InputStream<'a>| {
|
||||||
let start = i.checkpoint();
|
let start = i.checkpoint();
|
||||||
let actual = ws(identifier).parse_next(i)?;
|
let actual = ws(identifier).parse_next(i)?;
|
||||||
if actual == expected {
|
if actual == expected {
|
||||||
@ -1458,7 +1497,7 @@ fn end_node<'a, 'g: 'a>(
|
|||||||
i.reset(&start);
|
i.reset(&start);
|
||||||
cut_error!(
|
cut_error!(
|
||||||
format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"),
|
format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"),
|
||||||
*i,
|
***i,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
i.reset(&start);
|
i.reset(&start);
|
||||||
|
@ -4,9 +4,9 @@ use winnow::token::one_of;
|
|||||||
use winnow::{ModalParser, Parser};
|
use winnow::{ModalParser, Parser};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CharLit, ErrorContext, Num, ParseErr, ParseResult, PathComponent, PathOrIdentifier, State,
|
CharLit, ErrorContext, InputStream, Num, ParseErr, ParseResult, PathComponent,
|
||||||
StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, cut_error, identifier,
|
PathOrIdentifier, State, StrLit, WithSpan, bool_lit, can_be_variable_name, char_lit, cut_error,
|
||||||
is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws,
|
identifier, is_rust_keyword, keyword, num_lit, path_or_identifier, str_lit, ws,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -31,7 +31,7 @@ pub enum Target<'a> {
|
|||||||
|
|
||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
/// Parses multiple targets with `or` separating them
|
/// 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 _level_guard = s.level.nest(i)?;
|
||||||
let mut p = opt(preceded(ws(keyword("or")), |i: &mut _| {
|
let mut p = opt(preceded(ws(keyword("or")), |i: &mut _| {
|
||||||
Self::parse_one(i, s)
|
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.
|
/// 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_paren = opt(ws('(')).map(|o| o.is_some());
|
||||||
let mut opt_opening_brace = 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());
|
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 = |i: &mut _| path_or_identifier(i, s.level);
|
||||||
let path = path.try_map(|r| match r {
|
let path = path.verify_map(|r| match r {
|
||||||
PathOrIdentifier::Path(v) => Ok(v),
|
PathOrIdentifier::Path(v) => Some(v),
|
||||||
PathOrIdentifier::Identifier(v) => Err(v),
|
PathOrIdentifier::Identifier(_) => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
// match structs
|
// match structs
|
||||||
@ -136,16 +136,15 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// neither literal nor struct nor path
|
// neither literal nor struct nor path
|
||||||
let i_before_identifier = *i;
|
|
||||||
let name = identifier.parse_next(i)?;
|
let name = identifier.parse_next(i)?;
|
||||||
let target = match name {
|
let target = match name {
|
||||||
"_" => Self::Placeholder(WithSpan::new((), i_before_identifier, i)),
|
"_" => Self::Placeholder(WithSpan::new_with_full((), name)),
|
||||||
_ => verify_name(i_before_identifier, name)?,
|
_ => verify_name(name)?,
|
||||||
};
|
};
|
||||||
Ok(target)
|
Ok(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit(i: &mut &'a str) -> ParseResult<'a, Self> {
|
fn lit(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||||
alt((
|
alt((
|
||||||
str_lit.map(Self::StrLit),
|
str_lit.map(Self::StrLit),
|
||||||
char_lit.map(Self::CharLit),
|
char_lit.map(Self::CharLit),
|
||||||
@ -157,14 +156,12 @@ impl<'a> Target<'a> {
|
|||||||
.parse_next(i)
|
.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)
|
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)> {
|
fn named(i: &mut InputStream<'a>, s: &State<'_, '_>) -> ParseResult<'a, (&'a str, Self)> {
|
||||||
let start = *i;
|
if let Some(rest) = opt(Self::rest.with_taken()).parse_next(i)? {
|
||||||
let rest = opt(Self::rest.with_taken()).parse_next(i)?;
|
|
||||||
if let Some(rest) = rest {
|
|
||||||
let chr = peek(ws(opt(one_of([',', ':'])))).parse_next(i)?;
|
let chr = peek(ws(opt(one_of([',', ':'])))).parse_next(i)?;
|
||||||
if let Some(chr) = chr {
|
if let Some(chr) = chr {
|
||||||
return cut_error!(
|
return cut_error!(
|
||||||
@ -172,7 +169,7 @@ impl<'a> Target<'a> {
|
|||||||
"unexpected `{chr}` character after `..`\n\
|
"unexpected `{chr}` character after `..`\n\
|
||||||
note that in a named struct, `..` must come last to ignore other members"
|
note that in a named struct, `..` must come last to ignore other members"
|
||||||
),
|
),
|
||||||
*i,
|
***i,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if let Target::Rest(ref s) = rest.0
|
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 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> {
|
fn rest(i: &mut InputStream<'a>) -> ParseResult<'a, Self> {
|
||||||
let start = *i;
|
let start = ***i;
|
||||||
let (ident, _) = (opt((identifier, ws('@'))), "..").parse_next(i)?;
|
let (ident, _) = (opt((identifier, ws('@'))), "..").parse_next(i)?;
|
||||||
Ok(Self::Rest(WithSpan::new(
|
Ok(Self::Rest(WithSpan::new(
|
||||||
ident.map(|(ident, _)| ident),
|
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) {
|
if is_rust_keyword(name) {
|
||||||
cut_error!(
|
cut_error!(
|
||||||
format!("cannot use `{name}` as a name: it is a rust keyword"),
|
format!("cannot use `{name}` as a name: it is a rust keyword"),
|
||||||
input,
|
name,
|
||||||
)
|
)
|
||||||
} else if !can_be_variable_name(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") {
|
} else if name.starts_with("__askama") {
|
||||||
cut_error!(
|
cut_error!(
|
||||||
format!("cannot use `{name}` as a name: it is reserved for `askama`"),
|
format!("cannot use `{name}` as a name: it is reserved for `askama`"),
|
||||||
input,
|
name,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Ok(Target::Name(name))
|
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>(
|
fn collect_targets<'a, T>(
|
||||||
i: &mut &'a str,
|
i: &mut InputStream<'a>,
|
||||||
delim: char,
|
delim: char,
|
||||||
one: impl ModalParser<&'a str, T, ErrorContext<'a>>,
|
one: impl ModalParser<InputStream<'a>, T, ErrorContext<'a>>,
|
||||||
) -> ParseResult<'a, (bool, Vec<T>)> {
|
) -> ParseResult<'a, (bool, Vec<T>)> {
|
||||||
let opt_comma = ws(opt(',')).map(|o| o.is_some());
|
let opt_comma = ws(opt(',')).map(|o| o.is_some());
|
||||||
let mut opt_end = ws(opt(one_of(delim))).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 targets = opt(separated(1.., one, ws(',')).map(|v: Vec<_>| v)).parse_next(i)?;
|
||||||
let Some(targets) = targets else {
|
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)?;
|
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"),
|
true => format!("expected member, or `{delim}` as terminator"),
|
||||||
false => format!("expected `,` for more members, 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::node::{Lit, Raw, Whitespace, Ws};
|
||||||
use crate::{
|
use crate::{
|
||||||
Ast, Expr, Filter, InnerSyntax, Node, Num, PathComponent, PathOrIdentifier, Span, StrLit,
|
Ast, Expr, Filter, InnerSyntax, InputStream, Node, Num, PathComponent, PathOrIdentifier, Span,
|
||||||
Syntax, SyntaxBuilder, WithSpan,
|
StrLit, Syntax, SyntaxBuilder, WithSpan,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<T> WithSpan<'static, T> {
|
impl<T> WithSpan<'static, T> {
|
||||||
@ -1349,10 +1349,13 @@ fn test_filter_with_path() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn underscore_is_an_identifier() {
|
fn underscore_is_an_identifier() {
|
||||||
let mut input = "_";
|
let mut input = InputStream {
|
||||||
|
input: LocatingSlice::new("_"),
|
||||||
|
state: (),
|
||||||
|
};
|
||||||
let result = crate::identifier.parse_next(&mut input);
|
let result = crate::identifier.parse_next(&mut input);
|
||||||
assert_eq!(result.unwrap(), "_");
|
assert_eq!(result.unwrap(), "_");
|
||||||
assert_eq!(input, "");
|
assert_eq!(**input, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
error: unknown node `fail`
|
error: unknown node `fail`
|
||||||
--> <source attribute>:2:6
|
--> <source attribute>:2:7
|
||||||
" fail %}\n{% endif %}"
|
"fail %}\n{% endif %}"
|
||||||
--> tests/ui/askama-block.rs:5:1
|
--> tests/ui/askama-block.rs:5:1
|
||||||
|
|
|
|
||||||
5 | /// Some documentation
|
5 | /// Some documentation
|
||||||
|
@ -54,7 +54,7 @@ error: unicode escape must be at most 10FFFF
|
|||||||
28 | #[template(path = "char-literals/char-literal-7.txt")]
|
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
|
--> <source attribute>:1:11
|
||||||
"'aaa' %}"
|
"'aaa' %}"
|
||||||
--> tests/ui/char_literal.rs:32:21
|
--> tests/ui/char_literal.rs:32:21
|
||||||
@ -62,7 +62,7 @@ error: invalid character
|
|||||||
32 | #[template(source = "{% let s = 'aaa' %}", ext = "html")]
|
32 | #[template(source = "{% let s = 'aaa' %}", ext = "html")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unterminated byte constant
|
error: unterminated byte literal
|
||||||
--> <source attribute>:1:3
|
--> <source attribute>:1:3
|
||||||
"b'c }}"
|
"b'c }}"
|
||||||
--> tests/ui/char_literal.rs:36:21
|
--> tests/ui/char_literal.rs:36:21
|
||||||
@ -70,7 +70,7 @@ error: unterminated byte constant
|
|||||||
36 | #[template(source = r#"{{ b'c }}"#, ext = "html")]
|
36 | #[template(source = r#"{{ b'c }}"#, ext = "html")]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: empty character literal
|
error: empty byte literal
|
||||||
--> <source attribute>:1:3
|
--> <source attribute>:1:3
|
||||||
"b'' }}"
|
"b'' }}"
|
||||||
--> tests/ui/char_literal.rs:40:21
|
--> 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")]
|
68 | #[template(source = r#"{{ b'\u{10ffff}' }}"#, ext = "html")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unterminated byte constant
|
error: unterminated byte literal
|
||||||
--> <source attribute>:1:6
|
--> <source attribute>:1:6
|
||||||
"b'c) }}"
|
"b'c) }}"
|
||||||
--> tests/ui/char_literal.rs:72:21
|
--> tests/ui/char_literal.rs:72:21
|
||||||
@ -142,7 +142,7 @@ error: unterminated byte constant
|
|||||||
72 | #[template(source = r#"{{ a!(b'c) }}"#, ext = "html")]
|
72 | #[template(source = r#"{{ a!(b'c) }}"#, ext = "html")]
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: empty character literal
|
error: empty byte literal
|
||||||
--> <source attribute>:1:3
|
--> <source attribute>:1:3
|
||||||
"b'' }}"
|
"b'' }}"
|
||||||
--> tests/ui/char_literal.rs:76:21
|
--> 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
|
error: node `end` was not expected in the current context
|
||||||
--> <source attribute>:1:2
|
--> <source attribute>:1:3
|
||||||
" end %}"
|
"end %}"
|
||||||
--> tests/ui/unexpected-tag.rs:5:1
|
--> tests/ui/unexpected-tag.rs:5:1
|
||||||
|
|
|
|
||||||
5 | /// ```askama
|
5 | /// ```askama
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: node `elif` was not expected in the current context: `for` block
|
error: node `elif` was not expected in the current context: `for` block
|
||||||
--> <source attribute>:3:2
|
--> <source attribute>:3:3
|
||||||
" elif %}\n what?\n{% endfor %}"
|
"elif %}\n what?\n{% endfor %}"
|
||||||
--> tests/ui/unexpected-tag.rs:12:1
|
--> tests/ui/unexpected-tag.rs:12:1
|
||||||
|
|
|
|
||||||
12 | /// ```askama
|
12 | /// ```askama
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: node `else` was not expected in the current context: `block` block
|
error: node `else` was not expected in the current context: `block` block
|
||||||
--> <source attribute>:3:2
|
--> <source attribute>:3:3
|
||||||
" else %}\n else\n{% endblock meta %}"
|
"else %}\n else\n{% endblock meta %}"
|
||||||
--> tests/ui/unexpected-tag.rs:23:1
|
--> tests/ui/unexpected-tag.rs:23:1
|
||||||
|
|
|
|
||||||
23 | /// ```askama
|
23 | /// ```askama
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: node `when` was not expected in the current context
|
error: node `when` was not expected in the current context
|
||||||
--> <source attribute>:1:2
|
--> <source attribute>:1:3
|
||||||
" when condition %}\n true\n{% endwhen %}"
|
"when condition %}\n true\n{% endwhen %}"
|
||||||
--> tests/ui/unexpected-tag.rs:34:1
|
--> tests/ui/unexpected-tag.rs:34:1
|
||||||
|
|
|
|
||||||
34 | /// ```askama
|
34 | /// ```askama
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unexpected closing tag `endlet`
|
error: unexpected closing tag `endlet`
|
||||||
--> <source attribute>:1:20
|
--> <source attribute>:1:21
|
||||||
" endlet %}"
|
"endlet %}"
|
||||||
--> tests/ui/unexpected-tag.rs:43:1
|
--> tests/ui/unexpected-tag.rs:43:1
|
||||||
|
|
|
|
||||||
43 | /// ```askama
|
43 | /// ```askama
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: unknown node `syntax`
|
error: unknown node `syntax`
|
||||||
--> <source attribute>:1:2
|
--> <source attribute>:1:3
|
||||||
" syntax error %}"
|
"syntax error %}"
|
||||||
--> tests/ui/unexpected-tag.rs:50:1
|
--> tests/ui/unexpected-tag.rs:50:1
|
||||||
|
|
|
|
||||||
50 | /// ```askama
|
50 | /// ```askama
|
||||||
|
Loading…
x
Reference in New Issue
Block a user