implement order of operations

This commit is contained in:
ConnorSkees 2020-04-05 17:34:30 -04:00
parent c8a59ea501
commit d550615957
5 changed files with 125 additions and 36 deletions

View File

@ -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)),

View File

@ -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!(),
}
} }
} }

View File

@ -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();

View File

@ -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()),
}) })
} }

View 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"
);