mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +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 super::Value;
|
||||||
use 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.
|
/// Apply the plus operator to a value.
|
||||||
pub fn pos(value: Value) -> Value {
|
pub fn pos(value: Value) -> Value {
|
||||||
match value {
|
match value {
|
||||||
@ -55,7 +74,14 @@ pub fn add(lhs: Value, rhs: Value) -> Value {
|
|||||||
|
|
||||||
(Fractional(a), Fractional(b)) => Fractional(a + b),
|
(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.
|
/// Compute whether two values are equal.
|
||||||
pub fn eq(lhs: Value, rhs: Value) -> Value {
|
pub fn eq(lhs: Value, rhs: Value) -> Value {
|
||||||
Bool(lhs.eq(&rhs))
|
Bool(equal(&lhs, &rhs))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute whether two values are equal.
|
/// Compute whether two values are equal.
|
||||||
pub fn neq(lhs: Value, rhs: Value) -> Value {
|
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 {
|
macro_rules! comparison {
|
||||||
($name:ident, $($pat:tt)*) => {
|
($name:ident, $($pat:tt)*) => {
|
||||||
/// Compute how a value compares with another value.
|
/// Compute how a value compares with another value.
|
||||||
pub fn $name(lhs: Value, rhs: Value) -> Value {
|
pub fn $name(lhs: Value, rhs: Value) -> Value {
|
||||||
lhs.cmp(&rhs)
|
compare(&lhs, &rhs)
|
||||||
.map_or(Value::Error, |x| Value::Bool(matches!(x, $($pat)*)))
|
.map_or(Error, |x| Bool(matches!(x, $($pat)*)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
comparison!(lt, Less);
|
comparison!(lt, Ordering::Less);
|
||||||
comparison!(leq, Less | Equal);
|
comparison!(leq, Ordering::Less | Ordering::Equal);
|
||||||
comparison!(gt, Greater);
|
comparison!(gt, Ordering::Greater);
|
||||||
comparison!(geq, Greater | Equal);
|
comparison!(geq, Ordering::Greater | Ordering::Equal);
|
||||||
|
|
||||||
/// Compute the range from `lhs` to `rhs`.
|
/// Compute the range from `lhs` to `rhs`.
|
||||||
pub fn range(lhs: Value, rhs: Value) -> Value {
|
pub fn range(lhs: Value, rhs: Value) -> Value {
|
||||||
@ -216,29 +292,3 @@ pub fn range(lhs: Value, rhs: Value) -> Value {
|
|||||||
_ => Error,
|
_ => 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};
|
use crate::syntax::{Span, Spanned};
|
||||||
|
|
||||||
/// A computational value.
|
/// A computational value.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
/// The value that indicates the absence of a meaningful value.
|
/// The value that indicates the absence of a meaningful value.
|
||||||
None,
|
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.
|
/// Try to cast the value into a specific type.
|
||||||
pub fn cast<T>(self) -> CastResult<T, Self>
|
pub fn cast<T>(self) -> CastResult<T, Self>
|
||||||
where
|
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.
|
/// A wrapper around a dynamic value.
|
||||||
pub struct AnyValue(Box<dyn Bounds>);
|
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) {
|
while let Some(value) = args.eat::<Value>(ctx) {
|
||||||
if let Some(prev) = &extremum {
|
if let Some(prev) = &extremum {
|
||||||
match value.cmp(&prev) {
|
match value.partial_cmp(&prev) {
|
||||||
Some(ordering) if ordering == goal => extremum = Some(value),
|
Some(ordering) if ordering == goal => extremum = Some(value),
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
None => {
|
None => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user