implement order of operations
This commit is contained in:
parent
c8a59ea501
commit
d550615957
@ -102,8 +102,11 @@ impl Mixin {
|
|||||||
while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector)? {
|
while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector)? {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::AtRule(a) => match a {
|
Expr::AtRule(a) => match a {
|
||||||
|
AtRule::While(s) | AtRule::Each(s) | AtRule::For(s) => stmts.extend(s),
|
||||||
|
AtRule::If(i) => stmts.extend(i.eval(&mut self.scope.clone(), super_selector)?),
|
||||||
AtRule::Content => stmts.extend(self.content.clone()),
|
AtRule::Content => stmts.extend(self.content.clone()),
|
||||||
_ => stmts.push(Stmt::AtRule(a)),
|
AtRule::Return(..) => return Err("This at-rule is not allowed here.".into()),
|
||||||
|
r => stmts.push(Stmt::AtRule(r)),
|
||||||
},
|
},
|
||||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||||
Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)),
|
Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)),
|
||||||
|
@ -228,24 +228,12 @@ impl Value {
|
|||||||
Op::Mul => *lhs * *rhs,
|
Op::Mul => *lhs * *rhs,
|
||||||
Op::Div => *lhs / *rhs,
|
Op::Div => *lhs / *rhs,
|
||||||
Op::Rem => *lhs % *rhs,
|
Op::Rem => *lhs % *rhs,
|
||||||
Op::GreaterThan => match lhs.cmp(&rhs, op)? {
|
Op::GreaterThan => lhs.cmp(*rhs, op),
|
||||||
Ordering::Greater => Ok(Self::True),
|
Op::GreaterThanEqual => lhs.cmp(*rhs, op),
|
||||||
Ordering::Less | Ordering::Equal => Ok(Self::False),
|
Op::LessThan => lhs.cmp(*rhs, op),
|
||||||
},
|
Op::LessThanEqual => lhs.cmp(*rhs, op),
|
||||||
Op::GreaterThanEqual => match lhs.cmp(&rhs, op)? {
|
|
||||||
Ordering::Greater | Ordering::Equal => Ok(Self::True),
|
|
||||||
Ordering::Less => Ok(Self::False),
|
|
||||||
},
|
|
||||||
Op::LessThan => match lhs.cmp(&rhs, op)? {
|
|
||||||
Ordering::Less => Ok(Self::True),
|
|
||||||
Ordering::Greater | Ordering::Equal => Ok(Self::False),
|
|
||||||
},
|
|
||||||
Op::LessThanEqual => match lhs.cmp(&rhs, op)? {
|
|
||||||
Ordering::Less | Ordering::Equal => Ok(Self::True),
|
|
||||||
Ordering::Greater => Ok(Self::False),
|
|
||||||
},
|
|
||||||
Op::Not => unreachable!(),
|
Op::Not => unreachable!(),
|
||||||
Op::And => Ok(if lhs.is_true()? {
|
Op::And => Ok(if lhs.clone().is_true()? {
|
||||||
rhs.eval()?
|
rhs.eval()?
|
||||||
} else {
|
} else {
|
||||||
lhs.eval()?
|
lhs.eval()?
|
||||||
@ -267,16 +255,20 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmp(&self, other: &Self, op: Op) -> SassResult<Ordering> {
|
pub fn cmp(self, mut other: Self, op: Op) -> SassResult<Value> {
|
||||||
Ok(match self {
|
if let Self::Paren(..) = other {
|
||||||
Self::Dimension(num, ref unit) => match other {
|
other = other.eval()?
|
||||||
|
}
|
||||||
|
let precedence = op.precedence();
|
||||||
|
let ordering = match self {
|
||||||
|
Self::Dimension(num, unit) => match &other {
|
||||||
Self::Dimension(num2, unit2) => {
|
Self::Dimension(num2, unit2) => {
|
||||||
if !unit.comparable(unit2) {
|
if !unit.comparable(&unit2) {
|
||||||
return Err(format!("Incompatible units {} and {}.", unit2, unit).into());
|
return Err(format!("Incompatible units {} and {}.", unit2, unit).into());
|
||||||
}
|
}
|
||||||
if unit == unit2 {
|
if &unit == unit2 {
|
||||||
num.cmp(num2)
|
num.cmp(num2)
|
||||||
} else if unit == &Unit::None {
|
} else if unit == Unit::None {
|
||||||
num.cmp(num2)
|
num.cmp(num2)
|
||||||
} else if unit2 == &Unit::None {
|
} else if unit2 == &Unit::None {
|
||||||
num.cmp(num2)
|
num.cmp(num2)
|
||||||
@ -288,13 +280,42 @@ impl Value {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
Self::BinaryOp(..) => todo!(),
|
||||||
return Err(
|
v => return Err(format!("Undefined operation \"{} {} {}\".", v, op, other).into()),
|
||||||
format!("Undefined operation \"{} {} {}\".", self, op, other).into(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
Self::BinaryOp(left, op2, right) => {
|
||||||
|
return if op2.precedence() >= precedence {
|
||||||
|
Self::BinaryOp(left, op2, right).eval()?.cmp(other, op)
|
||||||
|
} else {
|
||||||
|
Self::BinaryOp(
|
||||||
|
left,
|
||||||
|
op2,
|
||||||
|
Box::new(Self::BinaryOp(right, op, Box::new(other)).eval()?),
|
||||||
|
)
|
||||||
|
.eval()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::UnaryOp(..) | Self::Paren(..) => return self.eval()?.cmp(other, op),
|
||||||
_ => return Err(format!("Undefined operation \"{} {} {}\".", self, op, other).into()),
|
_ => return Err(format!("Undefined operation \"{} {} {}\".", self, op, other).into()),
|
||||||
})
|
};
|
||||||
|
match op {
|
||||||
|
Op::GreaterThan => match ordering {
|
||||||
|
Ordering::Greater => Ok(Self::True),
|
||||||
|
Ordering::Less | Ordering::Equal => Ok(Self::False),
|
||||||
|
},
|
||||||
|
Op::GreaterThanEqual => match ordering {
|
||||||
|
Ordering::Greater | Ordering::Equal => Ok(Self::True),
|
||||||
|
Ordering::Less => Ok(Self::False),
|
||||||
|
},
|
||||||
|
Op::LessThan => match ordering {
|
||||||
|
Ordering::Less => Ok(Self::True),
|
||||||
|
Ordering::Greater | Ordering::Equal => Ok(Self::False),
|
||||||
|
},
|
||||||
|
Op::LessThanEqual => match ordering {
|
||||||
|
Ordering::Less | Ordering::Equal => Ok(Self::True),
|
||||||
|
Ordering::Greater => Ok(Self::False),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ use crate::error::SassError;
|
|||||||
|
|
||||||
const PRECISION: usize = 10;
|
const PRECISION: usize = 10;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub(crate) struct Number {
|
pub(crate) struct Number {
|
||||||
val: BigRational,
|
val: BigRational,
|
||||||
}
|
}
|
||||||
@ -164,6 +164,12 @@ from_integer!(i32);
|
|||||||
from_integer!(u32);
|
from_integer!(u32);
|
||||||
from_integer!(u8);
|
from_integer!(u8);
|
||||||
|
|
||||||
|
impl fmt::Debug for Number {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Number {{ {} }}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Number {
|
impl Display for Number {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut whole = self.val.to_integer().abs();
|
let mut whole = self.val.to_integer().abs();
|
||||||
|
@ -101,7 +101,10 @@ impl Sub for Value {
|
|||||||
type Output = SassResult<Self>;
|
type Output = SassResult<Self>;
|
||||||
|
|
||||||
fn sub(self, mut other: Self) -> Self::Output {
|
fn sub(self, mut other: Self) -> Self::Output {
|
||||||
other = other.eval()?;
|
if let Self::Paren(..) = other {
|
||||||
|
other = other.eval()?
|
||||||
|
}
|
||||||
|
let precedence = Op::Mul.precedence();
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Null => todo!(),
|
Self::Null => todo!(),
|
||||||
Self::Dimension(num, unit) => match other {
|
Self::Dimension(num, unit) => match other {
|
||||||
@ -143,7 +146,19 @@ impl Sub for Value {
|
|||||||
}
|
}
|
||||||
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None),
|
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None),
|
||||||
},
|
},
|
||||||
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? - other)?,
|
Self::BinaryOp(left, op, right) => {
|
||||||
|
if op.precedence() >= precedence {
|
||||||
|
(Self::BinaryOp(left, op, right).eval()? - other)?
|
||||||
|
} else {
|
||||||
|
Self::BinaryOp(
|
||||||
|
left,
|
||||||
|
op,
|
||||||
|
Box::new(Self::BinaryOp(right, Op::Minus, Box::new(other)).eval()?),
|
||||||
|
)
|
||||||
|
.eval()?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Paren(..) => (self.eval()? - other)?,
|
||||||
Self::Ident(s1, q1) => match other {
|
Self::Ident(s1, q1) => match other {
|
||||||
Self::Ident(s2, q2) => Value::Ident(
|
Self::Ident(s2, q2) => Value::Ident(
|
||||||
format!(
|
format!(
|
||||||
@ -199,7 +214,10 @@ impl Mul for Value {
|
|||||||
type Output = SassResult<Self>;
|
type Output = SassResult<Self>;
|
||||||
|
|
||||||
fn mul(self, mut other: Self) -> Self::Output {
|
fn mul(self, mut other: Self) -> Self::Output {
|
||||||
other = other.eval()?;
|
if let Self::Paren(..) = other {
|
||||||
|
other = other.eval()?
|
||||||
|
}
|
||||||
|
let precedence = Op::Mul.precedence();
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Self::Null => todo!(),
|
Self::Null => todo!(),
|
||||||
Self::Dimension(num, unit) => match other {
|
Self::Dimension(num, unit) => match other {
|
||||||
@ -225,8 +243,19 @@ impl Mul for Value {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? * other)?,
|
Self::BinaryOp(left, op, right) => {
|
||||||
Self::UnaryOp(..) => (self.eval()? * other)?,
|
if op.precedence() >= precedence {
|
||||||
|
(Self::BinaryOp(left, op, right).eval()? * other)?
|
||||||
|
} else {
|
||||||
|
Self::BinaryOp(
|
||||||
|
left,
|
||||||
|
op,
|
||||||
|
Box::new(Self::BinaryOp(right, Op::Mul, Box::new(other)).eval()?),
|
||||||
|
)
|
||||||
|
.eval()?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::UnaryOp(..) | Self::Paren(..) => (self.eval()? * other)?,
|
||||||
_ => return Err(format!("Undefined operation \"{} * {}\".", self, other).into()),
|
_ => return Err(format!("Undefined operation \"{} * {}\".", self, other).into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
30
tests/order-of-operations.rs
Normal file
30
tests/order-of-operations.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
test!(
|
||||||
|
addition_then_division,
|
||||||
|
"a {\n color: 3+3/4;\n}\n",
|
||||||
|
"a {\n color: 3.75;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
division_then_addition,
|
||||||
|
"a {\n color: 3/4 + 3;\n}\n",
|
||||||
|
"a {\n color: 3.75;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
addition_then_multiplication,
|
||||||
|
"a {\n color: 4 + 2 * 3;\n}\n",
|
||||||
|
"a {\n color: 10;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
multiplication_then_addition,
|
||||||
|
"a {\n color: 4 * 2 + 3;\n}\n",
|
||||||
|
"a {\n color: 11;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
comparison,
|
||||||
|
"a {\n color: 1 < 1 and 1 < 1;;\n}\n",
|
||||||
|
"a {\n color: false;\n}\n"
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user