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)? {
|
||||
match expr {
|
||||
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()),
|
||||
_ => 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::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)),
|
||||
|
@ -228,24 +228,12 @@ impl Value {
|
||||
Op::Mul => *lhs * *rhs,
|
||||
Op::Div => *lhs / *rhs,
|
||||
Op::Rem => *lhs % *rhs,
|
||||
Op::GreaterThan => match lhs.cmp(&rhs, op)? {
|
||||
Ordering::Greater => Ok(Self::True),
|
||||
Ordering::Less | Ordering::Equal => Ok(Self::False),
|
||||
},
|
||||
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::GreaterThan => lhs.cmp(*rhs, op),
|
||||
Op::GreaterThanEqual => lhs.cmp(*rhs, op),
|
||||
Op::LessThan => lhs.cmp(*rhs, op),
|
||||
Op::LessThanEqual => lhs.cmp(*rhs, op),
|
||||
Op::Not => unreachable!(),
|
||||
Op::And => Ok(if lhs.is_true()? {
|
||||
Op::And => Ok(if lhs.clone().is_true()? {
|
||||
rhs.eval()?
|
||||
} else {
|
||||
lhs.eval()?
|
||||
@ -267,16 +255,20 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmp(&self, other: &Self, op: Op) -> SassResult<Ordering> {
|
||||
Ok(match self {
|
||||
Self::Dimension(num, ref unit) => match other {
|
||||
pub fn cmp(self, mut other: Self, op: Op) -> SassResult<Value> {
|
||||
if let Self::Paren(..) = other {
|
||||
other = other.eval()?
|
||||
}
|
||||
let precedence = op.precedence();
|
||||
let ordering = match self {
|
||||
Self::Dimension(num, unit) => match &other {
|
||||
Self::Dimension(num2, unit2) => {
|
||||
if !unit.comparable(unit2) {
|
||||
if !unit.comparable(&unit2) {
|
||||
return Err(format!("Incompatible units {} and {}.", unit2, unit).into());
|
||||
}
|
||||
if unit == unit2 {
|
||||
if &unit == unit2 {
|
||||
num.cmp(num2)
|
||||
} else if unit == &Unit::None {
|
||||
} else if unit == Unit::None {
|
||||
num.cmp(num2)
|
||||
} else if unit2 == &Unit::None {
|
||||
num.cmp(num2)
|
||||
@ -288,13 +280,42 @@ impl Value {
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
format!("Undefined operation \"{} {} {}\".", self, op, other).into(),
|
||||
)
|
||||
}
|
||||
Self::BinaryOp(..) => todo!(),
|
||||
v => return Err(format!("Undefined operation \"{} {} {}\".", v, 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()),
|
||||
})
|
||||
};
|
||||
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;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub(crate) struct Number {
|
||||
val: BigRational,
|
||||
}
|
||||
@ -164,6 +164,12 @@ from_integer!(i32);
|
||||
from_integer!(u32);
|
||||
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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut whole = self.val.to_integer().abs();
|
||||
|
@ -101,7 +101,10 @@ impl Sub for Value {
|
||||
type Output = SassResult<Self>;
|
||||
|
||||
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 {
|
||||
Self::Null => todo!(),
|
||||
Self::Dimension(num, unit) => match other {
|
||||
@ -143,7 +146,19 @@ impl Sub for Value {
|
||||
}
|
||||
_ => 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(s2, q2) => Value::Ident(
|
||||
format!(
|
||||
@ -199,7 +214,10 @@ impl Mul for Value {
|
||||
type Output = SassResult<Self>;
|
||||
|
||||
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 {
|
||||
Self::Null => todo!(),
|
||||
Self::Dimension(num, unit) => match other {
|
||||
@ -225,8 +243,19 @@ impl Mul for Value {
|
||||
)
|
||||
}
|
||||
},
|
||||
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? * other)?,
|
||||
Self::UnaryOp(..) => (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::Mul, Box::new(other)).eval()?),
|
||||
)
|
||||
.eval()?
|
||||
}
|
||||
}
|
||||
Self::UnaryOp(..) | Self::Paren(..) => (self.eval()? * other)?,
|
||||
_ => 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