mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Move comparisons into standard traits
This commit is contained in:
parent
6a4823461f
commit
982ce85976
122
src/eval/ops.rs
122
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),
|
||||
})
|
||||
}
|
||||
|
@ -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>);
|
||||
|
||||
|
@ -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 => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user