use winnow::Parser; use winnow::ascii::digit1; use winnow::combinator::{
alt, cut_err, fail, not, opt, peek, preceded, repeat, separated, terminated,
}; use winnow::error::ParserError as _; use winnow::stream::Stream as _;
#[derive(Clone, Debug, PartialEq)] pubenum Expr<'a> {
BoolLit(bool),
NumLit(&'a str, Num<'a>),
StrLit(StrLit<'a>),
CharLit(CharLit<'a>),
Var(&'a str),
Path(Vec<&'a str>),
Array(Vec<WithSpan<'a, Expr<'a>>>),
Attr(Box<WithSpan<'a, Expr<'a>>>, Attr<'a>),
Index(Box<WithSpan<'a, Expr<'a>>>, Box<WithSpan<'a, Expr<'a>>>),
Filter(Filter<'a>), As(Box<WithSpan<'a, Expr<'a>>>, &'a str),
NamedArgument(&'a str, Box<WithSpan<'a, Expr<'a>>>),
Unary(&'a str, Box<WithSpan<'a, Expr<'a>>>),
BinOp(
&'a str, Box<WithSpan<'a, Expr<'a>>>, Box<WithSpan<'a, Expr<'a>>>,
),
Range(
&'a str,
Option<Box<WithSpan<'a, Expr<'a>>>>,
Option<Box<WithSpan<'a, Expr<'a>>>>,
),
Group(Box<WithSpan<'a, Expr<'a>>>),
Tuple(Vec<WithSpan<'a, Expr<'a>>>),
Call {
path: Box<WithSpan<'a, Expr<'a>>>,
args: Vec<WithSpan<'a, Expr<'a>>>,
generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
},
RustMacro(Vec<&'a str>, &'a str), Try(Box<WithSpan<'a, Expr<'a>>>), /// This variant should never be used directly. It is created when generating filter blocks.
FilterSource,
IsDefined(&'a str),
IsNotDefined(&'a str),
Concat(Vec<WithSpan<'a, Expr<'a>>>), /// If you have `&& let Some(y)`, this variant handles it.
LetCond(Box<WithSpan<'a, CondTest<'a>>>), /// This variant should never be used directly. /// It is used for the handling of named arguments in the generator, esp. with filters.
ArgumentPlaceholder,
}
preceded(
ws('('),
cut_err(terminated(
separated( 0..,
ws(move |i: &mut _| { // Needed to prevent borrowing it twice between this closure and the one // calling `Self::named_arguments`. let named_arguments = &mut named_arguments; let has_named_arguments = !named_arguments.is_empty();
fn named_argument(
i: &mut &'a str,
level: Level<'_>,
named_arguments: &mut HashSet<&'a str>,
start: &'a str,
is_template_macro: bool,
) -> ParseResult<'a, WithSpan<'a, Self>> { if !is_template_macro { // If this is not a template macro, we don't want to parse named arguments so // we instead return an error which will allow to continue the parsing. return fail.parse_next(i);
}
let (argument, _, value) = (identifier, ws('='), move |i: &mut _| { Self::parse(i, level, false)
})
.parse_next(i)?; if named_arguments.insert(argument) {
Ok(WithSpan::new( Self::NamedArgument(argument, Box::new(value)),
start,
))
} else {
Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!("named argument `{argument}` was passed more than once"),
start,
)))
}
}
fn is_as(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> { let start = *i; let lhs = Self::filtered(i, level)?; let before_keyword = *i; let rhs = opt(ws(identifier)).parse_next(i)?; match rhs {
Some("is") => {}
Some("as") => { let target = opt(identifier).parse_next(i)?; let target = target.unwrap_or_default(); ifcrate::PRIMITIVE_TYPES.contains(&target) { return Ok(WithSpan::new(Self::As(Box::new(lhs), target), start));
} elseif target.is_empty() { return Err(winnow::error::ErrMode::Cut(ErrorContext::new( "`as` operator expects the name of a primitive type on its right-hand side",
before_keyword.trim_start(),
)));
} else { return Err(winnow::error::ErrMode::Cut(ErrorContext::new(
format!( "`as` operator expects the name of a primitive type on its right-hand \
side, found `{target}`"
),
before_keyword.trim_start(),
)));
}
}
_ => {
*i = before_keyword; return Ok(lhs);
}
}
let rhs = opt(terminated(opt(keyword("not")), ws(keyword("defined")))).parse_next(i)?; let ctor = match rhs {
None => { return Err(winnow::error::ErrMode::Cut(ErrorContext::new( "expected `defined` or `not defined` after `is`", // We use `start` to show the whole `var is` thing instead of the current token.
start,
)));
}
Some(None) => Self::IsDefined,
Some(Some(_)) => Self::IsNotDefined,
}; let var_name = match *lhs { Self::Var(var_name) => var_name, Self::Attr(_, _) => { return Err(winnow::error::ErrMode::Cut(ErrorContext::new( "`is defined` operator can only be used on variables, not on their fields",
start,
)));
}
_ => { return Err(winnow::error::ErrMode::Cut(ErrorContext::new( "`is defined` operator can only be used on variables",
start,
)));
}
};
Ok(WithSpan::new(ctor(var_name), start))
}
// This is a rare place where we create recursion in the parsed AST // without recursing the parser call stack. However, this can lead // to stack overflows in drop glue when the AST is very deep. letmut level_guard = level.guard(); letmut ops = vec![]; letmut i_before = *i; whilelet Some(op) = opt(ws(alt(("!", "-", "*", "&")))).parse_next(i)? {
level_guard.nest(i_before)?;
ops.push(op);
i_before = *i;
}
letmut expr = Suffix::parse(i, level)?; for op in ops.iter().rev() {
expr = WithSpan::new(Self::Unary(op, Box::new(expr)), start);
}
fn group(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> { let start = *i; let expr = preceded(ws('('), opt(|i: &mut _| Self::parse(i, level, true))).parse_next(i)?; let Some(expr) = expr else { let _ = ')'.parse_next(i)?; return Ok(WithSpan::new(Self::Tuple(vec![]), start));
};
let comma = ws(opt(peek(','))).parse_next(i)?; if comma.is_none() { let _ = ')'.parse_next(i)?; return Ok(WithSpan::new(Self::Group(Box::new(expr)), start));
}
fn token_xor<'a>(i: &mut &'a str) -> ParseResult<'a> { let good = alt((keyword("xor").value(true), '^'.value(false))).parse_next(i)?; if good {
Ok("^")
} else {
Err(winnow::error::ErrMode::Cut(ErrorContext::new( "the binary XOR operator is called `xor` in askama",
*i,
)))
}
}
fn token_bitand<'a>(i: &mut &'a str) -> ParseResult<'a> { let good = alt((keyword("bitand").value(true), ('&', not('&')).value(false))).parse_next(i)?; if good {
Ok("&")
} else {
Err(winnow::error::ErrMode::Cut(ErrorContext::new( "the binary AND operator is called `bitand` in askama",
*i,
)))
}
}
enum Suffix<'a> {
Attr(Attr<'a>),
Index(WithSpan<'a, Expr<'a>>),
Call {
args: Vec<WithSpan<'a, Expr<'a>>>,
generics: Vec<WithSpan<'a, TyGenerics<'a>>>,
}, // The value is the arguments of the macro call.
MacroCall(&'a str), Try,
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.