diff --git a/crates/typst-macros/src/scope.rs b/crates/typst-macros/src/scope.rs index 07a0efe05..d86c6aea9 100644 --- a/crates/typst-macros/src/scope.rs +++ b/crates/typst-macros/src/scope.rs @@ -13,11 +13,26 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result { let self_ty = &item.self_ty; + let mut primitive_ident_ext = None; + if let syn::Type::Path(syn::TypePath { path, .. }) = self_ty.as_ref() { + if let Some(ident) = path.get_ident() { + if is_primitive(ident) { + let ident_ext = quote::format_ident!("{ident}Ext"); + primitive_ident_ext = Some(ident_ext); + } + } + } + + let self_ty_expr = match &primitive_ident_ext { + None => quote! { #self_ty }, + Some(ident_ext) => quote! { <#self_ty as #ident_ext> }, + }; + let mut definitions = vec![]; let mut constructor = quote! { None }; for child in &mut item.items { let def = match child { - syn::ImplItem::Const(item) => handle_const(self_ty, item)?, + syn::ImplItem::Const(item) => handle_const(&self_ty_expr, item)?, syn::ImplItem::Fn(item) => match handle_fn(self_ty, item)? { FnKind::Member(tokens) => tokens, FnKind::Constructor(tokens) => { @@ -33,14 +48,10 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result { item.items.retain(|item| !matches!(item, syn::ImplItem::Verbatim(_))); - let mut base = quote! { #item }; - if let syn::Type::Path(syn::TypePath { path, .. }) = self_ty.as_ref() { - if let Some(ident) = path.get_ident() { - if is_primitive(ident) { - base = rewrite_primitive_base(&item, ident); - } - } - } + let base = match &primitive_ident_ext { + None => quote! { #item }, + Some(ident_ext) => rewrite_primitive_base(&item, ident_ext), + }; Ok(quote! { #base @@ -60,7 +71,7 @@ pub fn scope(_: TokenStream, item: syn::Item) -> Result { } /// Process a const item and returns its definition. -fn handle_const(self_ty: &syn::Type, item: &syn::ImplItemConst) -> Result { +fn handle_const(self_ty: &TokenStream, item: &syn::ImplItemConst) -> Result { let ident = &item.ident; let name = ident.to_string().to_kebab_case(); Ok(quote! { scope.define(#name, #self_ty::#ident) }) @@ -117,31 +128,39 @@ fn is_primitive(ident: &syn::Ident) -> bool { } /// Rewrite an impl block for a primitive into a trait + trait impl. -fn rewrite_primitive_base(item: &syn::ItemImpl, ident: &syn::Ident) -> TokenStream { +fn rewrite_primitive_base(item: &syn::ItemImpl, ident_ext: &syn::Ident) -> TokenStream { let mut sigs = vec![]; let mut items = vec![]; for sub in &item.items { - let syn::ImplItem::Fn(mut func) = sub.clone() else { continue }; - func.vis = syn::Visibility::Inherited; - items.push(func.clone()); + match sub.clone() { + syn::ImplItem::Fn(mut func) => { + func.vis = syn::Visibility::Inherited; + items.push(func.clone()); - let mut sig = func.sig; - let inputs = sig.inputs.iter().cloned().map(|mut input| { - if let syn::FnArg::Typed(typed) = &mut input { - typed.attrs.clear(); + let mut sig = func.sig; + let inputs = sig.inputs.iter().cloned().map(|mut input| { + if let syn::FnArg::Typed(typed) = &mut input { + typed.attrs.clear(); + } + input + }); + sig.inputs = parse_quote! { #(#inputs),* }; + + let ident_data = quote::format_ident!("{}_data", sig.ident); + sigs.push(quote! { #sig; }); + sigs.push(quote! { + fn #ident_data() -> &'static #foundations::NativeFuncData; + }); } - input - }); - sig.inputs = parse_quote! { #(#inputs),* }; - let ident_data = quote::format_ident!("{}_data", sig.ident); - sigs.push(quote! { #sig; }); - sigs.push(quote! { - fn #ident_data() -> &'static #foundations::NativeFuncData; - }); + syn::ImplItem::Const(cons) => { + sigs.push(quote! { #cons }); + } + + _ => {} + } } - let ident_ext = quote::format_ident!("{ident}Ext"); let self_ty = &item.self_ty; quote! { trait #ident_ext { diff --git a/crates/typst/src/foundations/calc.rs b/crates/typst/src/foundations/calc.rs index d07b23c5a..287bc828a 100644 --- a/crates/typst/src/foundations/calc.rs +++ b/crates/typst/src/foundations/calc.rs @@ -50,7 +50,6 @@ pub fn module() -> Module { scope.define_func::(); scope.define_func::(); scope.define("inf", f64::INFINITY); - scope.define("nan", f64::NAN); scope.define("pi", std::f64::consts::PI); scope.define("tau", std::f64::consts::TAU); scope.define("e", std::f64::consts::E); diff --git a/crates/typst/src/foundations/float.rs b/crates/typst/src/foundations/float.rs index d346c3296..fd094950b 100644 --- a/crates/typst/src/foundations/float.rs +++ b/crates/typst/src/foundations/float.rs @@ -13,6 +13,9 @@ use crate::layout::Ratio; /// /// You can convert a value to a float with this type's constructor. /// +/// NaN and positive infinity are available as `{float.nan}` and `{float.inf}` +/// respectively. +/// /// # Example /// ```example /// #3.14 \ @@ -24,6 +27,13 @@ type f64; #[scope] impl f64 { + /// Positive infinity. + const INF: f64 = f64::INFINITY; + + /// A NaN value, as defined by the + /// [IEEE 754 standard](https://en.wikipedia.org/wiki/IEEE_754). + const NAN: f64 = f64::NAN; + /// Converts a value to a float. /// /// - Booleans are converted to `0.0` or `1.0`. @@ -58,7 +68,7 @@ impl f64 { /// ```example /// #float.is-nan(0) \ /// #float.is-nan(1) \ - /// #float.is-nan(calc.nan) + /// #float.is-nan(float.nan) /// ``` #[func] pub fn is_nan(self) -> bool { @@ -73,7 +83,7 @@ impl f64 { /// ```example /// #float.is-infinite(0) \ /// #float.is-infinite(1) \ - /// #float.is-infinite(calc.inf) + /// #float.is-infinite(float.inf) /// ``` #[func] pub fn is_infinite(self) -> bool { @@ -84,13 +94,13 @@ impl f64 { /// /// - If the number is positive (including `{+0.0}`), returns `{1.0}`. /// - If the number is negative (including `{-0.0}`), returns `{-1.0}`. - /// - If the number is `{calc.nan}`, returns `{calc.nan}`. + /// - If the number is `{float.nan}`, returns `{float.nan}`. /// /// ```example /// #(5.0).signum() \ /// #(-5.0).signum() \ /// #(0.0).signum() \ - /// #calc.nan.signum() + /// #float.nan.signum() /// ``` #[func] pub fn signum(self) -> f64 { diff --git a/crates/typst/src/foundations/repr.rs b/crates/typst/src/foundations/repr.rs index 68c94c56d..9c098bd19 100644 --- a/crates/typst/src/foundations/repr.rs +++ b/crates/typst/src/foundations/repr.rs @@ -76,7 +76,7 @@ pub fn format_int_with_base(mut n: i64, base: i64) -> EcoString { /// unit, all with a single allocation. /// /// The returned string is always valid Typst code. As such, it might not be a -/// float literal. For example, it may return `"calc.inf"`. +/// float literal. For example, it may return `"float.inf"`. pub fn format_float( mut value: f64, precision: Option, @@ -91,10 +91,10 @@ pub fn format_float( // when necessary. let unit_multiplication = if unit.is_empty() { "" } else { " * 1" }; if value.is_nan() { - eco_format!("calc.nan{unit_multiplication}{unit}") + eco_format!("float.nan{unit_multiplication}{unit}") } else if value.is_infinite() { let sign = if value < 0.0 { "-" } else { "" }; - eco_format!("{sign}calc.inf{unit_multiplication}{unit}") + eco_format!("{sign}float.inf{unit_multiplication}{unit}") } else if force_separator { eco_format!("{value:?}{unit}") } else { diff --git a/tests/ref/float-repr.png b/tests/ref/float-repr.png index 7b49e9d15..e73ee3280 100644 Binary files a/tests/ref/float-repr.png and b/tests/ref/float-repr.png differ diff --git a/tests/ref/grid-rowspan-split-15.png b/tests/ref/grid-rowspan-split-15.png index bbefe0da0..bd435f3c0 100644 Binary files a/tests/ref/grid-rowspan-split-15.png and b/tests/ref/grid-rowspan-split-15.png differ diff --git a/tests/ref/ops-multiply-inf-with-length.png b/tests/ref/ops-multiply-inf-with-length.png index 0662d568b..b7c458a15 100644 Binary files a/tests/ref/ops-multiply-inf-with-length.png and b/tests/ref/ops-multiply-inf-with-length.png differ diff --git a/tests/suite/foundations/float.typ b/tests/suite/foundations/float.typ index 206013acd..01769260e 100644 --- a/tests/suite/foundations/float.typ +++ b/tests/suite/foundations/float.typ @@ -18,7 +18,7 @@ --- float-is-nan --- // Test float `is-nan()`. -#test(float(calc.nan).is-nan(), true) +#test(float(float.nan).is-nan(), true) #test(float(10).is-nan(), false) #test(float(calc.inf).is-nan(), false) #test(float(-calc.inf).is-nan(), false) @@ -29,7 +29,7 @@ #test(float(-calc.inf).is-infinite(), true) #test(float(10).is-infinite(), false) #test(float(-10).is-infinite(), false) -#test(float(calc.nan).is-infinite(), false) +#test(float(float.nan).is-infinite(), false) --- float-signum --- // Test float `signum()` @@ -40,7 +40,7 @@ #test(float(-10.0).signum(), -1.0) #test(float(calc.inf).signum(), 1.0) #test(float(-calc.inf).signum(), -1.0) -#test(float(calc.nan).signum().is-nan(), true) +#test(float(float.nan).signum().is-nan(), true) --- float-repr --- // Test the `repr` function with floats. @@ -55,9 +55,9 @@ #repr(-0987654321.0) \ #repr(-3.14) \ #repr(4.0 - 8.0) \ -#repr(calc.inf) \ -#repr(-calc.inf) \ -#repr(calc.nan) +#repr(float.inf) \ +#repr(-float.inf) \ +#repr(float.nan) --- float-display --- // Test floats. @@ -72,6 +72,6 @@ #(-0987654321.0) \ #(-3.14) \ #(4.0 - 8.0) \ -#calc.inf \ -#(-calc.inf) \ -#calc.nan +#float.inf \ +#(-float.inf) \ +#float.nan diff --git a/tests/suite/scripting/ops.typ b/tests/suite/scripting/ops.typ index 0df4b82da..1c9572173 100644 --- a/tests/suite/scripting/ops.typ +++ b/tests/suite/scripting/ops.typ @@ -335,7 +335,7 @@ #(1em <= 10pt) --- ops-compare-normal-float-with-nan --- -// Error: 3-22 cannot compare 2.2 with calc.nan +// Error: 3-22 cannot compare 2.2 with float.nan #(2.2 <= float("nan")) --- ops-compare-int-and-str --- @@ -343,7 +343,7 @@ #((0, 1, 3) > (0, 1, "a")) --- ops-compare-array-nested-failure --- -// Error: 3-42 cannot compare 3.5 with calc.nan +// Error: 3-42 cannot compare 3.5 with float.nan #((0, "a", 3.5) <= (0, "a", float("nan"))) --- ops-divide-by-zero-float ---