aboutsummaryrefslogtreecommitdiffstats
path: root/askama_derive/src/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--askama_derive/src/parser.rs1940
1 files changed, 0 insertions, 1940 deletions
diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs
deleted file mode 100644
index 5045731..0000000
--- a/askama_derive/src/parser.rs
+++ /dev/null
@@ -1,1940 +0,0 @@
-use std::cell::Cell;
-use std::str;
-
-use nom::branch::alt;
-use nom::bytes::complete::{escaped, is_not, tag, take_till, take_until};
-use nom::character::complete::{anychar, char, digit1};
-use nom::combinator::{complete, consumed, cut, eof, map, not, opt, peek, recognize, value};
-use nom::error::{Error, ErrorKind};
-use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
-use nom::sequence::{delimited, pair, preceded, terminated, tuple};
-use nom::{self, error_position, AsChar, IResult, InputTakeAtPosition};
-
-use crate::config::Syntax;
-use crate::CompileError;
-
-#[derive(Debug, PartialEq)]
-pub(crate) enum Node<'a> {
- Lit(&'a str, &'a str, &'a str),
- Comment(Ws),
- Expr(Ws, Expr<'a>),
- Call(Ws, Option<&'a str>, &'a str, Vec<Expr<'a>>),
- LetDecl(Ws, Target<'a>),
- Let(Ws, Target<'a>, Expr<'a>),
- Cond(Vec<Cond<'a>>, Ws),
- Match(Ws, Expr<'a>, Vec<When<'a>>, Ws),
- Loop(Loop<'a>),
- Extends(&'a str),
- BlockDef(Ws, &'a str, Vec<Node<'a>>, Ws),
- Include(Ws, &'a str),
- Import(Ws, &'a str, &'a str),
- Macro(&'a str, Macro<'a>),
- Raw(Ws, &'a str, &'a str, &'a str, Ws),
- Break(Ws),
- Continue(Ws),
-}
-
-#[derive(Debug, PartialEq)]
-pub(crate) struct Loop<'a> {
- pub(crate) ws1: Ws,
- pub(crate) var: Target<'a>,
- pub(crate) iter: Expr<'a>,
- pub(crate) cond: Option<Expr<'a>>,
- pub(crate) body: Vec<Node<'a>>,
- pub(crate) ws2: Ws,
- pub(crate) else_block: Vec<Node<'a>>,
- pub(crate) ws3: Ws,
-}
-
-#[derive(Debug, PartialEq)]
-pub(crate) enum Expr<'a> {
- BoolLit(&'a str),
- NumLit(&'a str),
- StrLit(&'a str),
- CharLit(&'a str),
- Var(&'a str),
- Path(Vec<&'a str>),
- Array(Vec<Expr<'a>>),
- Attr(Box<Expr<'a>>, &'a str),
- Index(Box<Expr<'a>>, Box<Expr<'a>>),
- Filter(&'a str, Vec<Expr<'a>>),
- Unary(&'a str, Box<Expr<'a>>),
- BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
- Range(&'a str, Option<Box<Expr<'a>>>, Option<Box<Expr<'a>>>),
- Group(Box<Expr<'a>>),
- Tuple(Vec<Expr<'a>>),
- Call(Box<Expr<'a>>, Vec<Expr<'a>>),
- RustMacro(&'a str, &'a str),
- Try(Box<Expr<'a>>),
-}
-
-impl Expr<'_> {
- /// Returns `true` if enough assumptions can be made,
- /// to determine that `self` is copyable.
- pub(crate) fn is_copyable(&self) -> bool {
- self.is_copyable_within_op(false)
- }
-
- fn is_copyable_within_op(&self, within_op: bool) -> bool {
- use Expr::*;
- match self {
- BoolLit(_) | NumLit(_) | StrLit(_) | CharLit(_) => true,
- Unary(.., expr) => expr.is_copyable_within_op(true),
- BinOp(_, lhs, rhs) => {
- lhs.is_copyable_within_op(true) && rhs.is_copyable_within_op(true)
- }
- Range(..) => true,
- // The result of a call likely doesn't need to be borrowed,
- // as in that case the call is more likely to return a
- // reference in the first place then.
- Call(..) | Path(..) => true,
- // If the `expr` is within a `Unary` or `BinOp` then
- // an assumption can be made that the operand is copy.
- // If not, then the value is moved and adding `.clone()`
- // will solve that issue. However, if the operand is
- // implicitly borrowed, then it's likely not even possible
- // to get the template to compile.
- _ => within_op && self.is_attr_self(),
- }
- }
-
- /// Returns `true` if this is an `Attr` where the `obj` is `"self"`.
- pub(crate) fn is_attr_self(&self) -> bool {
- match self {
- Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Var("self")) => true,
- Expr::Attr(obj, _) if matches!(obj.as_ref(), Expr::Attr(..)) => obj.is_attr_self(),
- _ => false,
- }
- }
-
- /// Returns `true` if the outcome of this expression may be used multiple times in the same
- /// `write!()` call, without evaluating the expression again, i.e. the expression should be
- /// side-effect free.
- pub(crate) fn is_cachable(&self) -> bool {
- match self {
- // Literals are the definition of pure:
- Expr::BoolLit(_) => true,
- Expr::NumLit(_) => true,
- Expr::StrLit(_) => true,
- Expr::CharLit(_) => true,
- // fmt::Display should have no effects:
- Expr::Var(_) => true,
- Expr::Path(_) => true,
- // Check recursively:
- Expr::Array(args) => args.iter().all(|arg| arg.is_cachable()),
- Expr::Attr(lhs, _) => lhs.is_cachable(),
- Expr::Index(lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
- Expr::Filter(_, args) => args.iter().all(|arg| arg.is_cachable()),
- Expr::Unary(_, arg) => arg.is_cachable(),
- Expr::BinOp(_, lhs, rhs) => lhs.is_cachable() && rhs.is_cachable(),
- Expr::Range(_, lhs, rhs) => {
- lhs.as_ref().map_or(true, |v| v.is_cachable())
- && rhs.as_ref().map_or(true, |v| v.is_cachable())
- }
- Expr::Group(arg) => arg.is_cachable(),
- Expr::Tuple(args) => args.iter().all(|arg| arg.is_cachable()),
- // We have too little information to tell if the expression is pure:
- Expr::Call(_, _) => false,
- Expr::RustMacro(_, _) => false,
- Expr::Try(_) => false,
- }
- }
-}
-
-pub(crate) type When<'a> = (Ws, Target<'a>, Vec<Node<'a>>);
-
-#[derive(Debug, PartialEq)]
-pub(crate) struct Macro<'a> {
- pub(crate) ws1: Ws,
- pub(crate) args: Vec<&'a str>,
- pub(crate) nodes: Vec<Node<'a>>,
- pub(crate) ws2: Ws,
-}
-
-#[derive(Debug, PartialEq)]
-pub(crate) enum Target<'a> {
- Name(&'a str),
- Tuple(Vec<&'a str>, Vec<Target<'a>>),
- Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>),
- NumLit(&'a str),
- StrLit(&'a str),
- CharLit(&'a str),
- BoolLit(&'a str),
- Path(Vec<&'a str>),
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub(crate) enum Whitespace {
- Preserve,
- Suppress,
- Minimize,
-}
-
-impl From<char> for Whitespace {
- fn from(c: char) -> Self {
- match c {
- '+' => Self::Preserve,
- '-' => Self::Suppress,
- '~' => Self::Minimize,
- _ => panic!("unsupported `Whitespace` conversion"),
- }
- }
-}
-
-/// First field is "minus/plus sign was used on the left part of the item".
-///
-/// Second field is "minus/plus sign was used on the right part of the item".
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub(crate) struct Ws(pub(crate) Option<Whitespace>, pub(crate) Option<Whitespace>);
-
-pub(crate) type Cond<'a> = (Ws, Option<CondTest<'a>>, Vec<Node<'a>>);
-
-#[derive(Debug, PartialEq)]
-pub(crate) struct CondTest<'a> {
- pub(crate) target: Option<Target<'a>>,
- pub(crate) expr: Expr<'a>,
-}
-
-fn is_ws(c: char) -> bool {
- matches!(c, ' ' | '\t' | '\r' | '\n')
-}
-
-fn not_ws(c: char) -> bool {
- !is_ws(c)
-}
-
-fn ws<'a, O>(
- inner: impl FnMut(&'a str) -> IResult<&'a str, O>,
-) -> impl FnMut(&'a str) -> IResult<&'a str, O> {
- delimited(take_till(not_ws), inner, take_till(not_ws))
-}
-
-fn split_ws_parts(s: &str) -> Node<'_> {
- let trimmed_start = s.trim_start_matches(is_ws);
- let len_start = s.len() - trimmed_start.len();
- let trimmed = trimmed_start.trim_end_matches(is_ws);
- Node::Lit(&s[..len_start], trimmed, &trimmed_start[trimmed.len()..])
-}
-
-/// Skips input until `end` was found, but does not consume it.
-/// Returns tuple that would be returned when parsing `end`.
-fn skip_till<'a, O>(
- end: impl FnMut(&'a str) -> IResult<&'a str, O>,
-) -> impl FnMut(&'a str) -> IResult<&'a str, (&'a str, O)> {
- enum Next<O> {
- IsEnd(O),
- NotEnd(char),
- }
- let mut next = alt((map(end, Next::IsEnd), map(anychar, Next::NotEnd)));
- move |start: &'a str| {
- let mut i = start;
- loop {
- let (j, is_end) = next(i)?;
- match is_end {
- Next::IsEnd(lookahead) => return Ok((i, (j, lookahead))),
- Next::NotEnd(_) => i = j,
- }
- }
- }
-}
-
-fn keyword<'a>(k: &'a str) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str> {
- move |i: &'a str| -> IResult<&'a str, &'a str> {
- let (j, v) = identifier(i)?;
- if k == v {
- Ok((j, v))
- } else {
- Err(nom::Err::Error(error_position!(i, ErrorKind::Tag)))
- }
- }
-}
-
-struct State<'a> {
- syntax: &'a Syntax,
- loop_depth: Cell<usize>,
-}
-
-fn take_content<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let p_start = alt((
- tag(s.syntax.block_start.as_str()),
- tag(s.syntax.comment_start.as_str()),
- tag(s.syntax.expr_start.as_str()),
- ));
-
- let (i, _) = not(eof)(i)?;
- let (i, content) = opt(recognize(skip_till(p_start)))(i)?;
- let (i, content) = match content {
- Some("") => {
- // {block,comment,expr}_start follows immediately.
- return Err(nom::Err::Error(error_position!(i, ErrorKind::TakeUntil)));
- }
- Some(content) => (i, content),
- None => ("", i), // there is no {block,comment,expr}_start: take everything
- };
- Ok((i, split_ws_parts(content)))
-}
-
-fn identifier(input: &str) -> IResult<&str, &str> {
- recognize(pair(identifier_start, opt(identifier_tail)))(input)
-}
-
-fn identifier_start(s: &str) -> IResult<&str, &str> {
- s.split_at_position1_complete(
- |c| !(c.is_alpha() || c == '_' || c >= '\u{0080}'),
- nom::error::ErrorKind::Alpha,
- )
-}
-
-fn identifier_tail(s: &str) -> IResult<&str, &str> {
- s.split_at_position1_complete(
- |c| !(c.is_alphanum() || c == '_' || c >= '\u{0080}'),
- nom::error::ErrorKind::Alpha,
- )
-}
-
-fn bool_lit(i: &str) -> IResult<&str, &str> {
- alt((keyword("false"), keyword("true")))(i)
-}
-
-fn expr_bool_lit(i: &str) -> IResult<&str, Expr<'_>> {
- map(bool_lit, Expr::BoolLit)(i)
-}
-
-fn variant_bool_lit(i: &str) -> IResult<&str, Target<'_>> {
- map(bool_lit, Target::BoolLit)(i)
-}
-
-fn num_lit(i: &str) -> IResult<&str, &str> {
- recognize(pair(digit1, opt(pair(char('.'), digit1))))(i)
-}
-
-fn expr_num_lit(i: &str) -> IResult<&str, Expr<'_>> {
- map(num_lit, Expr::NumLit)(i)
-}
-
-fn expr_array_lit(i: &str) -> IResult<&str, Expr<'_>> {
- delimited(
- ws(char('[')),
- map(separated_list1(ws(char(',')), expr_any), Expr::Array),
- ws(char(']')),
- )(i)
-}
-
-fn variant_num_lit(i: &str) -> IResult<&str, Target<'_>> {
- map(num_lit, Target::NumLit)(i)
-}
-
-fn str_lit(i: &str) -> IResult<&str, &str> {
- let (i, s) = delimited(
- char('"'),
- opt(escaped(is_not("\\\""), '\\', anychar)),
- char('"'),
- )(i)?;
- Ok((i, s.unwrap_or_default()))
-}
-
-fn expr_str_lit(i: &str) -> IResult<&str, Expr<'_>> {
- map(str_lit, Expr::StrLit)(i)
-}
-
-fn variant_str_lit(i: &str) -> IResult<&str, Target<'_>> {
- map(str_lit, Target::StrLit)(i)
-}
-
-fn char_lit(i: &str) -> IResult<&str, &str> {
- let (i, s) = delimited(
- char('\''),
- opt(escaped(is_not("\\\'"), '\\', anychar)),
- char('\''),
- )(i)?;
- Ok((i, s.unwrap_or_default()))
-}
-
-fn expr_char_lit(i: &str) -> IResult<&str, Expr<'_>> {
- map(char_lit, Expr::CharLit)(i)
-}
-
-fn variant_char_lit(i: &str) -> IResult<&str, Target<'_>> {
- map(char_lit, Target::CharLit)(i)
-}
-
-fn expr_var(i: &str) -> IResult<&str, Expr<'_>> {
- map(identifier, Expr::Var)(i)
-}
-
-fn path(i: &str) -> IResult<&str, Vec<&str>> {
- let root = opt(value("", ws(tag("::"))));
- let tail = separated_list1(ws(tag("::")), identifier);
-
- match tuple((root, identifier, ws(tag("::")), tail))(i) {
- Ok((i, (root, start, _, rest))) => {
- let mut path = Vec::new();
- path.extend(root);
- path.push(start);
- path.extend(rest);
- Ok((i, path))
- }
- Err(err) => {
- if let Ok((i, name)) = identifier(i) {
- // The returned identifier can be assumed to be path if:
- // - Contains both a lowercase and uppercase character, i.e. a type name like `None`
- // - Doesn't contain any lowercase characters, i.e. it's a constant
- // In short, if it contains any uppercase characters it's a path.
- if name.contains(char::is_uppercase) {
- return Ok((i, vec![name]));
- }
- }
-
- // If `identifier()` fails then just return the original error
- Err(err)
- }
- }
-}
-
-fn expr_path(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, path) = path(i)?;
- Ok((i, Expr::Path(path)))
-}
-
-fn named_target(i: &str) -> IResult<&str, (&str, Target<'_>)> {
- let (i, (src, target)) = pair(identifier, opt(preceded(ws(char(':')), target)))(i)?;
- Ok((i, (src, target.unwrap_or(Target::Name(src)))))
-}
-
-fn variant_lit(i: &str) -> IResult<&str, Target<'_>> {
- alt((
- variant_str_lit,
- variant_char_lit,
- variant_num_lit,
- variant_bool_lit,
- ))(i)
-}
-
-fn target(i: &str) -> IResult<&str, Target<'_>> {
- let mut opt_opening_paren = map(opt(ws(char('('))), |o| o.is_some());
- let mut opt_closing_paren = map(opt(ws(char(')'))), |o| o.is_some());
- let mut opt_opening_brace = map(opt(ws(char('{'))), |o| o.is_some());
-
- let (i, lit) = opt(variant_lit)(i)?;
- if let Some(lit) = lit {
- return Ok((i, lit));
- }
-
- // match tuples and unused parentheses
- let (i, target_is_tuple) = opt_opening_paren(i)?;
- if target_is_tuple {
- let (i, is_empty_tuple) = opt_closing_paren(i)?;
- if is_empty_tuple {
- return Ok((i, Target::Tuple(Vec::new(), Vec::new())));
- }
-
- let (i, first_target) = target(i)?;
- let (i, is_unused_paren) = opt_closing_paren(i)?;
- if is_unused_paren {
- return Ok((i, first_target));
- }
-
- let mut targets = vec![first_target];
- let (i, _) = cut(tuple((
- fold_many0(
- preceded(ws(char(',')), target),
- || (),
- |_, target| {
- targets.push(target);
- },
- ),
- opt(ws(char(','))),
- ws(cut(char(')'))),
- )))(i)?;
- return Ok((i, Target::Tuple(Vec::new(), targets)));
- }
-
- // match structs
- let (i, path) = opt(path)(i)?;
- if let Some(path) = path {
- let i_before_matching_with = i;
- let (i, _) = opt(ws(keyword("with")))(i)?;
-
- let (i, is_unnamed_struct) = opt_opening_paren(i)?;
- if is_unnamed_struct {
- let (i, targets) = alt((
- map(char(')'), |_| Vec::new()),
- terminated(
- cut(separated_list1(ws(char(',')), target)),
- pair(opt(ws(char(','))), ws(cut(char(')')))),
- ),
- ))(i)?;
- return Ok((i, Target::Tuple(path, targets)));
- }
-
- let (i, is_named_struct) = opt_opening_brace(i)?;
- if is_named_struct {
- let (i, targets) = alt((
- map(char('}'), |_| Vec::new()),
- terminated(
- cut(separated_list1(ws(char(',')), named_target)),
- pair(opt(ws(char(','))), ws(cut(char('}')))),
- ),
- ))(i)?;
- return Ok((i, Target::Struct(path, targets)));
- }
-
- return Ok((i_before_matching_with, Target::Path(path)));
- }
-
- // neither literal nor struct nor path
- map(identifier, Target::Name)(i)
-}
-
-fn arguments(i: &str) -> IResult<&str, Vec<Expr<'_>>> {
- delimited(
- ws(char('(')),
- separated_list0(char(','), ws(expr_any)),
- ws(char(')')),
- )(i)
-}
-
-fn macro_arguments(i: &str) -> IResult<&str, &str> {
- delimited(char('('), recognize(nested_parenthesis), char(')'))(i)
-}
-
-fn nested_parenthesis(i: &str) -> IResult<&str, ()> {
- let mut nested = 0;
- let mut last = 0;
- let mut in_str = false;
- let mut escaped = false;
-
- for (i, b) in i.chars().enumerate() {
- if !(b == '(' || b == ')') || !in_str {
- match b {
- '(' => nested += 1,
- ')' => {
- if nested == 0 {
- last = i;
- break;
- }
- nested -= 1;
- }
- '"' => {
- if in_str {
- if !escaped {
- in_str = false;
- }
- } else {
- in_str = true;
- }
- }
- '\\' => {
- escaped = !escaped;
- }
- _ => (),
- }
- }
-
- if escaped && b != '\\' {
- escaped = false;
- }
- }
-
- if nested == 0 {
- Ok((&i[last..], ()))
- } else {
- Err(nom::Err::Error(error_position!(
- i,
- ErrorKind::SeparatedNonEmptyList
- )))
- }
-}
-
-fn parameters(i: &str) -> IResult<&str, Vec<&str>> {
- delimited(
- ws(char('(')),
- separated_list0(char(','), ws(identifier)),
- ws(char(')')),
- )(i)
-}
-
-fn expr_group(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, expr) = preceded(ws(char('(')), opt(expr_any))(i)?;
- let expr = match expr {
- Some(expr) => expr,
- None => {
- let (i, _) = char(')')(i)?;
- return Ok((i, Expr::Tuple(vec![])));
- }
- };
-
- let (i, comma) = ws(opt(peek(char(','))))(i)?;
- if comma.is_none() {
- let (i, _) = char(')')(i)?;
- return Ok((i, Expr::Group(Box::new(expr))));
- }
-
- let mut exprs = vec![expr];
- let (i, _) = fold_many0(
- preceded(char(','), ws(expr_any)),
- || (),
- |_, expr| {
- exprs.push(expr);
- },
- )(i)?;
- let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
- Ok((i, Expr::Tuple(exprs)))
-}
-
-fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
- alt((
- expr_bool_lit,
- expr_num_lit,
- expr_str_lit,
- expr_char_lit,
- expr_path,
- expr_rust_macro,
- expr_array_lit,
- expr_var,
- expr_group,
- ))(i)
-}
-
-enum Suffix<'a> {
- Attr(&'a str),
- Index(Expr<'a>),
- Call(Vec<Expr<'a>>),
- Try,
-}
-
-fn expr_attr(i: &str) -> IResult<&str, Suffix<'_>> {
- map(
- preceded(
- ws(pair(char('.'), not(char('.')))),
- cut(alt((num_lit, identifier))),
- ),
- Suffix::Attr,
- )(i)
-}
-
-fn expr_index(i: &str) -> IResult<&str, Suffix<'_>> {
- map(
- preceded(ws(char('[')), cut(terminated(expr_any, ws(char(']'))))),
- Suffix::Index,
- )(i)
-}
-
-fn expr_call(i: &str) -> IResult<&str, Suffix<'_>> {
- map(arguments, Suffix::Call)(i)
-}
-
-fn expr_try(i: &str) -> IResult<&str, Suffix<'_>> {
- map(preceded(take_till(not_ws), char('?')), |_| Suffix::Try)(i)
-}
-
-fn filter(i: &str) -> IResult<&str, (&str, Option<Vec<Expr<'_>>>)> {
- let (i, (_, fname, args)) = tuple((char('|'), ws(identifier), opt(arguments)))(i)?;
- Ok((i, (fname, args)))
-}
-
-fn expr_filtered(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (obj, filters)) = tuple((expr_prefix, many0(filter)))(i)?;
-
- let mut res = obj;
- for (fname, args) in filters {
- res = Expr::Filter(fname, {
- let mut args = match args {
- Some(inner) => inner,
- None => Vec::new(),
- };
- args.insert(0, res);
- args
- });
- }
-
- Ok((i, res))
-}
-
-fn expr_prefix(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), expr_suffix)(i)?;
- for op in ops.iter().rev() {
- expr = Expr::Unary(op, Box::new(expr));
- }
- Ok((i, expr))
-}
-
-fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
- let (mut i, mut expr) = expr_single(i)?;
- loop {
- let (j, suffix) = opt(alt((expr_attr, expr_index, expr_call, expr_try)))(i)?;
- i = j;
- match suffix {
- Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
- Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
- Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
- Some(Suffix::Try) => expr = Expr::Try(expr.into()),
- None => break,
- }
- }
- Ok((i, expr))
-}
-
-fn expr_rust_macro(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (mname, _, args)) = tuple((identifier, char('!'), macro_arguments))(i)?;
- Ok((i, Expr::RustMacro(mname, args)))
-}
-
-macro_rules! expr_prec_layer {
- ( $name:ident, $inner:ident, $op:expr ) => {
- fn $name(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, left) = $inner(i)?;
- let (i, right) = many0(pair(
- ws(tag($op)),
- $inner,
- ))(i)?;
- Ok((
- i,
- right.into_iter().fold(left, |left, (op, right)| {
- Expr::BinOp(op, Box::new(left), Box::new(right))
- }),
- ))
- }
- };
- ( $name:ident, $inner:ident, $( $op:expr ),+ ) => {
- fn $name(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, left) = $inner(i)?;
- let (i, right) = many0(pair(
- ws(alt(($( tag($op) ),+,))),
- $inner,
- ))(i)?;
- Ok((
- i,
- right.into_iter().fold(left, |left, (op, right)| {
- Expr::BinOp(op, Box::new(left), Box::new(right))
- }),
- ))
- }
- }
-}
-
-expr_prec_layer!(expr_muldivmod, expr_filtered, "*", "/", "%");
-expr_prec_layer!(expr_addsub, expr_muldivmod, "+", "-");
-expr_prec_layer!(expr_shifts, expr_addsub, ">>", "<<");
-expr_prec_layer!(expr_band, expr_shifts, "&");
-expr_prec_layer!(expr_bxor, expr_band, "^");
-expr_prec_layer!(expr_bor, expr_bxor, "|");
-expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<");
-expr_prec_layer!(expr_and, expr_compare, "&&");
-expr_prec_layer!(expr_or, expr_and, "||");
-
-fn expr_handle_ws(i: &str) -> IResult<&str, Whitespace> {
- alt((char('-'), char('+'), char('~')))(i).map(|(s, r)| (s, Whitespace::from(r)))
-}
-
-fn expr_any(i: &str) -> IResult<&str, Expr<'_>> {
- let range_right = |i| pair(ws(alt((tag("..="), tag("..")))), opt(expr_or))(i);
- alt((
- map(range_right, |(op, right)| {
- Expr::Range(op, None, right.map(Box::new))
- }),
- map(
- pair(expr_or, opt(range_right)),
- |(left, right)| match right {
- Some((op, right)) => Expr::Range(op, Some(Box::new(left)), right.map(Box::new)),
- None => left,
- },
- ),
- ))(i)
-}
-
-fn expr_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- |i| tag_expr_start(i, s),
- cut(tuple((
- opt(expr_handle_ws),
- ws(expr_any),
- opt(expr_handle_ws),
- |i| tag_expr_end(i, s),
- ))),
- ));
- let (i, (_, (pws, expr, nws, _))) = p(i)?;
- Ok((i, Node::Expr(Ws(pws, nws), expr)))
-}
-
-fn block_call(i: &str) -> IResult<&str, Node<'_>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("call")),
- cut(tuple((
- opt(tuple((ws(identifier), ws(tag("::"))))),
- ws(identifier),
- ws(arguments),
- opt(expr_handle_ws),
- ))),
- ));
- let (i, (pws, _, (scope, name, args, nws))) = p(i)?;
- let scope = scope.map(|(scope, _)| scope);
- Ok((i, Node::Call(Ws(pws, nws), scope, name, args)))
-}
-
-fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> {
- let mut p = preceded(
- ws(keyword("if")),
- cut(tuple((
- opt(delimited(
- ws(alt((keyword("let"), keyword("set")))),
- ws(target),
- ws(char('=')),
- )),
- ws(expr_any),
- ))),
- );
- let (i, (target, expr)) = p(i)?;
- Ok((i, CondTest { target, expr }))
-}
-
-fn cond_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Cond<'a>> {
- let mut p = tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("else")),
- cut(tuple((
- opt(cond_if),
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(|i| parse_template(i, s)),
- ))),
- ));
- let (i, (_, pws, _, (cond, nws, _, block))) = p(i)?;
- Ok((i, (Ws(pws, nws), cond, block)))
-}
-
-fn block_if<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- cond_if,
- cut(tuple((
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(tuple((
- |i| parse_template(i, s),
- many0(|i| cond_block(i, s)),
- cut(tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("endif")),
- opt(expr_handle_ws),
- ))),
- ))),
- ))),
- ));
- let (i, (pws1, cond, (nws1, _, (block, elifs, (_, pws2, _, nws2))))) = p(i)?;
-
- let mut res = vec![(Ws(pws1, nws1), Some(cond), block)];
- res.extend(elifs);
- Ok((i, Node::Cond(res, Ws(pws2, nws2))))
-}
-
-fn match_else_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> {
- let mut p = tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("else")),
- cut(tuple((
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(|i| parse_template(i, s)),
- ))),
- ));
- let (i, (_, pws, _, (nws, _, block))) = p(i)?;
- Ok((i, (Ws(pws, nws), Target::Name("_"), block)))
-}
-
-fn when_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> {
- let mut p = tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("when")),
- cut(tuple((
- ws(target),
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(|i| parse_template(i, s)),
- ))),
- ));
- let (i, (_, pws, _, (target, nws, _, block))) = p(i)?;
- Ok((i, (Ws(pws, nws), target, block)))
-}
-
-fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("match")),
- cut(tuple((
- ws(expr_any),
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(tuple((
- ws(many0(ws(value((), |i| block_comment(i, s))))),
- many1(|i| when_block(i, s)),
- cut(tuple((
- opt(|i| match_else_block(i, s)),
- cut(tuple((
- ws(|i| tag_block_start(i, s)),
- opt(expr_handle_ws),
- ws(keyword("endmatch")),
- opt(expr_handle_ws),
- ))),
- ))),
- ))),
- ))),
- ));
- let (i, (pws1, _, (expr, nws1, _, (_, arms, (else_arm, (_, pws2, _, nws2)))))) = p(i)?;
-
- let mut arms = arms;
- if let Some(arm) = else_arm {
- arms.push(arm);
- }
-
- Ok((i, Node::Match(Ws(pws1, nws1), expr, arms, Ws(pws2, nws2))))
-}
-
-fn block_let(i: &str) -> IResult<&str, Node<'_>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(alt((keyword("let"), keyword("set")))),
- cut(tuple((
- ws(target),
- opt(tuple((ws(char('=')), ws(expr_any)))),
- opt(expr_handle_ws),
- ))),
- ));
- let (i, (pws, _, (var, val, nws))) = p(i)?;
-
- Ok((
- i,
- if let Some((_, val)) = val {
- Node::Let(Ws(pws, nws), var, val)
- } else {
- Node::LetDecl(Ws(pws, nws), var)
- },
- ))
-}
-
-fn parse_loop_content<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec<Node<'a>>> {
- s.loop_depth.set(s.loop_depth.get() + 1);
- let result = parse_template(i, s);
- s.loop_depth.set(s.loop_depth.get() - 1);
- result
-}
-
-fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let if_cond = preceded(ws(keyword("if")), cut(ws(expr_any)));
- let else_block = |i| {
- let mut p = preceded(
- ws(keyword("else")),
- cut(tuple((
- opt(expr_handle_ws),
- delimited(
- |i| tag_block_end(i, s),
- |i| parse_template(i, s),
- |i| tag_block_start(i, s),
- ),
- opt(expr_handle_ws),
- ))),
- );
- let (i, (pws, nodes, nws)) = p(i)?;
- Ok((i, (pws, nodes, nws)))
- };
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("for")),
- cut(tuple((
- ws(target),
- ws(keyword("in")),
- cut(tuple((
- ws(expr_any),
- opt(if_cond),
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- cut(tuple((
- |i| parse_loop_content(i, s),
- cut(tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- opt(else_block),
- ws(keyword("endfor")),
- opt(expr_handle_ws),
- ))),
- ))),
- ))),
- ))),
- ));
- let (i, (pws1, _, (var, _, (iter, cond, nws1, _, (body, (_, pws2, else_block, _, nws2)))))) =
- p(i)?;
- let (nws3, else_block, pws3) = else_block.unwrap_or_default();
- Ok((
- i,
- Node::Loop(Loop {
- ws1: Ws(pws1, nws1),
- var,
- iter,
- cond,
- body,
- ws2: Ws(pws2, nws3),
- else_block,
- ws3: Ws(pws3, nws2),
- }),
- ))
-}
-
-fn block_extends(i: &str) -> IResult<&str, Node<'_>> {
- let (i, (_, name)) = tuple((ws(keyword("extends")), ws(str_lit)))(i)?;
- Ok((i, Node::Extends(name)))
-}
-
-fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut start = tuple((
- opt(expr_handle_ws),
- ws(keyword("block")),
- cut(tuple((ws(identifier), opt(expr_handle_ws), |i| {
- tag_block_end(i, s)
- }))),
- ));
- let (i, (pws1, _, (name, nws1, _))) = start(i)?;
-
- let mut end = cut(tuple((
- |i| parse_template(i, s),
- cut(tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("endblock")),
- cut(tuple((opt(ws(keyword(name))), opt(expr_handle_ws)))),
- ))),
- )));
- let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?;
-
- Ok((
- i,
- Node::BlockDef(Ws(pws1, nws1), name, contents, Ws(pws2, nws2)),
- ))
-}
-
-fn block_include(i: &str) -> IResult<&str, Node<'_>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("include")),
- cut(pair(ws(str_lit), opt(expr_handle_ws))),
- ));
- let (i, (pws, _, (name, nws))) = p(i)?;
- Ok((i, Node::Include(Ws(pws, nws), name)))
-}
-
-fn block_import(i: &str) -> IResult<&str, Node<'_>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("import")),
- cut(tuple((
- ws(str_lit),
- ws(keyword("as")),
- cut(pair(ws(identifier), opt(expr_handle_ws))),
- ))),
- ));
- let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?;
- Ok((i, Node::Import(Ws(pws, nws), name, scope)))
-}
-
-fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut start = tuple((
- opt(expr_handle_ws),
- ws(keyword("macro")),
- cut(tuple((
- ws(identifier),
- ws(parameters),
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- ))),
- ));
- let (i, (pws1, _, (name, params, nws1, _))) = start(i)?;
-
- let mut end = cut(tuple((
- |i| parse_template(i, s),
- cut(tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("endmacro")),
- cut(tuple((opt(ws(keyword(name))), opt(expr_handle_ws)))),
- ))),
- )));
- let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?;
-
- assert_ne!(name, "super", "invalid macro name 'super'");
-
- Ok((
- i,
- Node::Macro(
- name,
- Macro {
- ws1: Ws(pws1, nws1),
- args: params,
- nodes: contents,
- ws2: Ws(pws2, nws2),
- },
- ),
- ))
-}
-
-fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let endraw = tuple((
- |i| tag_block_start(i, s),
- opt(expr_handle_ws),
- ws(keyword("endraw")),
- opt(expr_handle_ws),
- peek(|i| tag_block_end(i, s)),
- ));
-
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("raw")),
- cut(tuple((
- opt(expr_handle_ws),
- |i| tag_block_end(i, s),
- consumed(skip_till(endraw)),
- ))),
- ));
-
- let (_, (pws1, _, (nws1, _, (contents, (i, (_, pws2, _, nws2, _)))))) = p(i)?;
- let (lws, val, rws) = match split_ws_parts(contents) {
- Node::Lit(lws, val, rws) => (lws, val, rws),
- _ => unreachable!(),
- };
- let ws1 = Ws(pws1, nws1);
- let ws2 = Ws(pws2, nws2);
- Ok((i, Node::Raw(ws1, lws, val, rws, ws2)))
-}
-
-fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("break")),
- opt(expr_handle_ws),
- ));
- let (j, (pws, _, nws)) = p(i)?;
- if s.loop_depth.get() == 0 {
- return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag)));
- }
- Ok((j, Node::Break(Ws(pws, nws))))
-}
-
-fn continue_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- opt(expr_handle_ws),
- ws(keyword("continue")),
- opt(expr_handle_ws),
- ));
- let (j, (pws, _, nws)) = p(i)?;
- if s.loop_depth.get() == 0 {
- return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag)));
- }
- Ok((j, Node::Continue(Ws(pws, nws))))
-}
-
-fn block_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- |i| tag_block_start(i, s),
- alt((
- block_call,
- block_let,
- |i| block_if(i, s),
- |i| block_for(i, s),
- |i| block_match(i, s),
- block_extends,
- block_include,
- block_import,
- |i| block_block(i, s),
- |i| block_macro(i, s),
- |i| block_raw(i, s),
- |i| break_statement(i, s),
- |i| continue_statement(i, s),
- )),
- cut(|i| tag_block_end(i, s)),
- ));
- let (i, (_, contents, _)) = p(i)?;
- Ok((i, contents))
-}
-
-fn block_comment_body<'a>(mut i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- let mut level = 0;
- loop {
- let (end, tail) = take_until(s.syntax.comment_end.as_str())(i)?;
- match take_until::<_, _, Error<_>>(s.syntax.comment_start.as_str())(i) {
- Ok((start, _)) if start.as_ptr() < end.as_ptr() => {
- level += 1;
- i = &start[2..];
- }
- _ if level > 0 => {
- level -= 1;
- i = &end[2..];
- }
- _ => return Ok((end, tail)),
- }
- }
-}
-
-fn block_comment<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {
- let mut p = tuple((
- |i| tag_comment_start(i, s),
- cut(tuple((
- opt(expr_handle_ws),
- |i| block_comment_body(i, s),
- |i| tag_comment_end(i, s),
- ))),
- ));
- let (i, (_, (pws, tail, _))) = p(i)?;
- let nws = if tail.ends_with('-') {
- Some(Whitespace::Suppress)
- } else if tail.ends_with('+') {
- Some(Whitespace::Preserve)
- } else if tail.ends_with('~') {
- Some(Whitespace::Minimize)
- } else {
- None
- };
- Ok((i, Node::Comment(Ws(pws, nws))))
-}
-
-fn parse_template<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec<Node<'a>>> {
- many0(alt((
- complete(|i| take_content(i, s)),
- complete(|i| block_comment(i, s)),
- complete(|i| expr_node(i, s)),
- complete(|i| block_node(i, s)),
- )))(i)
-}
-
-fn tag_block_start<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.block_start.as_str())(i)
-}
-fn tag_block_end<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.block_end.as_str())(i)
-}
-fn tag_comment_start<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.comment_start.as_str())(i)
-}
-fn tag_comment_end<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.comment_end.as_str())(i)
-}
-fn tag_expr_start<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.expr_start.as_str())(i)
-}
-fn tag_expr_end<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
- tag(s.syntax.expr_end.as_str())(i)
-}
-
-pub(crate) fn parse<'a>(src: &'a str, syntax: &'a Syntax) -> Result<Vec<Node<'a>>, CompileError> {
- let state = State {
- syntax,
- loop_depth: Cell::new(0),
- };
- match parse_template(src, &state) {
- Ok((left, res)) => {
- if !left.is_empty() {
- Err(format!("unable to parse template:\n\n{left:?}").into())
- } else {
- Ok(res)
- }
- }
-
- Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => {
- let nom::error::Error { input, .. } = err;
- let offset = src.len() - input.len();
- let (source_before, source_after) = src.split_at(offset);
-
- let source_after = match source_after.char_indices().enumerate().take(41).last() {
- Some((40, (i, _))) => format!("{:?}...", &source_after[..i]),
- _ => format!("{source_after:?}"),
- };
-
- let (row, last_line) = source_before.lines().enumerate().last().unwrap();
- let column = last_line.chars().count();
-
- let msg = format!(
- "problems parsing template source at row {}, column {} near:\n{}",
- row + 1,
- column,
- source_after,
- );
- Err(msg.into())
- }
-
- Err(nom::Err::Incomplete(_)) => Err("parsing incomplete".into()),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{Expr, Node, Whitespace, Ws};
- use crate::config::Syntax;
-
- fn check_ws_split(s: &str, res: &(&str, &str, &str)) {
- match super::split_ws_parts(s) {
- Node::Lit(lws, s, rws) => {
- assert_eq!(lws, res.0);
- assert_eq!(s, res.1);
- assert_eq!(rws, res.2);
- }
- _ => {
- panic!("fail");
- }
- }
- }
-
- #[test]
- fn test_ws_splitter() {
- check_ws_split("", &("", "", ""));
- check_ws_split("a", &("", "a", ""));
- check_ws_split("\ta", &("\t", "a", ""));
- check_ws_split("b\n", &("", "b", "\n"));
- check_ws_split(" \t\r\n", &(" \t\r\n", "", ""));
- }
-
- #[test]
- #[should_panic]
- fn test_invalid_block() {
- super::parse("{% extend \"blah\" %}", &Syntax::default()).unwrap();
- }
-
- #[test]
- fn test_parse_filter() {
- use Expr::*;
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ strvar|e }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Filter("e", vec![Var("strvar")]),)],
- );
- assert_eq!(
- super::parse("{{ 2|abs }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Filter("abs", vec![NumLit("2")]),)],
- );
- assert_eq!(
- super::parse("{{ -2|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter("abs", vec![Unary("-", NumLit("2").into())]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1 - 2)|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter(
- "abs",
- vec![Group(
- BinOp("-", NumLit("1").into(), NumLit("2").into()).into()
- )]
- ),
- )],
- );
- }
-
- #[test]
- fn test_parse_numbers() {
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ 2 }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::NumLit("2"),)],
- );
- assert_eq!(
- super::parse("{{ 2.5 }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::NumLit("2.5"),)],
- );
- }
-
- #[test]
- fn test_parse_var() {
- let s = Syntax::default();
-
- assert_eq!(
- super::parse("{{ foo }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Var("foo"))],
- );
- assert_eq!(
- super::parse("{{ foo_bar }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Var("foo_bar"))],
- );
-
- assert_eq!(
- super::parse("{{ none }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Var("none"))],
- );
- }
-
- #[test]
- fn test_parse_const() {
- let s = Syntax::default();
-
- assert_eq!(
- super::parse("{{ FOO }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO"]))],
- );
- assert_eq!(
- super::parse("{{ FOO_BAR }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO_BAR"]))],
- );
-
- assert_eq!(
- super::parse("{{ NONE }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Path(vec!["NONE"]))],
- );
- }
-
- #[test]
- fn test_parse_path() {
- let s = Syntax::default();
-
- assert_eq!(
- super::parse("{{ None }}", &s).unwrap(),
- vec![Node::Expr(Ws(None, None), Expr::Path(vec!["None"]))],
- );
- assert_eq!(
- super::parse("{{ Some(123) }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Path(vec!["Some"])),
- vec![Expr::NumLit("123")]
- ),
- )],
- );
-
- assert_eq!(
- super::parse("{{ Ok(123) }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(Box::new(Expr::Path(vec!["Ok"])), vec![Expr::NumLit("123")]),
- )],
- );
- assert_eq!(
- super::parse("{{ Err(123) }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(Box::new(Expr::Path(vec!["Err"])), vec![Expr::NumLit("123")]),
- )],
- );
- }
-
- #[test]
- fn test_parse_var_call() {
- assert_eq!(
- super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Var("function")),
- vec![Expr::StrLit("123"), Expr::NumLit("3")]
- ),
- )],
- );
- }
-
- #[test]
- fn test_parse_path_call() {
- let s = Syntax::default();
-
- assert_eq!(
- super::parse("{{ Option::None }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Path(vec!["Option", "None"])
- )],
- );
- assert_eq!(
- super::parse("{{ Option::Some(123) }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Path(vec!["Option", "Some"])),
- vec![Expr::NumLit("123")],
- ),
- )],
- );
-
- assert_eq!(
- super::parse("{{ self::function(\"123\", 3) }}", &s).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Path(vec!["self", "function"])),
- vec![Expr::StrLit("123"), Expr::NumLit("3")],
- ),
- )],
- );
- }
-
- #[test]
- fn test_parse_root_path() {
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ std::string::String::new() }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Path(vec!["std", "string", "String", "new"])),
- vec![]
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Expr::Call(
- Box::new(Expr::Path(vec!["", "std", "string", "String", "new"])),
- vec![]
- ),
- )],
- );
- }
-
- #[test]
- fn change_delimiters_parse_filter() {
- let syntax = Syntax {
- expr_start: "{=".to_owned(),
- expr_end: "=}".to_owned(),
- ..Syntax::default()
- };
-
- super::parse("{= strvar|e =}", &syntax).unwrap();
- }
-
- #[test]
- fn test_precedence() {
- use Expr::*;
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ a + b == c }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "==",
- BinOp("+", Var("a").into(), Var("b").into()).into(),
- Var("c").into(),
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a + b * c - d / e }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "-",
- BinOp(
- "+",
- Var("a").into(),
- BinOp("*", Var("b").into(), Var("c").into()).into(),
- )
- .into(),
- BinOp("/", Var("d").into(), Var("e").into()).into(),
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a * (b + c) / -d }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "/",
- BinOp(
- "*",
- Var("a").into(),
- Group(BinOp("+", Var("b").into(), Var("c").into()).into()).into()
- )
- .into(),
- Unary("-", Var("d").into()).into()
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a || b && c || d && e }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "||",
- BinOp(
- "||",
- Var("a").into(),
- BinOp("&&", Var("b").into(), Var("c").into()).into(),
- )
- .into(),
- BinOp("&&", Var("d").into(), Var("e").into()).into(),
- )
- )],
- );
- }
-
- #[test]
- fn test_associativity() {
- use Expr::*;
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ a + b + c }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "+",
- BinOp("+", Var("a").into(), Var("b").into()).into(),
- Var("c").into()
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a * b * c }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "*",
- BinOp("*", Var("a").into(), Var("b").into()).into(),
- Var("c").into()
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a && b && c }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "&&",
- BinOp("&&", Var("a").into(), Var("b").into()).into(),
- Var("c").into()
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a + b - c + d }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "+",
- BinOp(
- "-",
- BinOp("+", Var("a").into(), Var("b").into()).into(),
- Var("c").into()
- )
- .into(),
- Var("d").into()
- )
- )],
- );
- assert_eq!(
- super::parse("{{ a == b != c > d > e == f }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "==",
- BinOp(
- ">",
- BinOp(
- ">",
- BinOp(
- "!=",
- BinOp("==", Var("a").into(), Var("b").into()).into(),
- Var("c").into()
- )
- .into(),
- Var("d").into()
- )
- .into(),
- Var("e").into()
- )
- .into(),
- Var("f").into()
- )
- )],
- );
- }
-
- #[test]
- fn test_odd_calls() {
- use Expr::*;
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ a[b](c) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Call(
- Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))),
- vec![Var("c")],
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ (a + b)(c) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Call(
- Box::new(Group(Box::new(BinOp(
- "+",
- Box::new(Var("a")),
- Box::new(Var("b"))
- )))),
- vec![Var("c")],
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ a + b(c) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "+",
- Box::new(Var("a")),
- Box::new(Call(Box::new(Var("b")), vec![Var("c")])),
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ (-a)(b) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Call(
- Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))),
- vec![Var("b")],
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ -a(b) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),),
- )],
- );
- }
-
- #[test]
- fn test_parse_comments() {
- let s = &Syntax::default();
-
- assert_eq!(
- super::parse("{##}", s).unwrap(),
- vec![Node::Comment(Ws(None, None))],
- );
- assert_eq!(
- super::parse("{#- #}", s).unwrap(),
- vec![Node::Comment(Ws(Some(Whitespace::Suppress), None))],
- );
- assert_eq!(
- super::parse("{# -#}", s).unwrap(),
- vec![Node::Comment(Ws(None, Some(Whitespace::Suppress)))],
- );
- assert_eq!(
- super::parse("{#--#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Suppress),
- Some(Whitespace::Suppress)
- ))],
- );
- assert_eq!(
- super::parse("{#- foo\n bar -#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Suppress),
- Some(Whitespace::Suppress)
- ))],
- );
- assert_eq!(
- super::parse("{#- foo\n {#- bar\n -#} baz -#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Suppress),
- Some(Whitespace::Suppress)
- ))],
- );
- assert_eq!(
- super::parse("{#+ #}", s).unwrap(),
- vec![Node::Comment(Ws(Some(Whitespace::Preserve), None))],
- );
- assert_eq!(
- super::parse("{# +#}", s).unwrap(),
- vec![Node::Comment(Ws(None, Some(Whitespace::Preserve)))],
- );
- assert_eq!(
- super::parse("{#++#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Preserve),
- Some(Whitespace::Preserve)
- ))],
- );
- assert_eq!(
- super::parse("{#+ foo\n bar +#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Preserve),
- Some(Whitespace::Preserve)
- ))],
- );
- assert_eq!(
- super::parse("{#+ foo\n {#+ bar\n +#} baz -+#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Preserve),
- Some(Whitespace::Preserve)
- ))],
- );
- assert_eq!(
- super::parse("{#~ #}", s).unwrap(),
- vec![Node::Comment(Ws(Some(Whitespace::Minimize), None))],
- );
- assert_eq!(
- super::parse("{# ~#}", s).unwrap(),
- vec![Node::Comment(Ws(None, Some(Whitespace::Minimize)))],
- );
- assert_eq!(
- super::parse("{#~~#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Minimize),
- Some(Whitespace::Minimize)
- ))],
- );
- assert_eq!(
- super::parse("{#~ foo\n bar ~#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Minimize),
- Some(Whitespace::Minimize)
- ))],
- );
- assert_eq!(
- super::parse("{#~ foo\n {#~ bar\n ~#} baz -~#}", s).unwrap(),
- vec![Node::Comment(Ws(
- Some(Whitespace::Minimize),
- Some(Whitespace::Minimize)
- ))],
- );
-
- assert_eq!(
- super::parse("{# foo {# bar #} {# {# baz #} qux #} #}", s).unwrap(),
- vec![Node::Comment(Ws(None, None))],
- );
- }
-
- #[test]
- fn test_parse_tuple() {
- use super::Expr::*;
- let syntax = Syntax::default();
- assert_eq!(
- super::parse("{{ () }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Tuple(vec![]),)],
- );
- assert_eq!(
- super::parse("{{ (1) }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Group(Box::new(NumLit("1"))),)],
- );
- assert_eq!(
- super::parse("{{ (1,) }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
- );
- assert_eq!(
- super::parse("{{ (1, ) }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
- );
- assert_eq!(
- super::parse("{{ (1 ,) }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
- );
- assert_eq!(
- super::parse("{{ (1 , ) }}", &syntax).unwrap(),
- vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)],
- );
- assert_eq!(
- super::parse("{{ (1, 2) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Tuple(vec![NumLit("1"), NumLit("2")]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1, 2,) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Tuple(vec![NumLit("1"), NumLit("2")]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1, 2, 3) }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]),
- )],
- );
- assert_eq!(
- super::parse("{{ ()|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter("abs", vec![Tuple(vec![])]),
- )],
- );
- assert_eq!(
- super::parse("{{ () | abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))),
- )],
- );
- assert_eq!(
- super::parse("{{ (1)|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter("abs", vec![Group(Box::new(NumLit("1")))]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1) | abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "|",
- Box::new(Group(Box::new(NumLit("1")))),
- Box::new(Var("abs"))
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ (1,)|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter("abs", vec![Tuple(vec![NumLit("1")])]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1,) | abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "|",
- Box::new(Tuple(vec![NumLit("1")])),
- Box::new(Var("abs"))
- ),
- )],
- );
- assert_eq!(
- super::parse("{{ (1, 2)|abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]),
- )],
- );
- assert_eq!(
- super::parse("{{ (1, 2) | abs }}", &syntax).unwrap(),
- vec![Node::Expr(
- Ws(None, None),
- BinOp(
- "|",
- Box::new(Tuple(vec![NumLit("1"), NumLit("2")])),
- Box::new(Var("abs"))
- ),
- )],
- );
- }
-
- #[test]
- fn test_missing_space_after_kw() {
- let syntax = Syntax::default();
- let err = super::parse("{%leta=b%}", &syntax).unwrap_err();
- assert!(matches!(
- &*err.msg,
- "unable to parse template:\n\n\"{%leta=b%}\""
- ));
- }
-}