2020-03-21 12:14:02 -04:00
|
|
|
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
|
2020-02-08 16:07:37 -05:00
|
|
|
|
|
|
|
use crate::common::QuoteKind;
|
2020-02-17 10:27:04 -05:00
|
|
|
use crate::error::SassResult;
|
2020-03-19 16:24:31 -04:00
|
|
|
use crate::unit::{Unit, UNIT_CONVERSION_TABLE};
|
2020-02-08 16:07:37 -05:00
|
|
|
use crate::value::Value;
|
2020-02-17 10:39:32 -05:00
|
|
|
|
2020-02-08 16:07:37 -05:00
|
|
|
impl Add for Value {
|
2020-02-17 10:27:04 -05:00
|
|
|
type Output = SassResult<Self>;
|
2020-02-08 16:07:37 -05:00
|
|
|
|
2020-03-19 08:36:11 -04:00
|
|
|
fn add(self, mut other: Self) -> Self::Output {
|
|
|
|
other = other.eval()?;
|
2020-02-17 10:27:04 -05:00
|
|
|
Ok(match self {
|
2020-03-30 15:43:15 -04:00
|
|
|
Self::Map(..) => todo!(),
|
2020-02-08 16:07:37 -05:00
|
|
|
Self::Important | Self::True | Self::False => match other {
|
|
|
|
Self::Ident(s, QuoteKind::Double) | Self::Ident(s, QuoteKind::Single) => {
|
|
|
|
Value::Ident(format!("{}{}", self, s), QuoteKind::Double)
|
|
|
|
}
|
|
|
|
Self::Null => Value::Ident(self.to_string(), QuoteKind::None),
|
|
|
|
_ => Value::Ident(format!("{}{}", self, other), QuoteKind::None),
|
|
|
|
},
|
|
|
|
Self::Null => match other {
|
|
|
|
Self::Null => Self::Null,
|
|
|
|
_ => Value::Ident(format!("{}", other), QuoteKind::None),
|
|
|
|
},
|
|
|
|
Self::Dimension(num, unit) => match other {
|
2020-03-01 14:53:52 -05:00
|
|
|
Self::Dimension(num2, unit2) => {
|
|
|
|
if !unit.comparable(&unit2) {
|
|
|
|
return Err(format!("Incompatible units {} and {}.", unit2, unit).into());
|
|
|
|
}
|
|
|
|
if unit == unit2 {
|
|
|
|
Value::Dimension(num + num2, unit)
|
2020-03-18 10:08:40 -04:00
|
|
|
} else if unit == Unit::None {
|
|
|
|
Value::Dimension(num + num2, unit2)
|
|
|
|
} else if unit2 == Unit::None {
|
|
|
|
Value::Dimension(num + num2, unit)
|
2020-03-01 14:53:52 -05:00
|
|
|
} else {
|
2020-03-18 10:08:40 -04:00
|
|
|
Value::Dimension(
|
|
|
|
num + num2
|
|
|
|
* UNIT_CONVERSION_TABLE[&unit.to_string()][&unit2.to_string()]
|
|
|
|
.clone(),
|
|
|
|
unit,
|
|
|
|
)
|
2020-03-01 14:53:52 -05:00
|
|
|
}
|
|
|
|
}
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(format!("{}{}{}", num, unit, s), q.normalize()),
|
2020-02-17 10:39:32 -05:00
|
|
|
Self::Null => Value::Ident(format!("{}{}", num, unit), QuoteKind::None),
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => {
|
|
|
|
Value::Ident(format!("{}{}{}", num, unit, other), QuoteKind::None)
|
|
|
|
}
|
2020-02-29 20:01:43 -05:00
|
|
|
_ => {
|
|
|
|
return Err(
|
|
|
|
format!("Undefined operation \"{}{} + {}\".", num, unit, other).into(),
|
|
|
|
)
|
|
|
|
}
|
2020-02-08 16:07:37 -05:00
|
|
|
},
|
2020-03-01 17:06:55 -05:00
|
|
|
Self::Color(c) => match other {
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(format!("{}{}", c, s), q.normalize()),
|
2020-02-09 10:49:37 -05:00
|
|
|
Self::Null => Value::Ident(c.to_string(), QuoteKind::None),
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => Value::Ident(format!("{}{}", c, other), QuoteKind::None),
|
2020-02-17 10:39:32 -05:00
|
|
|
_ => return Err(format!("Undefined operation \"{} + {}\".", c, other).into()),
|
2020-02-09 12:18:41 -05:00
|
|
|
},
|
2020-03-21 12:14:02 -04:00
|
|
|
Self::UnaryOp(..) | Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? + other)?,
|
2020-02-08 16:07:37 -05:00
|
|
|
Self::Ident(s1, quotes1) => match other {
|
|
|
|
Self::Ident(s2, quotes2) => {
|
|
|
|
let quotes = match (quotes1, quotes2) {
|
|
|
|
(QuoteKind::Double, _)
|
|
|
|
| (QuoteKind::Single, _)
|
|
|
|
| (_, QuoteKind::Double)
|
|
|
|
| (_, QuoteKind::Single) => QuoteKind::Double,
|
|
|
|
_ => QuoteKind::None,
|
|
|
|
};
|
|
|
|
Value::Ident(format!("{}{}", s1, s2), quotes)
|
|
|
|
}
|
|
|
|
Self::Important | Self::True | Self::False | Self::Dimension(..) => {
|
2020-03-23 12:12:08 -04:00
|
|
|
Value::Ident(format!("{}{}", s1, other), quotes1.normalize())
|
2020-02-09 10:49:37 -05:00
|
|
|
}
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Null => Value::Ident(s1, quotes1.normalize()),
|
|
|
|
Self::Color(c) => Value::Ident(format!("{}{}", s1, c), quotes1.normalize()),
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => Value::Ident(format!("{}{}", s1, other), quotes1),
|
2020-03-21 12:14:02 -04:00
|
|
|
Self::UnaryOp(..) | Self::BinaryOp(..) | Self::Paren(..) => todo!(),
|
2020-03-30 15:43:15 -04:00
|
|
|
Self::Map(..) => todo!(),
|
2020-02-08 16:07:37 -05:00
|
|
|
},
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => match other {
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(format!("{}{}", self, s), q.normalize()),
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::Paren(..) => (self + other.eval()?)?,
|
|
|
|
_ => Value::Ident(format!("{}{}", self, other), QuoteKind::None),
|
2020-03-19 16:24:31 -04:00
|
|
|
},
|
2020-02-17 10:27:04 -05:00
|
|
|
})
|
2020-02-08 16:07:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sub for Value {
|
2020-02-17 10:27:04 -05:00
|
|
|
type Output = SassResult<Self>;
|
2020-02-08 16:07:37 -05:00
|
|
|
|
2020-03-19 08:36:11 -04:00
|
|
|
fn sub(self, mut other: Self) -> Self::Output {
|
|
|
|
other = other.eval()?;
|
2020-02-17 10:27:04 -05:00
|
|
|
Ok(match self {
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => todo!(),
|
2020-02-08 16:07:37 -05:00
|
|
|
Self::Dimension(num, unit) => match other {
|
2020-03-01 14:53:52 -05:00
|
|
|
Self::Dimension(num2, unit2) => {
|
|
|
|
if !unit.comparable(&unit2) {
|
|
|
|
return Err(format!("Incompatible units {} and {}.", unit2, unit).into());
|
|
|
|
}
|
|
|
|
if unit == unit2 {
|
|
|
|
Value::Dimension(num - num2, unit)
|
2020-03-18 10:14:35 -04:00
|
|
|
} else if unit == Unit::None {
|
|
|
|
Value::Dimension(num - num2, unit2)
|
|
|
|
} else if unit2 == Unit::None {
|
|
|
|
Value::Dimension(num - num2, unit)
|
2020-03-01 14:53:52 -05:00
|
|
|
} else {
|
2020-03-18 10:14:35 -04:00
|
|
|
Value::Dimension(
|
|
|
|
num - num2
|
|
|
|
* UNIT_CONVERSION_TABLE[&unit.to_string()][&unit2.to_string()]
|
|
|
|
.clone(),
|
|
|
|
unit,
|
|
|
|
)
|
2020-03-01 14:53:52 -05:00
|
|
|
}
|
|
|
|
}
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => {
|
|
|
|
Value::Ident(format!("{}{}-{}", num, unit, other), QuoteKind::None)
|
|
|
|
}
|
2020-02-08 16:07:37 -05:00
|
|
|
_ => todo!(),
|
|
|
|
},
|
2020-02-09 11:07:13 -05:00
|
|
|
Self::Color(c) => match other {
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}-{}{}{}", c, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 11:07:13 -05:00
|
|
|
Self::Null => Value::Ident(format!("{}-", c), QuoteKind::None),
|
2020-02-29 20:14:51 -05:00
|
|
|
Self::Dimension(..) | Self::Color(..) => {
|
|
|
|
return Err(format!("Undefined operation \"{} - {}\".", c, other).into())
|
2020-02-29 20:01:43 -05:00
|
|
|
}
|
2020-02-09 12:18:41 -05:00
|
|
|
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None),
|
2020-02-09 11:07:13 -05:00
|
|
|
},
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? - other)?,
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s1, q1) => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s2, q2) => Value::Ident(
|
|
|
|
format!(
|
|
|
|
"{}{}{}-{}{}{}",
|
|
|
|
q1.normalize(),
|
|
|
|
s1,
|
|
|
|
q1.normalize(),
|
|
|
|
q2.normalize(),
|
|
|
|
s2,
|
|
|
|
q2.normalize()
|
|
|
|
),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 12:18:41 -05:00
|
|
|
Self::Important
|
|
|
|
| Self::True
|
|
|
|
| Self::False
|
|
|
|
| Self::Dimension(..)
|
2020-03-23 13:57:00 -04:00
|
|
|
| Self::Color(..) => Value::Ident(
|
|
|
|
format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
|
|
|
Self::Null => Value::Ident(
|
|
|
|
format!("{}{}{}-", q1.normalize(), s1, q1.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
|
|
|
Self::List(..) => Value::Ident(
|
|
|
|
format!("{}{}{}-{}", q1.normalize(), s1, q1.normalize(), other),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-08 16:07:37 -05:00
|
|
|
_ => todo!(),
|
|
|
|
},
|
2020-03-19 14:20:16 -04:00
|
|
|
Self::List(..) => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-03-30 17:06:23 -04:00
|
|
|
Self::Paren(..) => (self - other.eval()?)?,
|
2020-03-19 14:20:16 -04:00
|
|
|
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
|
2020-03-19 16:24:31 -04:00
|
|
|
},
|
2020-02-09 16:05:07 -05:00
|
|
|
_ => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}-{}{}{}", self, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None),
|
|
|
|
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
|
|
|
|
},
|
2020-02-17 10:27:04 -05:00
|
|
|
})
|
2020-02-09 16:05:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Mul for Value {
|
2020-02-17 10:27:04 -05:00
|
|
|
type Output = SassResult<Self>;
|
2020-02-09 16:05:07 -05:00
|
|
|
|
2020-03-19 08:36:11 -04:00
|
|
|
fn mul(self, mut other: Self) -> Self::Output {
|
|
|
|
other = other.eval()?;
|
2020-02-17 10:27:04 -05:00
|
|
|
Ok(match self {
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => todo!(),
|
|
|
|
Self::Dimension(num, unit) => match other {
|
2020-03-01 14:53:52 -05:00
|
|
|
Self::Dimension(num2, unit2) => {
|
2020-03-16 21:29:00 -04:00
|
|
|
if unit == Unit::None {
|
|
|
|
Value::Dimension(num * num2, unit2)
|
|
|
|
} else if unit2 == Unit::None {
|
2020-03-01 14:53:52 -05:00
|
|
|
Value::Dimension(num * num2, unit)
|
2020-03-17 10:06:24 -04:00
|
|
|
} else if let Unit::Mul(mut u) = unit {
|
|
|
|
u.push(unit2);
|
|
|
|
Value::Dimension(num * num2, Unit::Mul(u))
|
|
|
|
} else if let Unit::Mul(u2) = unit2 {
|
|
|
|
let mut u = vec![unit];
|
|
|
|
u.extend(u2);
|
|
|
|
Value::Dimension(num * num2, Unit::Mul(u))
|
2020-03-01 14:53:52 -05:00
|
|
|
} else {
|
2020-03-17 10:06:24 -04:00
|
|
|
Value::Dimension(num * num2, Unit::Mul(vec![unit, unit2]))
|
2020-03-01 14:53:52 -05:00
|
|
|
}
|
|
|
|
}
|
2020-02-29 20:01:43 -05:00
|
|
|
_ => {
|
|
|
|
return Err(
|
|
|
|
format!("Undefined operation \"{}{} * {}\".", num, unit, other).into(),
|
|
|
|
)
|
|
|
|
}
|
2020-02-09 16:05:07 -05:00
|
|
|
},
|
2020-02-17 10:27:04 -05:00
|
|
|
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
|
2020-03-30 00:38:46 -04:00
|
|
|
Self::UnaryOp(..) => (self.eval()? * other)?,
|
2020-02-29 20:14:51 -05:00
|
|
|
_ => return Err(format!("Undefined operation \"{} * {}\".", self, other).into()),
|
2020-02-17 10:27:04 -05:00
|
|
|
})
|
2020-02-09 16:05:07 -05:00
|
|
|
}
|
|
|
|
}
|
2020-03-17 10:06:24 -04:00
|
|
|
|
2020-02-09 16:05:07 -05:00
|
|
|
impl Div for Value {
|
2020-02-17 10:27:04 -05:00
|
|
|
type Output = SassResult<Self>;
|
2020-02-09 16:05:07 -05:00
|
|
|
|
2020-02-17 10:27:04 -05:00
|
|
|
fn div(self, other: Self) -> Self::Output {
|
|
|
|
Ok(match self {
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => todo!(),
|
|
|
|
Self::Dimension(num, unit) => match other {
|
2020-02-09 18:28:24 -05:00
|
|
|
Self::Dimension(num2, unit2) => {
|
|
|
|
if unit == unit2 {
|
|
|
|
Value::Dimension(num / num2, Unit::None)
|
|
|
|
} else {
|
|
|
|
todo!("unit conversions")
|
|
|
|
}
|
|
|
|
}
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}{}/{}{}{}", num, unit, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-17 10:27:04 -05:00
|
|
|
Self::BinaryOp(..) | Self::Paren(..) => {
|
|
|
|
(Self::Dimension(num, unit) / other.eval()?)?
|
|
|
|
}
|
2020-02-09 16:05:07 -05:00
|
|
|
_ => todo!(),
|
|
|
|
},
|
|
|
|
Self::Color(c) => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}/{}{}{}", c, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => Value::Ident(format!("{}/", c), QuoteKind::None),
|
2020-02-29 20:14:51 -05:00
|
|
|
Self::Dimension(..) | Self::Color(..) => {
|
2020-02-29 20:01:43 -05:00
|
|
|
return Err(format!("Undefined operation \"{} / {}\".", c, other).into())
|
|
|
|
}
|
2020-02-09 16:05:07 -05:00
|
|
|
_ => Value::Ident(format!("{}/{}", c, other), QuoteKind::None),
|
|
|
|
},
|
2020-02-17 10:27:04 -05:00
|
|
|
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
|
2020-03-23 12:12:08 -04:00
|
|
|
Self::Ident(s1, q1) => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s2, q2) => Value::Ident(
|
|
|
|
format!(
|
|
|
|
"{}{}{}/{}{}{}",
|
|
|
|
q1.normalize(),
|
|
|
|
s1,
|
|
|
|
q1.normalize(),
|
|
|
|
q2.normalize(),
|
|
|
|
s2,
|
|
|
|
q2.normalize()
|
|
|
|
),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Important
|
|
|
|
| Self::True
|
|
|
|
| Self::False
|
|
|
|
| Self::Dimension(..)
|
2020-03-23 13:57:00 -04:00
|
|
|
| Self::Color(..) => Value::Ident(
|
|
|
|
format!("{}{}{}/{}", q1.normalize(), s1, q1.normalize(), other),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
|
|
|
Self::Null => Value::Ident(
|
|
|
|
format!("{}{}{}/", q1.normalize(), s1, q1.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 16:05:07 -05:00
|
|
|
_ => todo!(),
|
|
|
|
},
|
|
|
|
_ => match other {
|
2020-03-23 13:57:00 -04:00
|
|
|
Self::Ident(s, q) => Value::Ident(
|
|
|
|
format!("{}/{}{}{}", self, q.normalize(), s, q.normalize()),
|
|
|
|
QuoteKind::None,
|
|
|
|
),
|
2020-02-09 16:05:07 -05:00
|
|
|
Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None),
|
|
|
|
_ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None),
|
|
|
|
},
|
2020-02-17 10:27:04 -05:00
|
|
|
})
|
2020-02-08 16:07:37 -05:00
|
|
|
}
|
|
|
|
}
|
2020-03-18 11:39:58 -04:00
|
|
|
|
|
|
|
impl Rem for Value {
|
|
|
|
type Output = SassResult<Self>;
|
|
|
|
|
|
|
|
fn rem(self, other: Self) -> Self::Output {
|
|
|
|
Ok(match self {
|
|
|
|
Value::Dimension(n, u) => match other {
|
|
|
|
Value::Dimension(n2, u2) => {
|
|
|
|
if !u.comparable(&u2) {
|
|
|
|
return Err(format!("Incompatible units {} and {}.", u2, u).into());
|
|
|
|
}
|
|
|
|
if u == u2 {
|
|
|
|
Value::Dimension(n % n2, u)
|
|
|
|
} else if u == Unit::None {
|
|
|
|
Value::Dimension(n % n2, u2)
|
|
|
|
} else if u2 == Unit::None {
|
|
|
|
Value::Dimension(n % n2, u)
|
|
|
|
} else {
|
|
|
|
Value::Dimension(n, u)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(format!(
|
|
|
|
"Undefined operation \"{} % {}\".",
|
|
|
|
Value::Dimension(n, u),
|
|
|
|
other
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => return Err(format!("Undefined operation \"{} % {}\".", self, other).into()),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2020-03-21 12:14:02 -04:00
|
|
|
|
|
|
|
impl Neg for Value {
|
|
|
|
type Output = SassResult<Self>;
|
|
|
|
|
|
|
|
fn neg(self) -> Self::Output {
|
|
|
|
Ok(match self.eval()? {
|
|
|
|
Value::Dimension(n, u) => Value::Dimension(-n, u),
|
|
|
|
v => Value::Ident(format!("-{}", v), QuoteKind::None),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|