From 2dc000daeba1718673b0f0025b31a78c001223d1 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 11 Sep 2023 17:26:56 +0200 Subject: [PATCH] Type compatibility For now, types can be used like strings in operations to prevent breakage of many packages. Hopefully, we can get rid of this in the future. --- crates/typst/src/eval/ops.rs | 18 ++++++++++++++++++ crates/typst/src/eval/ty.rs | 8 ++++++++ tests/typ/compiler/type-compatibility.typ | 10 ++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tests/typ/compiler/type-compatibility.typ diff --git a/crates/typst/src/eval/ops.rs b/crates/typst/src/eval/ops.rs index e44c3a412..23a324167 100644 --- a/crates/typst/src/eval/ops.rs +++ b/crates/typst/src/eval/ops.rs @@ -34,6 +34,11 @@ pub fn join(lhs: Value, rhs: Value) -> StrResult { (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), + + // Type compatibility. + (Type(a), Str(b)) => Str(format_str!("{a}{b}")), + (Str(a), Type(b)) => Str(format_str!("{a}{b}")), + (a, b) => mismatch!("cannot join {} with {}", a, b), }) } @@ -119,6 +124,10 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult { (Datetime(a), Duration(b)) => Datetime(a + b), (Duration(a), Datetime(b)) => Datetime(b + a), + // Type compatibility. + (Type(a), Str(b)) => Str(format_str!("{a}{b}")), + (Str(a), Type(b)) => Str(format_str!("{a}{b}")), + (Dyn(a), Dyn(b)) => { // Alignments can be summed. if let (Some(&a), Some(&b)) = (a.downcast::(), b.downcast::()) { @@ -380,6 +389,10 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool { (&Relative(a), &Length(b)) => a.abs == b && a.rel.is_zero(), (&Relative(a), &Ratio(b)) => a.rel == b && a.abs.is_zero(), + // Type compatibility. + (Type(a), Str(b)) => a.compat_name() == b.as_str(), + (Str(a), Type(b)) => a.as_str() == b.compat_name(), + _ => false, } } @@ -449,6 +462,11 @@ pub fn contains(lhs: &Value, rhs: &Value) -> Option { (Dyn(a), Str(b)) => a.downcast::().map(|regex| regex.is_match(b)), (Str(a), Dict(b)) => Some(b.contains(a)), (a, Array(b)) => Some(b.contains(a.clone())), + + // Type compatibility. + (Type(a), Str(b)) => Some(b.as_str().contains(a.compat_name())), + (Type(a), Dict(b)) => Some(b.contains(a.compat_name())), + _ => Option::None, } } diff --git a/crates/typst/src/eval/ty.rs b/crates/typst/src/eval/ty.rs index f78326722..da3c91aaf 100644 --- a/crates/typst/src/eval/ty.rs +++ b/crates/typst/src/eval/ty.rs @@ -80,6 +80,14 @@ impl Type { } } +// Type compatibility. +impl Type { + /// The type's backwards-compatible name. + pub fn compat_name(&self) -> &str { + self.long_name() + } +} + #[scope] impl Type { /// Determines a value's type. diff --git a/tests/typ/compiler/type-compatibility.typ b/tests/typ/compiler/type-compatibility.typ new file mode 100644 index 000000000..664981f47 --- /dev/null +++ b/tests/typ/compiler/type-compatibility.typ @@ -0,0 +1,10 @@ +// Test compatibility between types and strings. +// Ref: false + +--- +#test(type(10), int) +#test(type(10), "integer") +#test("is " + type(10), "is integer") +#test(int in ("integer", "string"), true) +#test(int in "integers or strings", true) +#test(str in "integers or strings", true)