From 2ce639e9bed74153e972b2ee441dd803db259fd1 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Mon, 30 Mar 2020 10:42:13 -0400 Subject: [PATCH] implement > < >= <= --- src/value/mod.rs | 43 ++++++++++++++++++++++++++++-- src/value/parse.rs | 20 ++++++++++++++ tests/ordering.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 tests/ordering.rs diff --git a/src/value/mod.rs b/src/value/mod.rs index 7505860..8ac8fd5 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,10 +1,11 @@ use std::fmt::{self, Display, Write}; use std::iter::Iterator; +use std::cmp::Ordering; use crate::color::Color; use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; -use crate::unit::Unit; +use crate::unit::{Unit, UNIT_CONVERSION_TABLE}; pub(crate) use number::Number; mod number; @@ -183,7 +184,22 @@ impl Value { Op::Mul => *lhs * *rhs, Op::Div => *lhs / *rhs, Op::Rem => *lhs % *rhs, - _ => Ok(Self::BinaryOp(lhs, op, rhs)), + Op::GreaterThan => match lhs.cmp(&rhs, op)? { + Ordering::Greater => Ok(Self::True), + Ordering::Less | Ordering::Equal=> Ok(Self::False), + }, + Op::GreaterThanEqual => match lhs.cmp(&rhs, op)? { + Ordering::Greater | Ordering::Equal => Ok(Self::True), + Ordering::Less => Ok(Self::False), + }, + Op::LessThan => match lhs.cmp(&rhs, op)? { + Ordering::Less => Ok(Self::True), + Ordering::Greater | Ordering::Equal=> Ok(Self::False), + }, + Op::LessThanEqual => match lhs.cmp(&rhs, op)? { + Ordering::Less | Ordering::Equal => Ok(Self::True), + Ordering::Greater => Ok(Self::False), + }, }, Self::Paren(v) => v.eval(), Self::UnaryOp(op, val) => match op { @@ -194,4 +210,27 @@ impl Value { _ => Ok(self), } } + + pub fn cmp(&self, other: &Self, op: Op) -> SassResult { + Ok(match self { + Self::Dimension(num, ref unit) => match other { + Self::Dimension(num2, unit2) => { + if !unit.comparable(&unit2) { + return Err(format!("Incompatible units {} and {}.", unit2, unit).into()); + } + if unit == unit2 { + num.cmp(num2) + } else if unit == &Unit::None { + num.cmp(num2) + } else if unit2 == &Unit::None { + num.cmp(num2) + } else { + num.cmp(&(num2.clone() * UNIT_CONVERSION_TABLE[&unit.to_string()][&unit2.to_string()].clone())) + } + } + _ => return Err(format!("Undefined operation \"{} {} {}\".", self, op, other).into()), + }, + _ => return Err(format!("Undefined operation \"{} {} {}\".", self, op, other).into()) + }) + } } diff --git a/src/value/parse.rs b/src/value/parse.rs index 972ff0b..86ee9f0 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -188,6 +188,26 @@ impl Value { return Err("expected \"=\".".into()); } } + q @ '>' | q @ '<' => { + toks.next(); + let op = if toks.peek().unwrap().kind == '=' { + toks.next(); + match q { + '>' => Op::GreaterThanEqual, + '<' => Op::LessThanEqual, + _ => unreachable!() + } + } else { + match q { + '>' => Op::GreaterThan, + '<' => Op::LessThan, + _ => unreachable!() + } + }; + devour_whitespace(toks); + let right = Self::from_tokens(toks, scope, super_selector)?; + Ok(Value::BinaryOp(Box::new(left), op, Box::new(right))) + } '!' => { toks.next(); if toks.peek().unwrap().kind == '=' { diff --git a/tests/ordering.rs b/tests/ordering.rs new file mode 100644 index 0000000..93db708 --- /dev/null +++ b/tests/ordering.rs @@ -0,0 +1,65 @@ +#![cfg(test)] + +#[macro_use] +mod macros; + +test!( + greater_than_or_equal_is_greater, + "a {\n color: 2 >= 1;\n}\n", + "a {\n color: true;\n}\n" +); +test!( + greater_than_or_equal_is_equal, + "a {\n color: 1 >= 1;\n}\n", + "a {\n color: true;\n}\n" +); +test!( + greater_than_or_equal_is_less, + "a {\n color: 0 >= 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + greater_than_is_greater, + "a {\n color: 2 > 1;\n}\n", + "a {\n color: true;\n}\n" +); +test!( + greater_than_is_equal, + "a {\n color: 1 > 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + greater_than_is_less, + "a {\n color: 0 > 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + less_than_or_equal_is_greater, + "a {\n color: 2 <= 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + less_than_or_equal_is_equal, + "a {\n color: 1 <= 1;\n}\n", + "a {\n color: true;\n}\n" +); +test!( + less_than_or_equal_is_less, + "a {\n color: 0 <= 1;\n}\n", + "a {\n color: true;\n}\n" +); +test!( + less_than_is_greater, + "a {\n color: 2 < 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + less_than_is_equal, + "a {\n color: 1 < 1;\n}\n", + "a {\n color: false;\n}\n" +); +test!( + less_than_is_less, + "a {\n color: 0 < 1;\n}\n", + "a {\n color: true;\n}\n" +);