Value operations can fail

This commit is contained in:
ConnorSkees 2020-02-17 10:27:04 -05:00
parent 0e2e5cb8bc
commit 844b506872
4 changed files with 51 additions and 47 deletions

View File

@ -1,18 +1,18 @@
macro_rules! arg {
($args:ident, $idx:literal, $name:literal) => {
match $args.remove(stringify!($idx)) {
Some(v) => v.eval(),
Some(v) => v.eval()?,
None => match $args.remove($name) {
Some(v) => v.eval(),
Some(v) => v.eval()?,
None => return Err(concat!("Missing argument $", $name, ".").into()),
},
};
};
($args:ident, $idx:literal, $name:literal=$default:expr) => {
match $args.remove(stringify!($idx)) {
Some(v) => v.eval(),
Some(v) => v.eval()?,
None => match $args.remove($name) {
Some(v) => v.eval(),
Some(v) => v.eval()?,
None => $default,
},
};

View File

@ -8,7 +8,7 @@ use crate::value::Value;
pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
decl!(f "if", |args, _| {
max_args!(args, 3);
if arg!(args, 0, "condition").is_true() {
if arg!(args, 0, "condition").is_true()? {
Ok(arg!(args, 1, "if-true"))
} else {
Ok(arg!(args, 2, "if-false"))
@ -46,7 +46,7 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
decl!(f "type-of", |args, _| {
max_args!(args, 1);
let value = arg!(args, 0, "value");
Ok(Value::Ident(value.kind().to_owned(), QuoteKind::None))
Ok(Value::Ident(value.kind()?.to_owned(), QuoteKind::None))
});
decl!(f "unitless", |args, _| {
max_args!(args, 1);

View File

@ -4,6 +4,7 @@ use std::iter::Iterator;
use crate::color::Color;
use crate::common::{ListSeparator, Op, QuoteKind};
use crate::error::SassResult;
use crate::units::Unit;
pub(crate) use number::Number;
@ -39,7 +40,7 @@ impl Display for Value {
.join(sep.as_str())
),
Self::Color(c) => write!(f, "{}", c),
Self::BinaryOp(..) => write!(f, "{}", self.clone().eval()),
Self::BinaryOp(..) => write!(f, "{}", self.clone().eval().unwrap()),
Self::Paren(val) => write!(f, "{}", val),
Self::Ident(val, kind) => write!(f, "{}{}{}", kind.as_str(), val, kind.as_str()),
Self::True => write!(f, "true"),
@ -54,11 +55,11 @@ impl Value {
self == &Value::Null
}
pub fn is_true(&self) -> bool {
pub fn is_true(&self) -> SassResult<bool> {
match self {
Value::Null | Value::False => false,
Self::BinaryOp(..) => self.clone().eval().is_true(),
_ => true,
Value::Null | Value::False => Ok(false),
Self::BinaryOp(..) => self.clone().eval()?.is_true(),
_ => Ok(true),
}
}
@ -69,17 +70,17 @@ impl Value {
}
}
pub fn kind(&self) -> &'static str {
pub fn kind(&self) -> SassResult<&'static str> {
match self {
Value::Color(..) => "color",
Value::Ident(..) => "string",
Value::Dimension(..) => "number",
Value::List(..) => "list",
// Value::Function(..) => "function",
Value::True | Value::False => "bool",
Value::Null => "null",
Value::BinaryOp(..) => self.clone().eval().kind(),
_ => "unknown",
Value::Color(..) => Ok("color"),
Value::Ident(..) => Ok("string"),
Value::Dimension(..) => Ok("number"),
Value::List(..) => Ok("list"),
// Value::Function(..) => Ok("function"),
Value::True | Value::False => Ok("bool"),
Value::Null => Ok("null"),
Value::BinaryOp(..) => self.clone().eval()?.kind(),
_ => Ok("unknown"),
}
}
@ -91,18 +92,18 @@ impl Value {
}
}
pub fn eval(self) -> Self {
pub fn eval(self) -> SassResult<Self> {
match self {
Self::BinaryOp(lhs, op, rhs) => match op {
Op::Plus => *lhs + *rhs,
Op::Minus => *lhs - *rhs,
Op::Equal => Self::bool(*lhs == *rhs),
Op::NotEqual => Self::bool(*lhs != *rhs),
Op::Equal => Ok(Self::bool(*lhs == *rhs)),
Op::NotEqual => Ok(Self::bool(*lhs != *rhs)),
Op::Mul => *lhs * *rhs,
Op::Div => *lhs / *rhs,
_ => Self::BinaryOp(lhs, op, rhs),
_ => Ok(Self::BinaryOp(lhs, op, rhs)),
},
_ => self,
_ => Ok(self),
}
}
}

View File

@ -1,14 +1,15 @@
use std::ops::{Add, Div, Mul, Sub};
use crate::common::QuoteKind;
use crate::error::SassResult;
use crate::units::Unit;
use crate::value::Value;
// Undefined operation "red + white".
impl Add for Value {
type Output = Self;
type Output = SassResult<Self>;
fn add(self, other: Self) -> Self {
match self {
fn add(self, other: Self) -> Self::Output {
Ok(match self {
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)
@ -77,15 +78,15 @@ impl Add for Value {
_ => todo!(),
},
_ => todo!(),
}
})
}
}
impl Sub for Value {
type Output = Self;
type Output = SassResult<Self>;
fn sub(self, other: Self) -> Self {
match self {
fn sub(self, other: Self) -> Self::Output {
Ok(match self {
Self::Null => todo!(),
Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => Value::Dimension(num - num2, unit),
@ -104,7 +105,7 @@ impl Sub for Value {
Self::Dimension(..) => todo!("investigate adding numbers and colors"),
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None),
},
Self::BinaryOp(..) | Self::Paren(..) => self.eval(),
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
Self::Ident(s1, quotes1) => match other {
Self::Ident(s2, quotes2) => {
let quotes1 = match quotes1 {
@ -157,30 +158,30 @@ impl Sub for Value {
Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None),
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
},
}
})
}
}
impl Mul for Value {
type Output = Self;
type Output = SassResult<Self>;
fn mul(self, other: Self) -> Self {
match self {
fn mul(self, other: Self) -> Self::Output {
Ok(match self {
Self::Null => todo!(),
Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => Value::Dimension(num * num2, unit),
_ => todo!(),
},
Self::BinaryOp(..) | Self::Paren(..) => self.eval(),
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
_ => todo!("incompatible mul types"),
}
})
}
}
impl Div for Value {
type Output = Self;
type Output = SassResult<Self>;
fn div(self, other: Self) -> Self {
match self {
fn div(self, other: Self) -> Self::Output {
Ok(match self {
Self::Null => todo!(),
Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => {
@ -200,7 +201,9 @@ impl Div for Value {
QuoteKind::None,
)
}
Self::BinaryOp(..) | Self::Paren(..) => Self::Dimension(num, unit) / other.eval(),
Self::BinaryOp(..) | Self::Paren(..) => {
(Self::Dimension(num, unit) / other.eval()?)?
}
_ => todo!(),
},
// Self::List(..) => todo!(),
@ -216,7 +219,7 @@ impl Div for Value {
Self::Dimension(..) => todo!("investigate adding numbers and colors"),
_ => Value::Ident(format!("{}/{}", c, other), QuoteKind::None),
},
Self::BinaryOp(..) | Self::Paren(..) => self.eval(),
Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
Self::Ident(s1, quotes1) => match other {
Self::Ident(s2, quotes2) => {
let quotes1 = match quotes1 {
@ -269,6 +272,6 @@ impl Div for Value {
Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None),
_ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None),
},
}
})
}
}