summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-10 20:22:52 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-10 23:10:35 +0200
commit982ce85976913463eed6c95d3599868c5e1a79dd (patch)
treef546eeb2f7c946a55dfda65ce446ec5671cb8dc9 /src
parent6a4823461f491aef63451f097ddfe5602e0b2157 (diff)
Move comparisons into standard traits
Diffstat (limited to 'src')
-rw-r--r--src/eval/ops.rs122
-rw-r--r--src/eval/value.rs47
-rw-r--r--src/library/utility.rs2
3 files changed, 100 insertions, 71 deletions
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index c1dd726b..53205ce8 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -1,8 +1,27 @@
-use std::cmp::Ordering::*;
+use std::cmp::Ordering;
use super::Value;
use Value::*;
+/// Join a value with another value.
+pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> {
+ Ok(match (lhs, rhs) {
+ (_, Error) => Error,
+ (Error, _) => Error,
+ (a, None) => a,
+ (None, b) => b,
+
+ (Str(a), Str(b)) => Str(a + &b),
+ (Array(a), Array(b)) => Array(a + &b),
+ (Dict(a), Dict(b)) => Dict(a + &b),
+ (Template(a), Template(b)) => Template(a + &b),
+ (Template(a), Str(b)) => Template(a + b),
+ (Str(a), Template(b)) => Template(a + b),
+
+ (lhs, _) => return Err(lhs),
+ })
+}
+
/// Apply the plus operator to a value.
pub fn pos(value: Value) -> Value {
match value {
@@ -55,7 +74,14 @@ pub fn add(lhs: Value, rhs: Value) -> Value {
(Fractional(a), Fractional(b)) => Fractional(a + b),
- (a, b) => concat(a, b).unwrap_or(Value::Error),
+ (Str(a), Str(b)) => Str(a + &b),
+ (Array(a), Array(b)) => Array(a + &b),
+ (Dict(a), Dict(b)) => Dict(a + &b),
+ (Template(a), Template(b)) => Template(a + &b),
+ (Template(a), Str(b)) => Template(a + b),
+ (Str(a), Template(b)) => Template(a + b),
+
+ _ => Error,
}
}
@@ -184,30 +210,80 @@ pub fn or(lhs: Value, rhs: Value) -> Value {
}
}
+/// Determine whether two values are equal.
+pub fn equal(lhs: &Value, rhs: &Value) -> bool {
+ match (lhs, rhs) {
+ // Compare reflexively.
+ (None, None) => true,
+ (Auto, Auto) => true,
+ (Bool(a), Bool(b)) => a == b,
+ (Int(a), Int(b)) => a == b,
+ (Float(a), Float(b)) => a == b,
+ (Length(a), Length(b)) => a == b,
+ (Angle(a), Angle(b)) => a == b,
+ (Relative(a), Relative(b)) => a == b,
+ (Linear(a), Linear(b)) => a == b,
+ (Fractional(a), Fractional(b)) => a == b,
+ (Color(a), Color(b)) => a == b,
+ (Str(a), Str(b)) => a == b,
+ (Array(a), Array(b)) => a == b,
+ (Dict(a), Dict(b)) => a == b,
+ (Template(a), Template(b)) => a == b,
+ (Func(a), Func(b)) => a == b,
+ (Any(a), Any(b)) => a == b,
+ (Error, Error) => true,
+
+ // Some technically different things should compare equal.
+ (&Int(a), &Float(b)) => a as f64 == b,
+ (&Float(a), &Int(b)) => a == b as f64,
+ (&Length(a), &Linear(b)) => a == b.abs && b.rel.is_zero(),
+ (&Relative(a), &Linear(b)) => a == b.rel && b.abs.is_zero(),
+ (&Linear(a), &Length(b)) => a.abs == b && a.rel.is_zero(),
+ (&Linear(a), &Relative(b)) => a.rel == b && a.abs.is_zero(),
+
+ _ => false,
+ }
+}
+
/// Compute whether two values are equal.
pub fn eq(lhs: Value, rhs: Value) -> Value {
- Bool(lhs.eq(&rhs))
+ Bool(equal(&lhs, &rhs))
}
/// Compute whether two values are equal.
pub fn neq(lhs: Value, rhs: Value) -> Value {
- Bool(!lhs.eq(&rhs))
+ Bool(!equal(&lhs, &rhs))
+}
+
+/// Compare two values.
+pub fn compare(lhs: &Value, rhs: &Value) -> Option<Ordering> {
+ match (lhs, rhs) {
+ (Bool(a), Bool(b)) => a.partial_cmp(b),
+ (Int(a), Int(b)) => a.partial_cmp(b),
+ (Int(a), Float(b)) => (*a as f64).partial_cmp(b),
+ (Float(a), Int(b)) => a.partial_cmp(&(*b as f64)),
+ (Float(a), Float(b)) => a.partial_cmp(b),
+ (Angle(a), Angle(b)) => a.partial_cmp(b),
+ (Length(a), Length(b)) => a.partial_cmp(b),
+ (Str(a), Str(b)) => a.partial_cmp(b),
+ _ => Option::None,
+ }
}
macro_rules! comparison {
($name:ident, $($pat:tt)*) => {
/// Compute how a value compares with another value.
pub fn $name(lhs: Value, rhs: Value) -> Value {
- lhs.cmp(&rhs)
- .map_or(Value::Error, |x| Value::Bool(matches!(x, $($pat)*)))
+ compare(&lhs, &rhs)
+ .map_or(Error, |x| Bool(matches!(x, $($pat)*)))
}
};
}
-comparison!(lt, Less);
-comparison!(leq, Less | Equal);
-comparison!(gt, Greater);
-comparison!(geq, Greater | Equal);
+comparison!(lt, Ordering::Less);
+comparison!(leq, Ordering::Less | Ordering::Equal);
+comparison!(gt, Ordering::Greater);
+comparison!(geq, Ordering::Greater | Ordering::Equal);
/// Compute the range from `lhs` to `rhs`.
pub fn range(lhs: Value, rhs: Value) -> Value {
@@ -216,29 +292,3 @@ pub fn range(lhs: Value, rhs: Value) -> Value {
_ => Error,
}
}
-
-/// Join a value with another value.
-pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> {
- Ok(match (lhs, rhs) {
- (_, Error) => Error,
- (Error, _) => Error,
-
- (a, None) => a,
- (None, b) => b,
-
- (a, b) => return concat(a, b),
- })
-}
-
-/// Concatentate two values.
-fn concat(lhs: Value, rhs: Value) -> Result<Value, Value> {
- Ok(match (lhs, rhs) {
- (Str(a), Str(b)) => Str(a + &b),
- (Array(a), Array(b)) => Array(a + &b),
- (Dict(a), Dict(b)) => Dict(a + &b),
- (Template(a), Template(b)) => Template(a + &b),
- (Template(a), Str(b)) => Template(a + b),
- (Str(a), Template(b)) => Template(a + b),
- (a, _) => return Err(a),
- })
-}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index fe9494b1..7c35fdbd 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -10,7 +10,7 @@ use crate::geom::{Angle, Fractional, Length, Linear, Relative};
use crate::syntax::{Span, Spanned};
/// A computational value.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone)]
pub enum Value {
/// The value that indicates the absence of a meaningful value.
None,
@@ -83,39 +83,6 @@ impl Value {
}
}
- /// Recursively compute whether two values are equal.
- pub fn eq(&self, rhs: &Self) -> bool {
- match (self, rhs) {
- (&Self::Int(a), &Self::Float(b)) => a as f64 == b,
- (&Self::Float(a), &Self::Int(b)) => a == b as f64,
- (&Self::Length(a), &Self::Linear(b)) => a == b.abs && b.rel.is_zero(),
- (&Self::Relative(a), &Self::Linear(b)) => a == b.rel && b.abs.is_zero(),
- (&Self::Linear(a), &Self::Length(b)) => a.abs == b && a.rel.is_zero(),
- (&Self::Linear(a), &Self::Relative(b)) => a.rel == b && a.abs.is_zero(),
- (Self::Array(a), Self::Array(b)) => {
- a.len() == b.len() && a.iter().zip(b).all(|(x, y)| x.eq(y))
- }
- (Self::Dict(a), Self::Dict(b)) => {
- a.len() == b.len()
- && a.iter().all(|(k, x)| b.get(k).map_or(false, |y| x.eq(y)))
- }
- (a, b) => a == b,
- }
- }
-
- /// Compare a value with another value.
- pub fn cmp(&self, rhs: &Self) -> Option<Ordering> {
- match (self, rhs) {
- (Self::Int(a), Self::Int(b)) => a.partial_cmp(b),
- (Self::Int(a), Self::Float(b)) => (*a as f64).partial_cmp(b),
- (Self::Float(a), Self::Int(b)) => a.partial_cmp(&(*b as f64)),
- (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
- (Self::Angle(a), Self::Angle(b)) => a.partial_cmp(b),
- (Self::Length(a), Self::Length(b)) => a.partial_cmp(b),
- _ => None,
- }
- }
-
/// Try to cast the value into a specific type.
pub fn cast<T>(self) -> CastResult<T, Self>
where
@@ -143,6 +110,18 @@ impl Default for Value {
}
}
+impl PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ ops::equal(self, other)
+ }
+}
+
+impl PartialOrd for Value {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ ops::compare(self, other)
+ }
+}
+
/// A wrapper around a dynamic value.
pub struct AnyValue(Box<dyn Bounds>);
diff --git a/src/library/utility.rs b/src/library/utility.rs
index c1f20cc6..eaa6146d 100644
--- a/src/library/utility.rs
+++ b/src/library/utility.rs
@@ -82,7 +82,7 @@ fn minmax(ctx: &mut EvalContext, args: &mut FuncArgs, goal: Ordering) -> Value {
while let Some(value) = args.eat::<Value>(ctx) {
if let Some(prev) = &extremum {
- match value.cmp(&prev) {
+ match value.partial_cmp(&prev) {
Some(ordering) if ordering == goal => extremum = Some(value),
Some(_) => {}
None => {