summaryrefslogtreecommitdiff
path: root/src/eval/ops.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-07-02 19:59:52 +0200
committerLaurenz <laurmaedje@gmail.com>2023-07-02 20:07:43 +0200
commitebfdb1dafa430786db10dad2ef7d5467c1bdbed1 (patch)
tree2bbc24ddb4124c4bb14dec0e536129d4de37b056 /src/eval/ops.rs
parent3ab19185093d7709f824b95b979060ce125389d8 (diff)
Move everything into `crates/` directory
Diffstat (limited to 'src/eval/ops.rs')
-rw-r--r--src/eval/ops.rs429
1 files changed, 0 insertions, 429 deletions
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
deleted file mode 100644
index 0880a87e..00000000
--- a/src/eval/ops.rs
+++ /dev/null
@@ -1,429 +0,0 @@
-//! Operations on values.
-
-use std::cmp::Ordering;
-use std::fmt::Debug;
-
-use ecow::eco_format;
-
-use super::{format_str, Regex, Value};
-use crate::diag::{bail, StrResult};
-use crate::geom::{Axes, Axis, GenAlign, Length, Numeric, PartialStroke, Rel, Smart};
-use Value::*;
-
-/// Bail with a type mismatch error.
-macro_rules! mismatch {
- ($fmt:expr, $($value:expr),* $(,)?) => {
- return Err(eco_format!($fmt, $($value.type_name()),*))
- };
-}
-
-/// Join a value with another value.
-pub fn join(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(match (lhs, rhs) {
- (a, None) => a,
- (None, b) => b,
- (Symbol(a), Symbol(b)) => Str(format_str!("{a}{b}")),
- (Str(a), Str(b)) => Str(a + b),
- (Str(a), Symbol(b)) => Str(format_str!("{a}{b}")),
- (Symbol(a), Str(b)) => Str(format_str!("{a}{b}")),
- (Content(a), Content(b)) => Content(a + b),
- (Content(a), Symbol(b)) => Content(a + item!(text)(b.get().into())),
- (Content(a), Str(b)) => Content(a + item!(text)(b.into())),
- (Str(a), Content(b)) => Content(item!(text)(a.into()) + b),
- (Symbol(a), Content(b)) => Content(item!(text)(a.get().into()) + b),
- (Array(a), Array(b)) => Array(a + b),
- (Dict(a), Dict(b)) => Dict(a + b),
- (a, b) => mismatch!("cannot join {} with {}", a, b),
- })
-}
-
-/// Apply the unary plus operator to a value.
-pub fn pos(value: Value) -> StrResult<Value> {
- Ok(match value {
- Int(v) => Int(v),
- Float(v) => Float(v),
- Length(v) => Length(v),
- Angle(v) => Angle(v),
- Ratio(v) => Ratio(v),
- Relative(v) => Relative(v),
- Fraction(v) => Fraction(v),
- v => mismatch!("cannot apply '+' to {}", v),
- })
-}
-
-/// Compute the negation of a value.
-pub fn neg(value: Value) -> StrResult<Value> {
- Ok(match value {
- Int(v) => Int(v.checked_neg().ok_or("value is too large")?),
- Float(v) => Float(-v),
- Length(v) => Length(-v),
- Angle(v) => Angle(-v),
- Ratio(v) => Ratio(-v),
- Relative(v) => Relative(-v),
- Fraction(v) => Fraction(-v),
- v => mismatch!("cannot apply '-' to {}", v),
- })
-}
-
-/// Compute the sum of two values.
-pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(match (lhs, rhs) {
- (a, None) => a,
- (None, b) => b,
-
- (Int(a), Int(b)) => Int(a.checked_add(b).ok_or("value is too large")?),
- (Int(a), Float(b)) => Float(a as f64 + b),
- (Float(a), Int(b)) => Float(a + b as f64),
- (Float(a), Float(b)) => Float(a + b),
-
- (Angle(a), Angle(b)) => Angle(a + b),
-
- (Length(a), Length(b)) => Length(a + b),
- (Length(a), Ratio(b)) => Relative(b + a),
- (Length(a), Relative(b)) => Relative(b + a),
-
- (Ratio(a), Length(b)) => Relative(a + b),
- (Ratio(a), Ratio(b)) => Ratio(a + b),
- (Ratio(a), Relative(b)) => Relative(b + a),
-
- (Relative(a), Length(b)) => Relative(a + b),
- (Relative(a), Ratio(b)) => Relative(a + b),
- (Relative(a), Relative(b)) => Relative(a + b),
-
- (Fraction(a), Fraction(b)) => Fraction(a + b),
-
- (Symbol(a), Symbol(b)) => Str(format_str!("{a}{b}")),
- (Str(a), Str(b)) => Str(a + b),
- (Str(a), Symbol(b)) => Str(format_str!("{a}{b}")),
- (Symbol(a), Str(b)) => Str(format_str!("{a}{b}")),
- (Content(a), Content(b)) => Content(a + b),
- (Content(a), Symbol(b)) => Content(a + item!(text)(b.get().into())),
- (Content(a), Str(b)) => Content(a + item!(text)(b.into())),
- (Str(a), Content(b)) => Content(item!(text)(a.into()) + b),
- (Symbol(a), Content(b)) => Content(item!(text)(a.get().into()) + b),
-
- (Array(a), Array(b)) => Array(a + b),
- (Dict(a), Dict(b)) => Dict(a + b),
-
- (Color(color), Length(thickness)) | (Length(thickness), Color(color)) => {
- Value::dynamic(PartialStroke {
- paint: Smart::Custom(color.into()),
- thickness: Smart::Custom(thickness),
- ..PartialStroke::default()
- })
- }
-
- (Dyn(a), Dyn(b)) => {
- // 1D alignments can be summed into 2D alignments.
- if let (Some(&a), Some(&b)) =
- (a.downcast::<GenAlign>(), b.downcast::<GenAlign>())
- {
- if a.axis() == b.axis() {
- return Err(eco_format!("cannot add two {:?} alignments", a.axis()));
- }
-
- return Ok(Value::dynamic(match a.axis() {
- Axis::X => Axes { x: a, y: b },
- Axis::Y => Axes { x: b, y: a },
- }));
- };
-
- mismatch!("cannot add {} and {}", a, b);
- }
-
- (a, b) => mismatch!("cannot add {} and {}", a, b),
- })
-}
-
-/// Compute the difference of two values.
-pub fn sub(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(match (lhs, rhs) {
- (Int(a), Int(b)) => Int(a.checked_sub(b).ok_or("value is too large")?),
- (Int(a), Float(b)) => Float(a as f64 - b),
- (Float(a), Int(b)) => Float(a - b as f64),
- (Float(a), Float(b)) => Float(a - b),
-
- (Angle(a), Angle(b)) => Angle(a - b),
-
- (Length(a), Length(b)) => Length(a - b),
- (Length(a), Ratio(b)) => Relative(-b + a),
- (Length(a), Relative(b)) => Relative(-b + a),
-
- (Ratio(a), Length(b)) => Relative(a + -b),
- (Ratio(a), Ratio(b)) => Ratio(a - b),
- (Ratio(a), Relative(b)) => Relative(-b + a),
-
- (Relative(a), Length(b)) => Relative(a + -b),
- (Relative(a), Ratio(b)) => Relative(a + -b),
- (Relative(a), Relative(b)) => Relative(a - b),
-
- (Fraction(a), Fraction(b)) => Fraction(a - b),
-
- (a, b) => mismatch!("cannot subtract {1} from {0}", a, b),
- })
-}
-
-/// Compute the product of two values.
-pub fn mul(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(match (lhs, rhs) {
- (Int(a), Int(b)) => Int(a.checked_mul(b).ok_or("value is too large")?),
- (Int(a), Float(b)) => Float(a as f64 * b),
- (Float(a), Int(b)) => Float(a * b as f64),
- (Float(a), Float(b)) => Float(a * b),
-
- (Length(a), Int(b)) => Length(a * b as f64),
- (Length(a), Float(b)) => Length(a * b),
- (Length(a), Ratio(b)) => Length(a * b.get()),
- (Int(a), Length(b)) => Length(b * a as f64),
- (Float(a), Length(b)) => Length(b * a),
- (Ratio(a), Length(b)) => Length(b * a.get()),
-
- (Angle(a), Int(b)) => Angle(a * b as f64),
- (Angle(a), Float(b)) => Angle(a * b),
- (Angle(a), Ratio(b)) => Angle(a * b.get()),
- (Int(a), Angle(b)) => Angle(a as f64 * b),
- (Float(a), Angle(b)) => Angle(a * b),
- (Ratio(a), Angle(b)) => Angle(a.get() * b),
-
- (Ratio(a), Ratio(b)) => Ratio(a * b),
- (Ratio(a), Int(b)) => Ratio(a * b as f64),
- (Ratio(a), Float(b)) => Ratio(a * b),
- (Int(a), Ratio(b)) => Ratio(a as f64 * b),
- (Float(a), Ratio(b)) => Ratio(a * b),
-
- (Relative(a), Int(b)) => Relative(a * b as f64),
- (Relative(a), Float(b)) => Relative(a * b),
- (Relative(a), Ratio(b)) => Relative(a * b.get()),
- (Int(a), Relative(b)) => Relative(a as f64 * b),
- (Float(a), Relative(b)) => Relative(a * b),
- (Ratio(a), Relative(b)) => Relative(a.get() * b),
-
- (Fraction(a), Int(b)) => Fraction(a * b as f64),
- (Fraction(a), Float(b)) => Fraction(a * b),
- (Fraction(a), Ratio(b)) => Fraction(a * b.get()),
- (Int(a), Fraction(b)) => Fraction(a as f64 * b),
- (Float(a), Fraction(b)) => Fraction(a * b),
- (Ratio(a), Fraction(b)) => Fraction(a.get() * b),
-
- (Str(a), Int(b)) => Str(a.repeat(b)?),
- (Int(a), Str(b)) => Str(b.repeat(a)?),
- (Array(a), Int(b)) => Array(a.repeat(b)?),
- (Int(a), Array(b)) => Array(b.repeat(a)?),
- (Content(a), b @ Int(_)) => Content(a.repeat(b.cast()?)),
- (a @ Int(_), Content(b)) => Content(b.repeat(a.cast()?)),
-
- (a, b) => mismatch!("cannot multiply {} with {}", a, b),
- })
-}
-
-/// Compute the quotient of two values.
-pub fn div(lhs: Value, rhs: Value) -> StrResult<Value> {
- if is_zero(&rhs) {
- bail!("cannot divide by zero");
- }
-
- Ok(match (lhs, rhs) {
- (Int(a), Int(b)) => Float(a as f64 / b as f64),
- (Int(a), Float(b)) => Float(a as f64 / b),
- (Float(a), Int(b)) => Float(a / b as f64),
- (Float(a), Float(b)) => Float(a / b),
-
- (Length(a), Int(b)) => Length(a / b as f64),
- (Length(a), Float(b)) => Length(a / b),
- (Length(a), Length(b)) => Float(try_div_length(a, b)?),
- (Length(a), Relative(b)) if b.rel.is_zero() => Float(try_div_length(a, b.abs)?),
-
- (Angle(a), Int(b)) => Angle(a / b as f64),
- (Angle(a), Float(b)) => Angle(a / b),
- (Angle(a), Angle(b)) => Float(a / b),
-
- (Ratio(a), Int(b)) => Ratio(a / b as f64),
- (Ratio(a), Float(b)) => Ratio(a / b),
- (Ratio(a), Ratio(b)) => Float(a / b),
- (Ratio(a), Relative(b)) if b.abs.is_zero() => Float(a / b.rel),
-
- (Relative(a), Int(b)) => Relative(a / b as f64),
- (Relative(a), Float(b)) => Relative(a / b),
- (Relative(a), Length(b)) if a.rel.is_zero() => Float(try_div_length(a.abs, b)?),
- (Relative(a), Ratio(b)) if a.abs.is_zero() => Float(a.rel / b),
- (Relative(a), Relative(b)) => Float(try_div_relative(a, b)?),
-
- (Fraction(a), Int(b)) => Fraction(a / b as f64),
- (Fraction(a), Float(b)) => Fraction(a / b),
- (Fraction(a), Fraction(b)) => Float(a / b),
-
- (a, b) => mismatch!("cannot divide {} by {}", a, b),
- })
-}
-
-/// Whether a value is a numeric zero.
-fn is_zero(v: &Value) -> bool {
- match *v {
- Int(v) => v == 0,
- Float(v) => v == 0.0,
- Length(v) => v.is_zero(),
- Angle(v) => v.is_zero(),
- Ratio(v) => v.is_zero(),
- Relative(v) => v.is_zero(),
- Fraction(v) => v.is_zero(),
- _ => false,
- }
-}
-
-/// Try to divide two lengths.
-fn try_div_length(a: Length, b: Length) -> StrResult<f64> {
- a.try_div(b).ok_or_else(|| "cannot divide these two lengths".into())
-}
-
-/// Try to divide two relative lengths.
-fn try_div_relative(a: Rel<Length>, b: Rel<Length>) -> StrResult<f64> {
- a.try_div(b)
- .ok_or_else(|| "cannot divide these two relative lengths".into())
-}
-
-/// Compute the logical "not" of a value.
-pub fn not(value: Value) -> StrResult<Value> {
- match value {
- Bool(b) => Ok(Bool(!b)),
- v => mismatch!("cannot apply 'not' to {}", v),
- }
-}
-
-/// Compute the logical "and" of two values.
-pub fn and(lhs: Value, rhs: Value) -> StrResult<Value> {
- match (lhs, rhs) {
- (Bool(a), Bool(b)) => Ok(Bool(a && b)),
- (a, b) => mismatch!("cannot apply 'and' to {} and {}", a, b),
- }
-}
-
-/// Compute the logical "or" of two values.
-pub fn or(lhs: Value, rhs: Value) -> StrResult<Value> {
- match (lhs, rhs) {
- (Bool(a), Bool(b)) => Ok(Bool(a || b)),
- (a, b) => mismatch!("cannot apply 'or' to {} and {}", a, b),
- }
-}
-
-/// Compute whether two values are equal.
-pub fn eq(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(Bool(equal(&lhs, &rhs)))
-}
-
-/// Compute whether two values are unequal.
-pub fn neq(lhs: Value, rhs: Value) -> StrResult<Value> {
- Ok(Bool(!equal(&lhs, &rhs)))
-}
-
-macro_rules! comparison {
- ($name:ident, $op:tt, $($pat:tt)*) => {
- /// Compute how a value compares with another value.
- pub fn $name(lhs: Value, rhs: Value) -> StrResult<Value> {
- let ordering = compare(&lhs, &rhs)?;
- Ok(Bool(matches!(ordering, $($pat)*)))
- }
- };
-}
-
-comparison!(lt, "<", Ordering::Less);
-comparison!(leq, "<=", Ordering::Less | Ordering::Equal);
-comparison!(gt, ">", Ordering::Greater);
-comparison!(geq, ">=", Ordering::Greater | Ordering::Equal);
-
-/// 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,
- (Ratio(a), Ratio(b)) => a == b,
- (Relative(a), Relative(b)) => a == b,
- (Fraction(a), Fraction(b)) => a == b,
- (Color(a), Color(b)) => a == b,
- (Symbol(a), Symbol(b)) => a == b,
- (Str(a), Str(b)) => a == b,
- (Label(a), Label(b)) => a == b,
- (Content(a), Content(b)) => a == b,
- (Array(a), Array(b)) => a == b,
- (Dict(a), Dict(b)) => a == b,
- (Func(a), Func(b)) => a == b,
- (Args(a), Args(b)) => a == b,
- (Module(a), Module(b)) => a == b,
- (Dyn(a), Dyn(b)) => a == b,
-
- // 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), &Relative(b)) => a == b.abs && b.rel.is_zero(),
- (&Ratio(a), &Relative(b)) => a == b.rel && b.abs.is_zero(),
- (&Relative(a), &Length(b)) => a.abs == b && a.rel.is_zero(),
- (&Relative(a), &Ratio(b)) => a.rel == b && a.abs.is_zero(),
-
- _ => false,
- }
-}
-
-/// Compare two values.
-pub fn compare(lhs: &Value, rhs: &Value) -> StrResult<Ordering> {
- Ok(match (lhs, rhs) {
- (Bool(a), Bool(b)) => a.cmp(b),
- (Int(a), Int(b)) => a.cmp(b),
- (Float(a), Float(b)) => try_cmp_values(a, b)?,
- (Length(a), Length(b)) => try_cmp_values(a, b)?,
- (Angle(a), Angle(b)) => a.cmp(b),
- (Ratio(a), Ratio(b)) => a.cmp(b),
- (Relative(a), Relative(b)) => try_cmp_values(a, b)?,
- (Fraction(a), Fraction(b)) => a.cmp(b),
- (Str(a), Str(b)) => a.cmp(b),
-
- // Some technically different things should be comparable.
- (Int(a), Float(b)) => try_cmp_values(&(*a as f64), b)?,
- (Float(a), Int(b)) => try_cmp_values(a, &(*b as f64))?,
- (Length(a), Relative(b)) if b.rel.is_zero() => try_cmp_values(a, &b.abs)?,
- (Ratio(a), Relative(b)) if b.abs.is_zero() => a.cmp(&b.rel),
- (Relative(a), Length(b)) if a.rel.is_zero() => try_cmp_values(&a.abs, b)?,
- (Relative(a), Ratio(b)) if a.abs.is_zero() => a.rel.cmp(b),
-
- _ => mismatch!("cannot compare {} and {}", lhs, rhs),
- })
-}
-
-/// Try to compare two values.
-fn try_cmp_values<T: PartialOrd + Debug>(a: &T, b: &T) -> StrResult<Ordering> {
- a.partial_cmp(b)
- .ok_or_else(|| eco_format!("cannot compare {:?} with {:?}", a, b))
-}
-
-/// Test whether one value is "in" another one.
-pub fn in_(lhs: Value, rhs: Value) -> StrResult<Value> {
- if let Some(b) = contains(&lhs, &rhs) {
- Ok(Bool(b))
- } else {
- mismatch!("cannot apply 'in' to {} and {}", lhs, rhs)
- }
-}
-
-/// Test whether one value is "not in" another one.
-pub fn not_in(lhs: Value, rhs: Value) -> StrResult<Value> {
- if let Some(b) = contains(&lhs, &rhs) {
- Ok(Bool(!b))
- } else {
- mismatch!("cannot apply 'not in' to {} and {}", lhs, rhs)
- }
-}
-
-/// Test for containment.
-pub fn contains(lhs: &Value, rhs: &Value) -> Option<bool> {
- match (lhs, rhs) {
- (Str(a), Str(b)) => Some(b.as_str().contains(a.as_str())),
- (Dyn(a), Str(b)) => a.downcast::<Regex>().map(|regex| regex.is_match(b)),
- (Str(a), Dict(b)) => Some(b.contains(a)),
- (a, Array(b)) => Some(b.contains(a)),
- _ => Option::None,
- }
-}