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 { macro_rules! arg {
($args:ident, $idx:literal, $name:literal) => { ($args:ident, $idx:literal, $name:literal) => {
match $args.remove(stringify!($idx)) { match $args.remove(stringify!($idx)) {
Some(v) => v.eval(), Some(v) => v.eval()?,
None => match $args.remove($name) { None => match $args.remove($name) {
Some(v) => v.eval(), Some(v) => v.eval()?,
None => return Err(concat!("Missing argument $", $name, ".").into()), None => return Err(concat!("Missing argument $", $name, ".").into()),
}, },
}; };
}; };
($args:ident, $idx:literal, $name:literal=$default:expr) => { ($args:ident, $idx:literal, $name:literal=$default:expr) => {
match $args.remove(stringify!($idx)) { match $args.remove(stringify!($idx)) {
Some(v) => v.eval(), Some(v) => v.eval()?,
None => match $args.remove($name) { None => match $args.remove($name) {
Some(v) => v.eval(), Some(v) => v.eval()?,
None => $default, None => $default,
}, },
}; };

View File

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

View File

@ -4,6 +4,7 @@ use std::iter::Iterator;
use crate::color::Color; use crate::color::Color;
use crate::common::{ListSeparator, Op, QuoteKind}; use crate::common::{ListSeparator, Op, QuoteKind};
use crate::error::SassResult;
use crate::units::Unit; use crate::units::Unit;
pub(crate) use number::Number; pub(crate) use number::Number;
@ -39,7 +40,7 @@ impl Display for Value {
.join(sep.as_str()) .join(sep.as_str())
), ),
Self::Color(c) => write!(f, "{}", c), 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::Paren(val) => write!(f, "{}", val),
Self::Ident(val, kind) => write!(f, "{}{}{}", kind.as_str(), val, kind.as_str()), Self::Ident(val, kind) => write!(f, "{}{}{}", kind.as_str(), val, kind.as_str()),
Self::True => write!(f, "true"), Self::True => write!(f, "true"),
@ -54,11 +55,11 @@ impl Value {
self == &Value::Null self == &Value::Null
} }
pub fn is_true(&self) -> bool { pub fn is_true(&self) -> SassResult<bool> {
match self { match self {
Value::Null | Value::False => false, Value::Null | Value::False => Ok(false),
Self::BinaryOp(..) => self.clone().eval().is_true(), Self::BinaryOp(..) => self.clone().eval()?.is_true(),
_ => true, _ => Ok(true),
} }
} }
@ -69,17 +70,17 @@ impl Value {
} }
} }
pub fn kind(&self) -> &'static str { pub fn kind(&self) -> SassResult<&'static str> {
match self { match self {
Value::Color(..) => "color", Value::Color(..) => Ok("color"),
Value::Ident(..) => "string", Value::Ident(..) => Ok("string"),
Value::Dimension(..) => "number", Value::Dimension(..) => Ok("number"),
Value::List(..) => "list", Value::List(..) => Ok("list"),
// Value::Function(..) => "function", // Value::Function(..) => Ok("function"),
Value::True | Value::False => "bool", Value::True | Value::False => Ok("bool"),
Value::Null => "null", Value::Null => Ok("null"),
Value::BinaryOp(..) => self.clone().eval().kind(), Value::BinaryOp(..) => self.clone().eval()?.kind(),
_ => "unknown", _ => Ok("unknown"),
} }
} }
@ -91,18 +92,18 @@ impl Value {
} }
} }
pub fn eval(self) -> Self { pub fn eval(self) -> SassResult<Self> {
match self { match self {
Self::BinaryOp(lhs, op, rhs) => match op { Self::BinaryOp(lhs, op, rhs) => match op {
Op::Plus => *lhs + *rhs, Op::Plus => *lhs + *rhs,
Op::Minus => *lhs - *rhs, Op::Minus => *lhs - *rhs,
Op::Equal => Self::bool(*lhs == *rhs), Op::Equal => Ok(Self::bool(*lhs == *rhs)),
Op::NotEqual => Self::bool(*lhs != *rhs), Op::NotEqual => Ok(Self::bool(*lhs != *rhs)),
Op::Mul => *lhs * *rhs, Op::Mul => *lhs * *rhs,
Op::Div => *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 std::ops::{Add, Div, Mul, Sub};
use crate::common::QuoteKind; use crate::common::QuoteKind;
use crate::error::SassResult;
use crate::units::Unit; use crate::units::Unit;
use crate::value::Value; use crate::value::Value;
// Undefined operation "red + white".
impl Add for Value { impl Add for Value {
type Output = Self; type Output = SassResult<Self>;
fn add(self, other: Self) -> Self { fn add(self, other: Self) -> Self::Output {
match self { Ok(match self {
Self::Important | Self::True | Self::False => match other { Self::Important | Self::True | Self::False => match other {
Self::Ident(s, QuoteKind::Double) | Self::Ident(s, QuoteKind::Single) => { Self::Ident(s, QuoteKind::Double) | Self::Ident(s, QuoteKind::Single) => {
Value::Ident(format!("{}{}", self, s), QuoteKind::Double) Value::Ident(format!("{}{}", self, s), QuoteKind::Double)
@ -77,15 +78,15 @@ impl Add for Value {
_ => todo!(), _ => todo!(),
}, },
_ => todo!(), _ => todo!(),
} })
} }
} }
impl Sub for Value { impl Sub for Value {
type Output = Self; type Output = SassResult<Self>;
fn sub(self, other: Self) -> Self { fn sub(self, other: Self) -> Self::Output {
match self { Ok(match self {
Self::Null => todo!(), Self::Null => todo!(),
Self::Dimension(num, unit) => match other { Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => Value::Dimension(num - num2, unit), 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"), Self::Dimension(..) => todo!("investigate adding numbers and colors"),
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None), _ => 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(s1, quotes1) => match other {
Self::Ident(s2, quotes2) => { Self::Ident(s2, quotes2) => {
let quotes1 = match quotes1 { let quotes1 = match quotes1 {
@ -157,30 +158,30 @@ impl Sub for Value {
Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None), Self::Null => Value::Ident(format!("{}-", self), QuoteKind::None),
_ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None), _ => Value::Ident(format!("{}-{}", self, other), QuoteKind::None),
}, },
} })
} }
} }
impl Mul for Value { impl Mul for Value {
type Output = Self; type Output = SassResult<Self>;
fn mul(self, other: Self) -> Self { fn mul(self, other: Self) -> Self::Output {
match self { Ok(match self {
Self::Null => todo!(), Self::Null => todo!(),
Self::Dimension(num, unit) => match other { Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => Value::Dimension(num * num2, unit), Self::Dimension(num2, unit2) => Value::Dimension(num * num2, unit),
_ => todo!(), _ => todo!(),
}, },
Self::BinaryOp(..) | Self::Paren(..) => self.eval(), Self::BinaryOp(..) | Self::Paren(..) => self.eval()?,
_ => todo!("incompatible mul types"), _ => todo!("incompatible mul types"),
} })
} }
} }
impl Div for Value { impl Div for Value {
type Output = Self; type Output = SassResult<Self>;
fn div(self, other: Self) -> Self { fn div(self, other: Self) -> Self::Output {
match self { Ok(match self {
Self::Null => todo!(), Self::Null => todo!(),
Self::Dimension(num, unit) => match other { Self::Dimension(num, unit) => match other {
Self::Dimension(num2, unit2) => { Self::Dimension(num2, unit2) => {
@ -200,7 +201,9 @@ impl Div for Value {
QuoteKind::None, QuoteKind::None,
) )
} }
Self::BinaryOp(..) | Self::Paren(..) => Self::Dimension(num, unit) / other.eval(), Self::BinaryOp(..) | Self::Paren(..) => {
(Self::Dimension(num, unit) / other.eval()?)?
}
_ => todo!(), _ => todo!(),
}, },
// Self::List(..) => todo!(), // Self::List(..) => todo!(),
@ -216,7 +219,7 @@ impl Div for Value {
Self::Dimension(..) => todo!("investigate adding numbers and colors"), Self::Dimension(..) => todo!("investigate adding numbers and colors"),
_ => Value::Ident(format!("{}/{}", c, other), QuoteKind::None), _ => 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(s1, quotes1) => match other {
Self::Ident(s2, quotes2) => { Self::Ident(s2, quotes2) => {
let quotes1 = match quotes1 { let quotes1 = match quotes1 {
@ -269,6 +272,6 @@ impl Div for Value {
Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None), Self::Null => Value::Ident(format!("{}/", self), QuoteKind::None),
_ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None), _ => Value::Ident(format!("{}/{}", self, other), QuoteKind::None),
}, },
} })
} }
} }