diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 56bfdf2eb..57390a65b 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -2,9 +2,9 @@ use std::cmp::Ordering; -use super::{Dynamic, RawAlign, RawStroke, Smart, StrExt, Value}; +use super::{Dynamic, RawAlign, RawLength, RawStroke, Smart, StrExt, Value}; use crate::diag::StrResult; -use crate::geom::{Numeric, Spec, SpecAxis}; +use crate::geom::{Numeric, Relative, Spec, SpecAxis}; use crate::model; use Value::*; @@ -204,6 +204,8 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult { (Length(a), Int(b)) => Length(a / b as f64), (Length(a), Float(b)) => Length(a / b), + (Length(a), Length(b)) => Float(div_length(a, b)?), + (Length(a), Relative(b)) if b.rel.is_zero() => Float(div_length(a, b.abs)?), (Angle(a), Int(b)) => Angle(a / b as f64), (Angle(a), Float(b)) => Angle(a / b), @@ -212,9 +214,13 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult { (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(div_length(a.abs, b)?), + (Relative(a), Ratio(b)) if a.abs.is_zero() => Float(a.rel / b), + (Relative(a), Relative(b)) => Float(div_relative(a, b)?), (Fraction(a), Int(b)) => Fraction(a / b as f64), (Fraction(a), Float(b)) => Fraction(a / b), @@ -224,6 +230,28 @@ pub fn div(lhs: Value, rhs: Value) -> StrResult { }) } +/// Try to divide two lengths. +fn div_length(a: RawLength, b: RawLength) -> StrResult { + if a.length.is_zero() && b.length.is_zero() { + Ok(a.em / b.em) + } else if a.em.is_zero() && b.em.is_zero() { + Ok(a.length / b.length) + } else { + return Err("cannot divide these two lengths".into()); + } +} + +/// Try to divide two relative lengths. +fn div_relative(a: Relative, b: Relative) -> StrResult { + if a.rel.is_zero() && b.rel.is_zero() { + div_length(a.abs, b.abs) + } else if a.abs.is_zero() && b.abs.is_zero() { + Ok(a.rel / b.rel) + } else { + return Err("cannot divide these two relative lengths".into()); + } +} + /// Compute the logical "not" of a value. pub fn not(value: Value) -> StrResult { match value { diff --git a/tests/typ/code/ops-invalid.typ b/tests/typ/code/ops-invalid.typ index 4e7fdb040..3e9e54788 100644 --- a/tests/typ/code/ops-invalid.typ +++ b/tests/typ/code/ops-invalid.typ @@ -38,6 +38,26 @@ // Error: 03-10 cannot add integer and string {(1 + "2", 40% - 1)} +--- +// Error: 14-22 cannot add integer and string +{ let x = 1; x += "2" } + +--- +// Error: 3-12 cannot divide ratio by length +{ 10% / 5pt } + +--- +// Error: 3-12 cannot divide these two lengths +{ 1em / 5pt } + +--- +// Error: 3-19 cannot divide relative length by ratio +{ (10% + 1pt) / 5% } + +--- +// Error: 3-28 cannot divide these two relative lengths +{ (10% + 1pt) / (20% + 1pt) } + --- // Error: 12-19 cannot subtract integer from ratio {(1234567, 40% - 1)} @@ -64,10 +84,6 @@ {x * "abcdefgh"} } ---- -// Error: 14-22 cannot add integer and string -{ let x = 1; x += "2" } - --- // Error: 3-6 cannot mutate a temporary value { (x) = "" } diff --git a/tests/typ/code/ops.typ b/tests/typ/code/ops.typ index 1f2867bc8..33a1a4a4f 100644 --- a/tests/typ/code/ops.typ +++ b/tests/typ/code/ops.typ @@ -48,7 +48,15 @@ #test({ let x; x = 1 + 4*5 >= 21 and { x = "a"; x + "b" == "ab" }; x }, true) // Mathematical identities. -#let nums = (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt) +#let nums = ( + 1, 3.14, + 12pt, 3em, 12pt + 3em, + 45deg, + 90%, + 13% + 10pt, 5% + 1em + 3pt, + 2.3fr, +) + #for v in nums { // Test plus and minus. test(v + v - v, v) @@ -63,18 +71,16 @@ test(v + v, 2.0 * v) } - // Lengths cannot be divided by themselves. - if "length" not in type(v) { + if "relative" not in type(v) and ("pt" not in repr(v) or "em" not in repr(v)) { test(v / v, 1.0) - test(v / v == 1, true) } } // Make sure length, ratio and relative length // - can all be added to / subtracted from each other, // - multiplied with integers and floats, -// - divided by floats. -#let dims = (10pt, 30%, 50% + 3cm) +// - divided by integers and floats. +#let dims = (10pt, 1em, 10pt + 1em, 30%, 50% + 3cm, 40% + 2em + 1cm) #for a in dims { for b in dims { test(type(a + b), type(a - b)) @@ -87,6 +93,15 @@ } } +// Test division of different numeric types with zero components. +#for a in (0pt, 0em, 0%) { + for b in (10pt, 10em, 10%) { + test((2 * b) / b, 2) + test((a + b * 2) / b, 2) + test(b / (b * 2 + a), 0.5) + } +} + --- // Test boolean operators.