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