mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-28 05:21:14 +00:00
parser: introduce askama_parser::expr::BinOp
This commit is contained in:
parent
edfa31f9cf
commit
9f882e2ca7
@ -581,9 +581,9 @@ fn is_copyable_within_op(expr: &Expr<'_>, within_op: bool) -> bool {
|
||||
| Expr::NumLit(_, _)
|
||||
| Expr::StrLit(_)
|
||||
| Expr::CharLit(_)
|
||||
| Expr::BinOp(_, _, _) => true,
|
||||
| Expr::BinOp(_)
|
||||
| Expr::Range(..) => true,
|
||||
Expr::Unary(.., expr) => is_copyable_within_op(expr, true),
|
||||
Expr::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.
|
||||
|
@ -75,7 +75,7 @@ impl<'a> Generator<'a, '_> {
|
||||
ref generics,
|
||||
}) => self.visit_filter(ctx, buf, name, arguments, generics, expr.span())?,
|
||||
Expr::Unary(op, ref inner) => self.visit_unary(ctx, buf, op, inner)?,
|
||||
Expr::BinOp(op, ref left, ref right) => self.visit_binop(ctx, buf, op, left, right)?,
|
||||
Expr::BinOp(ref v) => self.visit_binop(ctx, buf, v.op, &v.lhs, &v.rhs)?,
|
||||
Expr::Range(op, ref left, ref right) => {
|
||||
self.visit_range(ctx, buf, op, left.as_deref(), right.as_deref())?
|
||||
}
|
||||
@ -113,9 +113,9 @@ impl<'a> Generator<'a, '_> {
|
||||
expr: &WithSpan<'a, Expr<'a>>,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
match **expr {
|
||||
Expr::BinOp(op @ ("||" | "&&"), ref left, _) => {
|
||||
let ret = self.visit_expr(ctx, buf, left)?;
|
||||
buf.write(format_args!(" {op} "));
|
||||
Expr::BinOp(ref v) if matches!(v.op, "&&" | "||") => {
|
||||
let ret = self.visit_expr(ctx, buf, &v.lhs)?;
|
||||
buf.write(format_args!(" {} ", &v.op));
|
||||
return Ok(ret);
|
||||
}
|
||||
Expr::Unary(op, ref inner) => {
|
||||
@ -135,8 +135,8 @@ impl<'a> Generator<'a, '_> {
|
||||
prev_display_wrap: DisplayWrap,
|
||||
) -> Result<DisplayWrap, CompileError> {
|
||||
match **expr {
|
||||
Expr::BinOp("||" | "&&", _, ref right) => {
|
||||
self.visit_condition(ctx, buf, right)?;
|
||||
Expr::BinOp(ref v) if matches!(v.op, "&&" | "||") => {
|
||||
self.visit_condition(ctx, buf, &v.rhs)?;
|
||||
Ok(DisplayWrap::Unwrapped)
|
||||
}
|
||||
Expr::Unary(_, ref inner) => {
|
||||
@ -160,10 +160,10 @@ impl<'a> Generator<'a, '_> {
|
||||
buf.write('!');
|
||||
self.visit_condition(ctx, buf, expr)?;
|
||||
}
|
||||
Expr::BinOp(op @ ("&&" | "||"), left, right) => {
|
||||
self.visit_condition(ctx, buf, left)?;
|
||||
buf.write(format_args!(" {op} "));
|
||||
self.visit_condition(ctx, buf, right)?;
|
||||
Expr::BinOp(v) if matches!(v.op, "&&" | "||") => {
|
||||
self.visit_condition(ctx, buf, &v.lhs)?;
|
||||
buf.write(format_args!(" {} ", v.op));
|
||||
self.visit_condition(ctx, buf, &v.rhs)?;
|
||||
}
|
||||
Expr::Group(expr) => {
|
||||
buf.write('(');
|
||||
|
@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
|
||||
use std::fmt::{self, Debug, Write};
|
||||
use std::mem;
|
||||
|
||||
use parser::expr::BinOp;
|
||||
use parser::node::{
|
||||
Call, Comment, Cond, CondTest, FilterBlock, If, Include, Let, Lit, Loop, Macro, Match,
|
||||
Whitespace, Ws,
|
||||
@ -232,15 +233,15 @@ impl<'a> Generator<'a, '_> {
|
||||
}
|
||||
}
|
||||
Expr::Unary(_, _) => (EvaluatedResult::Unknown, WithSpan::new(expr, span)),
|
||||
Expr::BinOp("&&", left, right) => {
|
||||
Expr::BinOp(v) if v.op == "&&" => {
|
||||
let (result_left, expr_left) =
|
||||
self.evaluate_condition(*left, only_contains_is_defined);
|
||||
self.evaluate_condition(v.lhs, only_contains_is_defined);
|
||||
if result_left == EvaluatedResult::AlwaysFalse {
|
||||
// The right side of the `&&` won't be evaluated, no need to go any further.
|
||||
return (result_left, WithSpan::new(Expr::BoolLit(false), ""));
|
||||
}
|
||||
let (result_right, expr_right) =
|
||||
self.evaluate_condition(*right, only_contains_is_defined);
|
||||
self.evaluate_condition(v.rhs, only_contains_is_defined);
|
||||
match (result_left, result_right) {
|
||||
(EvaluatedResult::AlwaysTrue, EvaluatedResult::AlwaysTrue) => (
|
||||
EvaluatedResult::AlwaysTrue,
|
||||
@ -248,31 +249,25 @@ impl<'a> Generator<'a, '_> {
|
||||
),
|
||||
(_, EvaluatedResult::AlwaysFalse) => (
|
||||
EvaluatedResult::AlwaysFalse,
|
||||
WithSpan::new(
|
||||
Expr::BinOp("&&", Box::new(expr_left), Box::new(expr_right)),
|
||||
span,
|
||||
),
|
||||
bin_op(span, "&&", expr_left, expr_right),
|
||||
),
|
||||
(EvaluatedResult::AlwaysTrue, _) => (result_right, expr_right),
|
||||
(_, EvaluatedResult::AlwaysTrue) => (result_left, expr_left),
|
||||
_ => (
|
||||
EvaluatedResult::Unknown,
|
||||
WithSpan::new(
|
||||
Expr::BinOp("&&", Box::new(expr_left), Box::new(expr_right)),
|
||||
span,
|
||||
),
|
||||
bin_op(span, "&&", expr_left, expr_right),
|
||||
),
|
||||
}
|
||||
}
|
||||
Expr::BinOp("||", left, right) => {
|
||||
Expr::BinOp(v) if v.op == "||" => {
|
||||
let (result_left, expr_left) =
|
||||
self.evaluate_condition(*left, only_contains_is_defined);
|
||||
self.evaluate_condition(v.lhs, only_contains_is_defined);
|
||||
if result_left == EvaluatedResult::AlwaysTrue {
|
||||
// The right side of the `||` won't be evaluated, no need to go any further.
|
||||
return (result_left, WithSpan::new(Expr::BoolLit(true), ""));
|
||||
}
|
||||
let (result_right, expr_right) =
|
||||
self.evaluate_condition(*right, only_contains_is_defined);
|
||||
self.evaluate_condition(v.rhs, only_contains_is_defined);
|
||||
match (result_left, result_right) {
|
||||
(EvaluatedResult::AlwaysFalse, EvaluatedResult::AlwaysFalse) => (
|
||||
EvaluatedResult::AlwaysFalse,
|
||||
@ -280,23 +275,17 @@ impl<'a> Generator<'a, '_> {
|
||||
),
|
||||
(_, EvaluatedResult::AlwaysTrue) => (
|
||||
EvaluatedResult::AlwaysTrue,
|
||||
WithSpan::new(
|
||||
Expr::BinOp("||", Box::new(expr_left), Box::new(expr_right)),
|
||||
span,
|
||||
),
|
||||
bin_op(span, "||", expr_left, expr_right),
|
||||
),
|
||||
(EvaluatedResult::AlwaysFalse, _) => (result_right, expr_right),
|
||||
(_, EvaluatedResult::AlwaysFalse) => (result_left, expr_left),
|
||||
_ => (
|
||||
EvaluatedResult::Unknown,
|
||||
WithSpan::new(
|
||||
Expr::BinOp("||", Box::new(expr_left), Box::new(expr_right)),
|
||||
span,
|
||||
),
|
||||
bin_op(span, "||", expr_left, expr_right),
|
||||
),
|
||||
}
|
||||
}
|
||||
Expr::BinOp(_, _, _) => {
|
||||
Expr::BinOp(_) => {
|
||||
*only_contains_is_defined = false;
|
||||
(EvaluatedResult::Unknown, WithSpan::new(expr, span))
|
||||
}
|
||||
@ -386,14 +375,18 @@ impl<'a> Generator<'a, '_> {
|
||||
// left expression has been handled but before the right expression is handled
|
||||
// but this one should have access to the let-bound variable.
|
||||
match &**expr {
|
||||
Expr::BinOp(op @ ("||" | "&&"), ref left, ref right) => {
|
||||
Expr::BinOp(v) if matches!(v.op, "||" | "&&") => {
|
||||
let display_wrap =
|
||||
this.visit_expr_first(ctx, &mut expr_buf, left)?;
|
||||
this.visit_expr_first(ctx, &mut expr_buf, &v.lhs)?;
|
||||
this.visit_target(buf, true, true, target);
|
||||
this.visit_expr_not_first(ctx, &mut expr_buf, left, display_wrap)?;
|
||||
buf.write(format_args!("= &{expr_buf}"));
|
||||
buf.write(format_args!(" {op} "));
|
||||
this.visit_condition(ctx, buf, right)?;
|
||||
this.visit_expr_not_first(
|
||||
ctx,
|
||||
&mut expr_buf,
|
||||
&v.lhs,
|
||||
display_wrap,
|
||||
)?;
|
||||
buf.write(format_args!("= &{expr_buf} {} ", v.op));
|
||||
this.visit_condition(ctx, buf, &v.rhs)?;
|
||||
}
|
||||
_ => {
|
||||
let display_wrap =
|
||||
@ -1392,6 +1385,15 @@ impl<'a> Generator<'a, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn bin_op<'a>(
|
||||
span: impl Into<Span<'a>>,
|
||||
op: &'a str,
|
||||
lhs: WithSpan<'a, Expr<'a>>,
|
||||
rhs: WithSpan<'a, Expr<'a>>,
|
||||
) -> WithSpan<'a, Expr<'a>> {
|
||||
WithSpan::new(Expr::BinOp(Box::new(BinOp { op, lhs, rhs })), span)
|
||||
}
|
||||
|
||||
struct CondInfo<'a> {
|
||||
cond: &'a WithSpan<'a, Cond<'a>>,
|
||||
cond_expr: Option<WithSpan<'a, Expr<'a>>>,
|
||||
@ -1645,7 +1647,7 @@ fn is_cacheable(expr: &WithSpan<'_, Expr<'_>>) -> bool {
|
||||
Expr::Index(lhs, rhs) => is_cacheable(lhs) && is_cacheable(rhs),
|
||||
Expr::Filter(Filter { arguments, .. }) => arguments.iter().all(is_cacheable),
|
||||
Expr::Unary(_, arg) => is_cacheable(arg),
|
||||
Expr::BinOp(_, lhs, rhs) => is_cacheable(lhs) && is_cacheable(rhs),
|
||||
Expr::BinOp(v) => is_cacheable(&v.lhs) && is_cacheable(&v.rhs),
|
||||
Expr::IsDefined(_) | Expr::IsNotDefined(_) => true,
|
||||
Expr::Range(_, lhs, rhs) => {
|
||||
lhs.as_ref().is_none_or(|v| is_cacheable(v))
|
||||
|
@ -19,21 +19,31 @@ use crate::{
|
||||
macro_rules! expr_prec_layer {
|
||||
( $name:ident, $inner:ident, $op:expr ) => {
|
||||
fn $name(i: &mut &'a str, level: Level<'_>) -> ParseResult<'a, WithSpan<'a, Self>> {
|
||||
let mut level_guard = level.guard();
|
||||
let start = *i;
|
||||
let mut expr = Self::$inner(i, level)?;
|
||||
let mut i_before = *i;
|
||||
let mut right = opt((ws($op), |i: &mut _| Self::$inner(i, level)));
|
||||
while let Some((op, right)) = right.parse_next(i)? {
|
||||
level_guard.nest(i_before)?;
|
||||
i_before = *i;
|
||||
expr = WithSpan::new(Self::BinOp(op, Box::new(expr), Box::new(right)), start);
|
||||
}
|
||||
Ok(expr)
|
||||
expr_prec_layer(i, level, Expr::$inner, |i: &mut _| $op.parse_next(i))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn expr_prec_layer<'a>(
|
||||
i: &mut &'a str,
|
||||
level: Level<'_>,
|
||||
inner: fn(&mut &'a str, Level<'_>) -> ParseResult<'a, WithSpan<'a, Expr<'a>>>,
|
||||
op: fn(&mut &'a str) -> ParseResult<'a>,
|
||||
) -> ParseResult<'a, WithSpan<'a, Expr<'a>>> {
|
||||
let start = *i;
|
||||
let mut expr = inner(i, level)?;
|
||||
|
||||
let mut i_before = *i;
|
||||
let mut level_guard = level.guard();
|
||||
while let Some((op, rhs)) = opt((ws(op), |i: &mut _| inner(i, level))).parse_next(i)? {
|
||||
level_guard.nest(i_before)?;
|
||||
i_before = *i;
|
||||
expr = WithSpan::new(Expr::BinOp(Box::new(BinOp { op, lhs: expr, rhs })), start);
|
||||
}
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct Allowed {
|
||||
underscore: bool,
|
||||
@ -83,10 +93,14 @@ fn check_expr<'a>(expr: &WithSpan<'a, Expr<'a>>, allowed: Allowed) -> Result<(),
|
||||
check_expr(elem, Allowed::default())
|
||||
}
|
||||
}
|
||||
Expr::Index(elem1, elem2) | Expr::BinOp(_, elem1, elem2) => {
|
||||
Expr::Index(elem1, elem2) => {
|
||||
check_expr(elem1, Allowed::default())?;
|
||||
check_expr(elem2, Allowed::default())
|
||||
}
|
||||
Expr::BinOp(v) => {
|
||||
check_expr(&v.lhs, Allowed::default())?;
|
||||
check_expr(&v.rhs, Allowed::default())
|
||||
}
|
||||
Expr::Range(_, elem1, elem2) => {
|
||||
if let Some(elem1) = elem1 {
|
||||
check_expr(elem1, Allowed::default())?;
|
||||
@ -155,11 +169,7 @@ pub enum Expr<'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>>>,
|
||||
),
|
||||
BinOp(Box<BinOp<'a>>),
|
||||
Range(
|
||||
&'a str,
|
||||
Option<Box<WithSpan<'a, Expr<'a>>>>,
|
||||
@ -186,6 +196,13 @@ pub enum Expr<'a> {
|
||||
ArgumentPlaceholder,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct BinOp<'a> {
|
||||
pub op: &'a str,
|
||||
pub lhs: WithSpan<'a, Expr<'a>>,
|
||||
pub rhs: WithSpan<'a, Expr<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
pub(super) fn arguments(
|
||||
i: &mut &'a str,
|
||||
@ -320,7 +337,7 @@ impl<'a> Expr<'a> {
|
||||
let Some((op, rhs)) = opt(right).parse_next(i)? else {
|
||||
return Ok(expr);
|
||||
};
|
||||
let expr = WithSpan::new(Self::BinOp(op, Box::new(expr), Box::new(rhs)), start);
|
||||
let expr = WithSpan::new(Expr::BinOp(Box::new(BinOp { op, lhs: expr, rhs })), start);
|
||||
|
||||
if let Some((op2, _)) = opt(right).parse_next(i)? {
|
||||
return Err(ErrMode::Cut(ErrorContext::new(
|
||||
@ -583,8 +600,8 @@ impl<'a> Expr<'a> {
|
||||
match self {
|
||||
Self::BoolLit(_) | Self::IsDefined(_) | Self::IsNotDefined(_) => true,
|
||||
Self::Unary(_, expr) | Self::Group(expr) => expr.contains_bool_lit_or_is_defined(),
|
||||
Self::BinOp("&&" | "||", left, right) => {
|
||||
left.contains_bool_lit_or_is_defined() || right.contains_bool_lit_or_is_defined()
|
||||
Self::BinOp(v) if matches!(v.op, "&&" | "||") => {
|
||||
v.lhs.contains_bool_lit_or_is_defined() || v.rhs.contains_bool_lit_or_is_defined()
|
||||
}
|
||||
Self::NumLit(_, _)
|
||||
| Self::StrLit(_)
|
||||
@ -602,7 +619,7 @@ impl<'a> Expr<'a> {
|
||||
| Self::Index(_, _)
|
||||
| Self::Tuple(_)
|
||||
| Self::Array(_)
|
||||
| Self::BinOp(_, _, _)
|
||||
| Self::BinOp(_)
|
||||
| Self::Path(_)
|
||||
| Self::Concat(_)
|
||||
| Self::LetCond(_)
|
||||
|
@ -423,13 +423,13 @@ impl<'a> CondTest<'a> {
|
||||
ws(|i: &mut _| {
|
||||
let start = *i;
|
||||
let mut expr = Expr::parse(i, s.level, false)?;
|
||||
if let Expr::BinOp(_, _, ref mut right) = expr.inner {
|
||||
if matches!(right.inner, Expr::Var("set" | "let")) {
|
||||
if let Expr::BinOp(v) = &mut expr.inner {
|
||||
if matches!(v.rhs.inner, Expr::Var("set" | "let")) {
|
||||
let _level_guard = s.level.nest(i)?;
|
||||
*i = right.span.as_suffix_of(start).unwrap();
|
||||
*i = v.rhs.span.as_suffix_of(start).unwrap();
|
||||
let start_span = Span::from(*i);
|
||||
let new_right = Self::parse_cond(i, s)?;
|
||||
right.inner = Expr::LetCond(Box::new(WithSpan::new(new_right, start_span)));
|
||||
v.rhs.inner = Expr::LetCond(Box::new(WithSpan::new(new_right, start_span)));
|
||||
}
|
||||
}
|
||||
Ok(expr)
|
||||
|
@ -41,6 +41,14 @@ fn int_lit(i: &str) -> Expr<'_> {
|
||||
Expr::NumLit(i, Num::Int(i, None))
|
||||
}
|
||||
|
||||
fn bin_op<'a>(
|
||||
op: &'a str,
|
||||
lhs: WithSpan<'a, Expr<'a>>,
|
||||
rhs: WithSpan<'a, Expr<'a>>,
|
||||
) -> WithSpan<'a, Expr<'a>> {
|
||||
WithSpan::new_without_span(Expr::BinOp(Box::new(crate::expr::BinOp { op, lhs, rhs })))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_filter() {
|
||||
let syntax = Syntax::default();
|
||||
@ -90,14 +98,11 @@ fn test_parse_filter() {
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::Filter(Filter {
|
||||
name: PathOrIdentifier::Identifier("abs"),
|
||||
arguments: vec![WithSpan::no_span(Expr::Group(
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
"-",
|
||||
WithSpan::no_span(int_lit("1")).into(),
|
||||
WithSpan::no_span(int_lit("2")).into()
|
||||
))
|
||||
.into()
|
||||
))],
|
||||
arguments: vec![WithSpan::no_span(Expr::Group(Box::new(bin_op(
|
||||
"-",
|
||||
WithSpan::no_span(int_lit("1")),
|
||||
WithSpan::no_span(int_lit("2")),
|
||||
))))],
|
||||
generics: vec![],
|
||||
})),
|
||||
)],
|
||||
@ -445,16 +450,15 @@ fn test_precedence() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"==",
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
WithSpan::no_span(Expr::Var("a")).into(),
|
||||
WithSpan::no_span(Expr::Var("b")).into()
|
||||
))
|
||||
.into(),
|
||||
WithSpan::no_span(Expr::Var("c")).into()
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -463,26 +467,23 @@ fn test_precedence() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"-",
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
WithSpan::no_span(Expr::Var("a")).into(),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
bin_op(
|
||||
"*",
|
||||
WithSpan::no_span(Expr::Var("b")).into(),
|
||||
WithSpan::no_span(Expr::Var("c")).into()
|
||||
))
|
||||
.into()
|
||||
))
|
||||
.into(),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
WithSpan::no_span(Expr::Var("b")),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
)
|
||||
),
|
||||
bin_op(
|
||||
"/",
|
||||
WithSpan::no_span(Expr::Var("d")).into(),
|
||||
WithSpan::no_span(Expr::Var("e")).into()
|
||||
))
|
||||
.into(),
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("d")),
|
||||
WithSpan::no_span(Expr::Var("e"))
|
||||
),
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -491,24 +492,22 @@ fn test_precedence() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"/",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"*",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
||||
Expr::BinOp(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::Var("b"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
)
|
||||
)))))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Unary(
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Group(Box::new(bin_op(
|
||||
"+",
|
||||
WithSpan::no_span(Expr::Var("b")),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
))))
|
||||
),
|
||||
WithSpan::no_span(Expr::Unary(
|
||||
"-",
|
||||
Box::new(WithSpan::no_span(Expr::Var("d")))
|
||||
)))
|
||||
))
|
||||
))
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -517,23 +516,23 @@ fn test_precedence() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"||",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"||",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
bin_op(
|
||||
"&&",
|
||||
Box::new(WithSpan::no_span(Expr::Var("b"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
))),
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
WithSpan::no_span(Expr::Var("b")),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
),
|
||||
),
|
||||
bin_op(
|
||||
"&&",
|
||||
Box::new(WithSpan::no_span(Expr::Var("d"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("e")))
|
||||
))),
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("d")),
|
||||
WithSpan::no_span(Expr::Var("e"))
|
||||
),
|
||||
)
|
||||
)],
|
||||
);
|
||||
}
|
||||
@ -547,15 +546,15 @@ fn test_associativity() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -564,15 +563,15 @@ fn test_associativity() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"*",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"*",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -581,15 +580,15 @@ fn test_associativity() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"&&",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"&&",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
)
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
@ -598,19 +597,19 @@ fn test_associativity() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"-",
|
||||
Box::new(WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("c")))
|
||||
))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("d")))
|
||||
))
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("c"))
|
||||
),
|
||||
WithSpan::no_span(Expr::Var("d"))
|
||||
)
|
||||
)],
|
||||
);
|
||||
}
|
||||
@ -639,12 +638,10 @@ fn test_odd_calls() {
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::Call {
|
||||
path: Box::new(WithSpan::no_span(Expr::Group(Box::new(WithSpan::no_span(
|
||||
Expr::BinOp(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Var("b")))
|
||||
)
|
||||
path: Box::new(WithSpan::no_span(Expr::Group(Box::new(bin_op(
|
||||
"+",
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Var("b"))
|
||||
))))),
|
||||
args: vec![WithSpan::no_span(Expr::Var("c"))],
|
||||
generics: vec![],
|
||||
@ -657,15 +654,15 @@ fn test_odd_calls() {
|
||||
.nodes,
|
||||
vec![Node::Expr(
|
||||
Ws(None, None),
|
||||
WithSpan::no_span(Expr::BinOp(
|
||||
bin_op(
|
||||
"+",
|
||||
Box::new(WithSpan::no_span(Expr::Var("a"))),
|
||||
Box::new(WithSpan::no_span(Expr::Call {
|
||||
WithSpan::no_span(Expr::Var("a")),
|
||||
WithSpan::no_span(Expr::Call {
|
||||
path: Box::new(WithSpan::no_span(Expr::Var("b"))),
|
||||
args: vec![WithSpan::no_span(Expr::Var("c"))],
|
||||
generics: vec![],
|
||||
})),
|
||||
)),
|
||||
}),
|
||||
),
|
||||
)],
|
||||
);
|
||||
assert_eq!(
|
||||
|
Loading…
x
Reference in New Issue
Block a user