implement unary ops + and -

This commit is contained in:
ConnorSkees 2020-03-21 12:14:02 -04:00
parent 51b080e6eb
commit 7d12bc8142
4 changed files with 86 additions and 5 deletions

View File

@ -20,6 +20,7 @@ pub(crate) enum Value {
Dimension(Number, Unit), Dimension(Number, Unit),
List(Vec<Value>, ListSeparator), List(Vec<Value>, ListSeparator),
Color(Color), Color(Color),
UnaryOp(Op, Box<Value>),
BinaryOp(Box<Value>, Op, Box<Value>), BinaryOp(Box<Value>, Op, Box<Value>),
Paren(Box<Value>), Paren(Box<Value>),
Ident(String, QuoteKind), Ident(String, QuoteKind),
@ -45,7 +46,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!( Self::UnaryOp(..) | Self::BinaryOp(..) => write!(
f, f,
"{}", "{}",
match self.clone().eval() { match self.clone().eval() {
@ -148,6 +149,13 @@ impl Value {
}) })
} }
pub fn unary_op_plus(self) -> SassResult<Self> {
Ok(match self.eval()? {
v @ Value::Dimension(..) => v,
v => Value::Ident(format!("+{}", v), QuoteKind::None),
})
}
pub fn eval(self) -> SassResult<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 {
@ -161,6 +169,11 @@ impl Value {
_ => Ok(Self::BinaryOp(lhs, op, rhs)), _ => Ok(Self::BinaryOp(lhs, op, rhs)),
}, },
Self::Paren(v) => v.eval(), Self::Paren(v) => v.eval(),
Self::UnaryOp(op, val) => match op {
Op::Plus => val.unary_op_plus(),
Op::Minus => -*val,
_ => unreachable!(),
},
_ => Ok(self), _ => Ok(self),
} }
} }

View File

@ -1,4 +1,4 @@
use std::ops::{Add, Div, Mul, Rem, Sub}; use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
use crate::common::QuoteKind; use crate::common::QuoteKind;
use crate::error::SassResult; use crate::error::SassResult;
@ -70,7 +70,7 @@ impl Add for Value {
Self::List(..) => Value::Ident(format!("{}{}", c, other), QuoteKind::None), Self::List(..) => Value::Ident(format!("{}{}", c, other), QuoteKind::None),
_ => return Err(format!("Undefined operation \"{} + {}\".", c, other).into()), _ => return Err(format!("Undefined operation \"{} + {}\".", c, other).into()),
}, },
Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? + other)?, Self::UnaryOp(..) | Self::BinaryOp(..) | Self::Paren(..) => (self.eval()? + other)?,
Self::Ident(s1, quotes1) => match other { Self::Ident(s1, quotes1) => match other {
Self::Ident(s2, quotes2) => { Self::Ident(s2, quotes2) => {
let quotes = match (quotes1, quotes2) { let quotes = match (quotes1, quotes2) {
@ -104,7 +104,7 @@ impl Add for Value {
Value::Ident(format!("{}{}", s1, c), quotes) Value::Ident(format!("{}{}", s1, c), quotes)
} }
Self::List(..) => Value::Ident(format!("{}{}", s1, other), quotes1), Self::List(..) => Value::Ident(format!("{}{}", s1, other), quotes1),
Self::BinaryOp(..) | Self::Paren(..) => todo!(), Self::UnaryOp(..) | Self::BinaryOp(..) | Self::Paren(..) => todo!(),
}, },
Self::List(..) => match other { Self::List(..) => match other {
Self::Ident(s, q) => { Self::Ident(s, q) => {
@ -417,3 +417,14 @@ impl Rem for Value {
}) })
} }
} }
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),
})
}
}

View File

@ -292,7 +292,17 @@ impl Value {
TokenKind::Keyword(Keyword::From(s)) => Ok(Value::Ident(s, QuoteKind::None)), TokenKind::Keyword(Keyword::From(s)) => Ok(Value::Ident(s, QuoteKind::None)),
TokenKind::Keyword(Keyword::Through(s)) => Ok(Value::Ident(s, QuoteKind::None)), TokenKind::Keyword(Keyword::Through(s)) => Ok(Value::Ident(s, QuoteKind::None)),
TokenKind::Keyword(Keyword::To(s)) => Ok(Value::Ident(s, QuoteKind::None)), TokenKind::Keyword(Keyword::To(s)) => Ok(Value::Ident(s, QuoteKind::None)),
TokenKind::AtRule(_) => return Err("expected \";\".".into()), TokenKind::AtRule(_) => Err("expected \";\".".into()),
TokenKind::Op(Op::Plus) | TokenKind::Symbol(Symbol::Plus) => {
devour_whitespace_or_comment(toks);
let v = Self::_from_tokens(toks, scope, super_selector)?;
Ok(Value::UnaryOp(Op::Plus, Box::new(v)))
}
TokenKind::Op(Op::Minus) | TokenKind::Symbol(Symbol::Minus) => {
devour_whitespace_or_comment(toks);
let v = Self::_from_tokens(toks, scope, super_selector)?;
Ok(Value::UnaryOp(Op::Minus, Box::new(v)))
}
v => { v => {
dbg!(v); dbg!(v);
panic!("Unexpected token in value parsing") panic!("Unexpected token in value parsing")

47
tests/unary.rs Normal file
View File

@ -0,0 +1,47 @@
#![cfg(test)]
#[macro_use]
mod macros;
test!(unary_pos_unquoted_ident, "a {\n color: +foo;\n}\n");
test!(
unary_pos_whitespace,
"a {\n color: + foo;\n}\n",
"a {\n color: +foo;\n}\n"
);
test!(unary_pos_dblquoted_ident, "a {\n color: +\"foo\";\n}\n");
test!(
unary_pos_sglquoted_ident,
"a {\n color: +'foo';\n}\n",
"a {\n color: +\"foo\";\n}\n"
);
test!(unary_pos_color, "a {\n color: +\"foo\";\n}\n");
test!(
unary_pos_number,
"a {\n color: +1px;\n}\n",
"a {\n color: 1px;\n}\n"
);
test!(
unary_pos_in_list,
"a {\n color: bar,+ \"bar\" - foo;\n}\n",
"a {\n color: bar, +\"bar\"-foo;\n}\n"
);
test!(unary_neg_unquoted_ident, "a {\n color: -foo;\n}\n");
test!(unary_neg_dblquoted_ident, "a {\n color: -\"foo\";\n}\n");
test!(
unary_neg_sglquoted_ident,
"a {\n color: -'foo';\n}\n",
"a {\n color: -\"foo\";\n}\n"
);
test!(unary_neg_color, "a {\n color: -\"foo\";\n}\n");
test!(unary_neg_number, "a {\n color: -1px;\n}\n");
test!(
unary_neg_whitespace,
"a {\n color: - 1px;\n}\n",
"a {\n color: -1px;\n}\n"
);
test!(
unary_neg_number_type,
"a {\n color: type-of(- 1px);\n}\n",
"a {\n color: number;\n}\n"
);