From 844b50687256a6ea1a885b50674ae72f9c2a5675 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 17 Feb 2020 10:27:04 -0500 Subject: [PATCH] Value operations can fail --- src/builtin/macros.rs | 8 ++++---- src/builtin/meta.rs | 4 ++-- src/value/mod.rs | 41 ++++++++++++++++++++------------------- src/value/ops.rs | 45 +++++++++++++++++++++++-------------------- 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/builtin/macros.rs b/src/builtin/macros.rs index bfeeeee..f237904 100644 --- a/src/builtin/macros.rs +++ b/src/builtin/macros.rs @@ -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, }, }; diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index 1e7a9b6..a0d9bef 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -8,7 +8,7 @@ use crate::value::Value; pub(crate) fn register(f: &mut BTreeMap) { 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) { 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); diff --git a/src/value/mod.rs b/src/value/mod.rs index 8fd52b3..726dfd8 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -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 { 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 { 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), } } } diff --git a/src/value/ops.rs b/src/value/ops.rs index 0eb8662..a3192ee 100644 --- a/src/value/ops.rs +++ b/src/value/ops.rs @@ -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; - 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; - 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; - 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; - 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), }, - } + }) } }