From fd417da04f7ca4b995de7f6510abafd3e9c31307 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 6 Jun 2023 21:13:59 +0200 Subject: Improve value casting infrastructure --- library/src/compute/calc.rs | 363 ++++++++++++++++--------------------- library/src/compute/construct.rs | 226 +++++++++++------------ library/src/compute/data.rs | 142 ++++++++------- library/src/compute/foundations.rs | 64 +++---- library/src/compute/mod.rs | 44 ++--- 5 files changed, 376 insertions(+), 463 deletions(-) (limited to 'library/src/compute') diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs index f485f817..0fc45891 100644 --- a/library/src/compute/calc.rs +++ b/library/src/compute/calc.rs @@ -11,38 +11,38 @@ use crate::prelude::*; /// A module with computational functions. pub fn module() -> Module { let mut scope = Scope::new(); - scope.define("abs", abs); - scope.define("pow", pow); - scope.define("sqrt", sqrt); - scope.define("sin", sin); - scope.define("cos", cos); - scope.define("tan", tan); - scope.define("asin", asin); - scope.define("acos", acos); - scope.define("atan", atan); - scope.define("atan2", atan2); - scope.define("sinh", sinh); - scope.define("cosh", cosh); - scope.define("tanh", tanh); - scope.define("log", log); - scope.define("fact", fact); - scope.define("perm", perm); - scope.define("binom", binom); - scope.define("gcd", gcd); - scope.define("lcm", lcm); - scope.define("floor", floor); - scope.define("ceil", ceil); - scope.define("trunc", trunc); - scope.define("fract", fract); - scope.define("round", round); - scope.define("clamp", clamp); - scope.define("min", min); - scope.define("max", max); - scope.define("even", even); - scope.define("odd", odd); - scope.define("rem", rem); - scope.define("mod", mod_); - scope.define("quo", quo); + scope.define("abs", abs_func()); + scope.define("pow", pow_func()); + scope.define("sqrt", sqrt_func()); + scope.define("sin", sin_func()); + scope.define("cos", cos_func()); + scope.define("tan", tan_func()); + scope.define("asin", asin_func()); + scope.define("acos", acos_func()); + scope.define("atan", atan_func()); + scope.define("atan2", atan2_func()); + scope.define("sinh", sinh_func()); + scope.define("cosh", cosh_func()); + scope.define("tanh", tanh_func()); + scope.define("log", log_func()); + scope.define("fact", fact_func()); + scope.define("perm", perm_func()); + scope.define("binom", binom_func()); + scope.define("gcd", gcd_func()); + scope.define("lcm", lcm_func()); + scope.define("floor", floor_func()); + scope.define("ceil", ceil_func()); + scope.define("trunc", trunc_func()); + scope.define("fract", fract_func()); + scope.define("round", round_func()); + scope.define("clamp", clamp_func()); + scope.define("min", min_func()); + scope.define("max", max_func()); + scope.define("even", even_func()); + scope.define("odd", odd_func()); + scope.define("rem", rem_func()); + scope.define("mod", mod_func()); + scope.define("quo", quo_func()); scope.define("inf", Value::Float(f64::INFINITY)); scope.define("nan", Value::Float(f64::NAN)); scope.define("pi", Value::Float(std::f64::consts::PI)); @@ -61,7 +61,6 @@ pub fn module() -> Module { /// /// Display: Absolute /// Category: calculate -/// Returns: any #[func] pub fn abs( /// The value whose absolute value to calculate. @@ -71,12 +70,12 @@ pub fn abs( } /// A value of which the absolute value can be taken. -struct ToAbs(Value); +pub struct ToAbs(Value); -cast_from_value! { +cast! { ToAbs, - v: i64 => Self(Value::Int(v.abs())), - v: f64 => Self(Value::Float(v.abs())), + v: i64 => Self(v.abs().into_value()), + v: f64 => Self(v.abs().into_value()), v: Length => Self(Value::Length(v.try_abs() .ok_or("cannot take absolute value of this length")?)), v: Angle => Self(Value::Angle(v.abs())), @@ -93,17 +92,18 @@ cast_from_value! { /// /// Display: Power /// Category: calculate -/// Returns: integer or float #[func] pub fn pow( /// The base of the power. base: Num, /// The exponent of the power. Must be non-negative. exponent: Spanned, -) -> Value { + /// The callsite span. + span: Span, +) -> SourceResult { match exponent.v { _ if exponent.v.float() == 0.0 && base.float() == 0.0 => { - bail!(args.span, "zero to the power of zero is undefined") + bail!(span, "zero to the power of zero is undefined") } Num::Int(i) if i32::try_from(i).is_err() => { bail!(exponent.span, "exponent is too large") @@ -119,16 +119,16 @@ pub fn pow( .checked_pow(b as u32) .map(Num::Int) .ok_or("the result is too large") - .at(args.span)?, + .at(span)?, (a, Num::Int(b)) => Num::Float(a.float().powi(b as i32)), (a, b) => Num::Float(a.float().powf(b.float())), }; if result.float().is_nan() { - bail!(args.span, "the result is not a real number") + bail!(span, "the result is not a real number") } - result.value() + Ok(result) } /// Calculate the square root of a number. @@ -141,16 +141,15 @@ pub fn pow( /// /// Display: Square Root /// Category: calculate -/// Returns: float #[func] pub fn sqrt( /// The number whose square root to calculate. Must be non-negative. value: Spanned, -) -> Value { +) -> SourceResult { if value.v.float() < 0.0 { bail!(value.span, "cannot take square root of negative number"); } - Value::Float(value.v.float().sqrt()) + Ok(value.v.float().sqrt()) } /// Calculate the sine of an angle. @@ -167,17 +166,16 @@ pub fn sqrt( /// /// Display: Sine /// Category: calculate -/// Returns: float #[func] pub fn sin( /// The angle whose sine to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.sin(), AngleLike::Int(n) => (n as f64).sin(), AngleLike::Float(n) => n.sin(), - }) + } } /// Calculate the cosine of an angle. @@ -194,17 +192,16 @@ pub fn sin( /// /// Display: Cosine /// Category: calculate -/// Returns: float #[func] pub fn cos( /// The angle whose cosine to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.cos(), AngleLike::Int(n) => (n as f64).cos(), AngleLike::Float(n) => n.cos(), - }) + } } /// Calculate the tangent of an angle. @@ -220,17 +217,16 @@ pub fn cos( /// /// Display: Tangent /// Category: calculate -/// Returns: float #[func] pub fn tan( /// The angle whose tangent to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.tan(), AngleLike::Int(n) => (n as f64).tan(), AngleLike::Float(n) => n.tan(), - }) + } } /// Calculate the arcsine of a number. @@ -243,17 +239,16 @@ pub fn tan( /// /// Display: Arcsine /// Category: calculate -/// Returns: angle #[func] pub fn asin( /// The number whose arcsine to calculate. Must be between -1 and 1. value: Spanned, -) -> Value { +) -> SourceResult { let val = value.v.float(); if val < -1.0 || val > 1.0 { bail!(value.span, "value must be between -1 and 1"); } - Value::Angle(Angle::rad(val.asin())) + Ok(Angle::rad(val.asin())) } /// Calculate the arccosine of a number. @@ -266,17 +261,16 @@ pub fn asin( /// /// Display: Arccosine /// Category: calculate -/// Returns: angle #[func] pub fn acos( /// The number whose arcsine to calculate. Must be between -1 and 1. value: Spanned, -) -> Value { +) -> SourceResult { let val = value.v.float(); if val < -1.0 || val > 1.0 { bail!(value.span, "value must be between -1 and 1"); } - Value::Angle(Angle::rad(val.acos())) + Ok(Angle::rad(val.acos())) } /// Calculate the arctangent of a number. @@ -289,13 +283,12 @@ pub fn acos( /// /// Display: Arctangent /// Category: calculate -/// Returns: angle #[func] pub fn atan( /// The number whose arctangent to calculate. value: Num, -) -> Value { - Value::Angle(Angle::rad(value.float().atan())) +) -> Angle { + Angle::rad(value.float().atan()) } /// Calculate the four-quadrant arctangent of a coordinate. @@ -310,15 +303,14 @@ pub fn atan( /// /// Display: Four-quadrant Arctangent /// Category: calculate -/// Returns: angle #[func] pub fn atan2( /// The X coordinate. x: Num, /// The Y coordinate. y: Num, -) -> Value { - Value::Angle(Angle::rad(f64::atan2(y.float(), x.float()))) +) -> Angle { + Angle::rad(f64::atan2(y.float(), x.float())) } /// Calculate the hyperbolic sine of an angle. @@ -333,17 +325,16 @@ pub fn atan2( /// /// Display: Hyperbolic sine /// Category: calculate -/// Returns: float #[func] pub fn sinh( /// The angle whose hyperbolic sine to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.to_rad().sinh(), AngleLike::Int(n) => (n as f64).sinh(), AngleLike::Float(n) => n.sinh(), - }) + } } /// Calculate the hyperbolic cosine of an angle. @@ -358,17 +349,16 @@ pub fn sinh( /// /// Display: Hyperbolic cosine /// Category: calculate -/// Returns: float #[func] pub fn cosh( /// The angle whose hyperbolic cosine to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.to_rad().cosh(), AngleLike::Int(n) => (n as f64).cosh(), AngleLike::Float(n) => n.cosh(), - }) + } } /// Calculate the hyperbolic tangent of an angle. @@ -383,17 +373,16 @@ pub fn cosh( /// /// Display: Hyperbolic tangent /// Category: calculate -/// Returns: float #[func] pub fn tanh( /// The angle whose hyperbolic tangent to calculate. angle: AngleLike, -) -> Value { - Value::Float(match angle { +) -> f64 { + match angle { AngleLike::Angle(a) => a.to_rad().tanh(), AngleLike::Int(n) => (n as f64).tanh(), AngleLike::Float(n) => n.tanh(), - }) + } } /// Calculate the logarithm of a number. @@ -407,7 +396,6 @@ pub fn tanh( /// /// Display: Logarithm /// Category: calculate -/// Returns: float #[func] pub fn log( /// The number whose logarithm to calculate. Must be strictly positive. @@ -416,7 +404,9 @@ pub fn log( #[named] #[default(Spanned::new(10.0, Span::detached()))] base: Spanned, -) -> Value { + /// The callsite span. + span: Span, +) -> SourceResult { let number = value.v.float(); if number <= 0.0 { bail!(value.span, "value must be strictly positive") @@ -435,10 +425,10 @@ pub fn log( }; if result.is_infinite() || result.is_nan() { - bail!(args.span, "the result is not a real number") + bail!(span, "the result is not a real number") } - Value::Float(result) + Ok(result) } /// Calculate the factorial of a number. @@ -450,33 +440,12 @@ pub fn log( /// /// Display: Factorial /// Category: calculate -/// Returns: integer #[func] pub fn fact( /// The number whose factorial to calculate. Must be non-negative. number: u64, -) -> Value { - factorial_range(1, number) - .map(Value::Int) - .ok_or("the result is too large") - .at(args.span)? -} - -/// Calculates the product of a range of numbers. Used to calculate -/// permutations. Returns None if the result is larger than `i64::MAX` -fn factorial_range(start: u64, end: u64) -> Option { - // By convention - if end + 1 < start { - return Some(0); - } - - let real_start: u64 = cmp::max(1, start); - let mut count: u64 = 1; - for i in real_start..=end { - count = count.checked_mul(i)?; - } - - i64::try_from(count).ok() +) -> StrResult { + Ok(fact_impl(1, number).ok_or("the result is too large")?) } /// Calculate a permutation. @@ -488,23 +457,36 @@ fn factorial_range(start: u64, end: u64) -> Option { /// /// Display: Permutation /// Category: calculate -/// Returns: integer #[func] pub fn perm( /// The base number. Must be non-negative. base: u64, /// The number of permutations. Must be non-negative. numbers: u64, -) -> Value { +) -> StrResult { // By convention. if base < numbers { - return Ok(Value::Int(0)); + return Ok(0); } - factorial_range(base - numbers + 1, base) - .map(Value::Int) - .ok_or("the result is too large") - .at(args.span)? + Ok(fact_impl(base - numbers + 1, base).ok_or("the result is too large")?) +} + +/// Calculates the product of a range of numbers. Used to calculate +/// permutations. Returns None if the result is larger than `i64::MAX` +fn fact_impl(start: u64, end: u64) -> Option { + // By convention + if end + 1 < start { + return Some(0); + } + + let real_start: u64 = cmp::max(1, start); + let mut count: u64 = 1; + for i in real_start..=end { + count = count.checked_mul(i)?; + } + + count.try_into().ok() } /// Calculate a binomial coefficient. @@ -516,24 +498,20 @@ pub fn perm( /// /// Display: Binomial /// Category: calculate -/// Returns: integer #[func] pub fn binom( /// The upper coefficient. Must be non-negative. n: u64, /// The lower coefficient. Must be non-negative. k: u64, -) -> Value { - binomial(n, k) - .map(Value::Int) - .ok_or("the result is too large") - .at(args.span)? +) -> StrResult { + Ok(binom_impl(n, k).ok_or("the result is too large")?) } /// Calculates a binomial coefficient, with `n` the upper coefficient and `k` /// the lower coefficient. Returns `None` if the result is larger than /// `i64::MAX` -fn binomial(n: u64, k: u64) -> Option { +fn binom_impl(n: u64, k: u64) -> Option { if k > n { return Some(0); } @@ -549,7 +527,7 @@ fn binomial(n: u64, k: u64) -> Option { result = result.checked_mul(n - i)?.checked_div(i + 1)?; } - i64::try_from(result).ok() + result.try_into().ok() } /// Calculate the greatest common divisor of two integers. @@ -561,20 +539,14 @@ fn binomial(n: u64, k: u64) -> Option { /// /// Display: Greatest Common Divisor /// Category: calculate -/// Returns: integer #[func] pub fn gcd( /// The first integer. a: i64, /// The second integer. b: i64, -) -> Value { - Value::Int(calculate_gcd(a, b)) -} - -/// Calculates the greatest common divisor of two integers -/// It is always non-negative. -fn calculate_gcd(mut a: i64, mut b: i64) -> i64 { +) -> i64 { + let (mut a, mut b) = (a, b); while b != 0 { let temp = b; b = a % b; @@ -593,29 +565,21 @@ fn calculate_gcd(mut a: i64, mut b: i64) -> i64 { /// /// Display: Least Common Multiple /// Category: calculate -/// Returns: integer #[func] pub fn lcm( /// The first integer. a: i64, /// The second integer. b: i64, -) -> Value { - calculate_lcm(a, b) - .map(Value::Int) - .ok_or("the return value is too large") - .at(args.span)? -} - -/// Calculates the least common multiple between two non-zero integers -/// Returns None if the value cannot be computed. -/// It is always non-negative. -fn calculate_lcm(a: i64, b: i64) -> Option { +) -> StrResult { if a == b { - return Some(a.abs()); + return Ok(a.abs()); } - a.checked_div(calculate_gcd(a, b))?.checked_mul(b).map(|v| v.abs()) + Ok(a.checked_div(gcd(a, b)) + .and_then(|gcd| gcd.checked_mul(b)) + .map(|v| v.abs()) + .ok_or("the return value is too large")?) } /// Round a number down to the nearest integer. @@ -631,15 +595,14 @@ fn calculate_lcm(a: i64, b: i64) -> Option { /// /// Display: Round down /// Category: calculate -/// Returns: integer #[func] pub fn floor( /// The number to round down. value: Num, -) -> Value { +) -> i64 { match value { - Num::Int(n) => Value::Int(n), - Num::Float(n) => Value::Int(n.floor() as i64), + Num::Int(n) => n, + Num::Float(n) => n.floor() as i64, } } @@ -656,15 +619,14 @@ pub fn floor( /// /// Display: Round up /// Category: calculate -/// Returns: integer #[func] pub fn ceil( /// The number to round up. value: Num, -) -> Value { +) -> i64 { match value { - Num::Int(n) => Value::Int(n), - Num::Float(n) => Value::Int(n.ceil() as i64), + Num::Int(n) => n, + Num::Float(n) => n.ceil() as i64, } } @@ -681,16 +643,15 @@ pub fn ceil( /// /// Display: Truncate /// Category: calculate -/// Returns: integer #[func] pub fn trunc( /// The number to truncate. value: Num, -) -> Value { - Value::Int(match value { +) -> i64 { + match value { Num::Int(n) => n, Num::Float(n) => n.trunc() as i64, - }) + } } /// Returns the fractional part of a number. @@ -705,15 +666,14 @@ pub fn trunc( /// /// Display: Fractional /// Category: calculate -/// Returns: integer or float #[func] pub fn fract( /// The number to truncate. value: Num, -) -> Value { +) -> Num { match value { - Num::Int(_) => Value::Int(0), - Num::Float(n) => Value::Float(n.fract()), + Num::Int(_) => Num::Int(0), + Num::Float(n) => Num::Float(n.fract()), } } @@ -730,7 +690,6 @@ pub fn fract( /// /// Display: Round /// Category: calculate -/// Returns: integer or float #[func] pub fn round( /// The number to round. @@ -739,13 +698,13 @@ pub fn round( #[named] #[default(0)] digits: i64, -) -> Value { +) -> Num { match value { - Num::Int(n) if digits == 0 => Value::Int(n), + Num::Int(n) if digits == 0 => Num::Int(n), _ => { let n = value.float(); let factor = 10.0_f64.powi(digits as i32); - Value::Float((n * factor).round() / factor) + Num::Float((n * factor).round() / factor) } } } @@ -761,7 +720,6 @@ pub fn round( /// /// Display: Clamp /// Category: calculate -/// Returns: integer or float #[func] pub fn clamp( /// The number to clamp. @@ -770,11 +728,11 @@ pub fn clamp( min: Num, /// The inclusive maximum value. max: Spanned, -) -> Value { +) -> SourceResult { if max.v.float() < min.float() { bail!(max.span, "max must be greater than or equal to min") } - value.apply3(min, max.v, i64::clamp, f64::clamp).value() + Ok(value.apply3(min, max.v, i64::clamp, f64::clamp)) } /// Determine the minimum of a sequence of values. @@ -787,15 +745,16 @@ pub fn clamp( /// /// Display: Minimum /// Category: calculate -/// Returns: any #[func] pub fn min( /// The sequence of values from which to extract the minimum. /// Must not be empty. #[variadic] values: Vec>, -) -> Value { - minmax(args.span, values, Ordering::Less)? + /// The callsite span. + span: Span, +) -> SourceResult { + minmax(span, values, Ordering::Less) } /// Determine the maximum of a sequence of values. @@ -808,15 +767,16 @@ pub fn min( /// /// Display: Maximum /// Category: calculate -/// Returns: any #[func] pub fn max( /// The sequence of values from which to extract the maximum. /// Must not be empty. #[variadic] values: Vec>, -) -> Value { - minmax(args.span, values, Ordering::Greater)? + /// The callsite span. + span: Span, +) -> SourceResult { + minmax(span, values, Ordering::Greater) } /// Find the minimum or maximum of a sequence of values. @@ -851,13 +811,12 @@ fn minmax( /// /// Display: Even /// Category: calculate -/// Returns: boolean #[func] pub fn even( /// The number to check for evenness. value: i64, -) -> Value { - Value::Bool(value % 2 == 0) +) -> bool { + value % 2 == 0 } /// Determine whether an integer is odd. @@ -871,13 +830,12 @@ pub fn even( /// /// Display: Odd /// Category: calculate -/// Returns: boolean #[func] pub fn odd( /// The number to check for oddness. value: i64, -) -> Value { - Value::Bool(value % 2 != 0) +) -> bool { + value % 2 != 0 } /// Calculate the remainder of two numbers. @@ -890,18 +848,17 @@ pub fn odd( /// /// Display: Remainder /// Category: calculate -/// Returns: integer or float #[func] pub fn rem( /// The dividend of the remainder. dividend: Num, /// The divisor of the remainder. divisor: Spanned, -) -> Value { +) -> SourceResult { if divisor.v.float() == 0.0 { bail!(divisor.span, "divisor must not be zero"); } - dividend.apply2(divisor.v, Rem::rem, Rem::rem).value() + Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem)) } /// Calculate the modulus of two numbers. (Deprecated) @@ -911,18 +868,17 @@ pub fn rem( /// /// Display: Modulus /// Category: calculate -/// Returns: integer or float #[func] pub fn mod_( /// The dividend of the remainder. dividend: Num, /// The divisor of the remainder. divisor: Spanned, -) -> Value { +) -> SourceResult { if divisor.v.float() == 0.0 { bail!(divisor.span, "divisor must not be zero"); } - dividend.apply2(divisor.v, Rem::rem, Rem::rem).value() + Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem)) } /// Calculate the quotient of two numbers. @@ -935,33 +891,29 @@ pub fn mod_( /// /// Display: Quotient /// Category: calculate -/// Returns: integer or float #[func] pub fn quo( /// The dividend of the quotient. dividend: Num, /// The divisor of the quotient. divisor: Spanned, -) -> Value { +) -> SourceResult { if divisor.v.float() == 0.0 { bail!(divisor.span, "divisor must not be zero"); } - Value::Int(match dividend.apply2(divisor.v, Div::div, Div::div) { - Num::Int(i) => i, - Num::Float(f) => f.floor() as i64, // Note: the result should be an integer but floats doesn't have the same precision as i64. - }) + Ok(floor(dividend.apply2(divisor.v, Div::div, Div::div))) } /// A value which can be passed to functions that work with integers and floats. #[derive(Debug, Copy, Clone)] -enum Num { +pub enum Num { Int(i64), Float(f64), } impl Num { - fn apply2( + pub fn apply2( self, other: Self, int: impl FnOnce(i64, i64) -> i64, @@ -973,7 +925,7 @@ impl Num { } } - fn apply3( + pub fn apply3( self, other: Self, third: Self, @@ -986,35 +938,32 @@ impl Num { } } - fn float(self) -> f64 { + pub fn float(self) -> f64 { match self { Self::Int(v) => v as f64, Self::Float(v) => v, } } - - fn value(self) -> Value { - match self { - Self::Int(v) => Value::Int(v), - Self::Float(v) => Value::Float(v), - } - } } -cast_from_value! { +cast! { Num, + self => match self { + Self::Int(v) => v.into_value(), + Self::Float(v) => v.into_value(), + }, v: i64 => Self::Int(v), v: f64 => Self::Float(v), } /// A value that can be passed to a trigonometric function. -enum AngleLike { +pub enum AngleLike { Int(i64), Float(f64), Angle(Angle), } -cast_from_value! { +cast! { AngleLike, v: i64 => Self::Int(v), v: f64 => Self::Float(v), diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs index d11eb398..5d2d35ae 100644 --- a/library/src/compute/construct.rs +++ b/library/src/compute/construct.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use time::{Month, PrimitiveDateTime}; -use typst::eval::{Datetime, Dynamic, Regex}; +use typst::eval::{Datetime, Regex}; use crate::prelude::*; @@ -23,19 +23,18 @@ use crate::prelude::*; /// /// Display: Integer /// Category: construct -/// Returns: integer #[func] pub fn int( /// The value that should be converted to an integer. value: ToInt, -) -> Value { - Value::Int(value.0) +) -> i64 { + value.0 } /// A value that can be cast to an integer. -struct ToInt(i64); +pub struct ToInt(i64); -cast_from_value! { +cast! { ToInt, v: bool => Self(v as i64), v: i64 => Self(v), @@ -63,19 +62,18 @@ cast_from_value! { /// /// Display: Float /// Category: construct -/// Returns: float #[func] pub fn float( /// The value that should be converted to a float. value: ToFloat, -) -> Value { - Value::Float(value.0) +) -> f64 { + value.0 } /// A value that can be cast to a float. -struct ToFloat(f64); +pub struct ToFloat(f64); -cast_from_value! { +cast! { ToFloat, v: bool => Self(v as i64 as f64), v: i64 => Self(v as f64), @@ -95,13 +93,12 @@ cast_from_value! { /// /// Display: Luma /// Category: construct -/// Returns: color #[func] pub fn luma( /// The gray component. gray: Component, -) -> Value { - Value::Color(LumaColor::new(gray.0).into()) +) -> Color { + LumaColor::new(gray.0).into() } /// Create an RGB(A) color. @@ -121,7 +118,6 @@ pub fn luma( /// /// Display: RGB /// Category: construct -/// Returns: color #[func] pub fn rgb( /// The color in hexadecimal notation. @@ -150,11 +146,14 @@ pub fn rgb( /// The alpha component. #[external] alpha: Component, -) -> Value { - Value::Color(if let Some(string) = args.find::>()? { + /// The arguments. + args: Args, +) -> SourceResult { + let mut args = args; + Ok(if let Some(string) = args.find::>()? { match RgbaColor::from_str(&string.v) { Ok(color) => color.into(), - Err(msg) => bail!(string.span, msg), + Err(msg) => bail!(string.span, "{msg}"), } } else { let Component(r) = args.expect("red component")?; @@ -166,9 +165,9 @@ pub fn rgb( } /// An integer or ratio component. -struct Component(u8); +pub struct Component(u8); -cast_from_value! { +cast! { Component, v: i64 => match v { 0 ..= 255 => Self(v as u8), @@ -208,10 +207,9 @@ cast_from_value! { /// /// Display: Datetime /// Category: construct -/// Returns: datetime #[func] #[scope( - scope.define("today", datetime_today); + scope.define("today", datetime_today_func()); scope )] pub fn datetime( @@ -233,95 +231,77 @@ pub fn datetime( /// The second of the datetime. #[named] second: Option, -) -> Value { +) -> StrResult { let time = match (hour, minute, second) { (Some(hour), Some(minute), Some(second)) => { match time::Time::from_hms(hour.0, minute.0, second.0) { Ok(time) => Some(time), - Err(_) => bail!(args.span, "time is invalid"), + Err(_) => bail!("time is invalid"), } } (None, None, None) => None, - _ => bail!(args.span, "time is incomplete"), + _ => bail!("time is incomplete"), }; let date = match (year, month, day) { (Some(year), Some(month), Some(day)) => { match time::Date::from_calendar_date(year.0, month.0, day.0) { Ok(date) => Some(date), - Err(_) => bail!(args.span, "date is invalid"), + Err(_) => bail!("date is invalid"), } } (None, None, None) => None, - _ => bail!(args.span, "date is incomplete"), + _ => bail!("date is incomplete"), }; - match (date, time) { - (Some(date), Some(time)) => Value::Dyn(Dynamic::new(Datetime::Datetime( - PrimitiveDateTime::new(date, time), - ))), - (Some(date), None) => Value::Dyn(Dynamic::new(Datetime::Date(date))), - (None, Some(time)) => Value::Dyn(Dynamic::new(Datetime::Time(time))), + Ok(match (date, time) { + (Some(date), Some(time)) => { + Datetime::Datetime(PrimitiveDateTime::new(date, time)) + } + (Some(date), None) => Datetime::Date(date), + (None, Some(time)) => Datetime::Time(time), (None, None) => { - bail!(args.span, "at least one of date or time must be fully specified") + bail!("at least one of date or time must be fully specified") } - } + }) } -struct YearComponent(i32); -struct MonthComponent(Month); -struct DayComponent(u8); -struct HourComponent(u8); -struct MinuteComponent(u8); -struct SecondComponent(u8); +pub struct YearComponent(i32); +pub struct MonthComponent(Month); +pub struct DayComponent(u8); +pub struct HourComponent(u8); +pub struct MinuteComponent(u8); +pub struct SecondComponent(u8); -cast_from_value!( +cast! { YearComponent, - v: i64 => match i32::try_from(v) { - Ok(n) => Self(n), - _ => Err("year is invalid")? - } -); + v: i32 => Self(v), +} -cast_from_value!( +cast! { MonthComponent, - v: i64 => match u8::try_from(v).ok().and_then(|n1| Month::try_from(n1).ok()).map(Self) { - Some(m) => m, - _ => Err("month is invalid")? - } -); + v: u8 => Self(Month::try_from(v).map_err(|_| "month is invalid")?) +} -cast_from_value!( +cast! { DayComponent, - v: i64 => match u8::try_from(v) { - Ok(n) => Self(n), - _ => Err("day is invalid")? - } -); + v: u8 => Self(v), +} -cast_from_value!( +cast! { HourComponent, - v: i64 => match u8::try_from(v) { - Ok(n) => Self(n), - _ => Err("hour is invalid")? - } -); + v: u8 => Self(v), +} -cast_from_value!( +cast! { MinuteComponent, - v: i64 => match u8::try_from(v) { - Ok(n) => Self(n), - _ => Err("minute is invalid")? - } -); + v: u8 => Self(v), +} -cast_from_value!( +cast! { SecondComponent, - v: i64 => match u8::try_from(v) { - Ok(n) => Self(n), - _ => Err("second is invalid")? - } -); + v: u8 => Self(v), +} /// Returns the current date. /// @@ -333,7 +313,6 @@ cast_from_value!( /// /// Display: Today /// Category: construct -/// Returns: datetime #[func] pub fn datetime_today( /// An offset to apply to the current UTC date. If set to `{auto}`, the @@ -341,13 +320,13 @@ pub fn datetime_today( #[named] #[default] offset: Smart, -) -> Value { - let current_date = match vm.vt.world.today(offset.as_custom()) { - Some(d) => d, - None => bail!(args.span, "unable to get the current date"), - }; - - Value::Dyn(Dynamic::new(current_date)) + /// The virtual machine. + vt: &mut Vt, +) -> StrResult { + Ok(vt + .world + .today(offset.as_custom()) + .ok_or("unable to get the current date")?) } /// Create a CMYK color. @@ -365,7 +344,6 @@ pub fn datetime_today( /// /// Display: CMYK /// Category: construct -/// Returns: color #[func] pub fn cmyk( /// The cyan component. @@ -376,14 +354,14 @@ pub fn cmyk( yellow: RatioComponent, /// The key component. key: RatioComponent, -) -> Value { - Value::Color(CmykColor::new(cyan.0, magenta.0, yellow.0, key.0).into()) +) -> Color { + CmykColor::new(cyan.0, magenta.0, yellow.0, key.0).into() } /// A component that must be a ratio. -struct RatioComponent(u8); +pub struct RatioComponent(u8); -cast_from_value! { +cast! { RatioComponent, v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) { Self((v.get() * 255.0).round() as u8) @@ -413,7 +391,6 @@ cast_from_value! { /// /// Display: Symbol /// Category: construct -/// Returns: symbol #[func] pub fn symbol( /// The variants of the symbol. @@ -425,10 +402,12 @@ pub fn symbol( /// all attached modifiers and the minimum number of other modifiers. #[variadic] variants: Vec>, -) -> Value { + /// The callsite span. + span: Span, +) -> SourceResult { let mut list = Vec::new(); if variants.is_empty() { - bail!(args.span, "expected at least one variant"); + bail!(span, "expected at least one variant"); } for Spanned { v, span } in variants { if list.iter().any(|(prev, _)| &v.0 == prev) { @@ -436,13 +415,13 @@ pub fn symbol( } list.push((v.0, v.1)); } - Value::Symbol(Symbol::runtime(list.into_boxed_slice())) + Ok(Symbol::runtime(list.into_boxed_slice())) } /// A value that can be cast to a symbol. -struct Variant(EcoString, char); +pub struct Variant(EcoString, char); -cast_from_value! { +cast! { Variant, c: char => Self(EcoString::new(), c), array: Array => { @@ -476,11 +455,10 @@ cast_from_value! { /// /// Display: String /// Category: construct -/// Returns: string #[func] #[scope( - scope.define("to-unicode", to_unicode); - scope.define("from-unicode", from_unicode); + scope.define("to-unicode", str_to_unicode_func()); + scope.define("from-unicode", str_from_unicode_func()); scope )] pub fn str( @@ -490,13 +468,13 @@ pub fn str( #[named] #[default(Spanned::new(10, Span::detached()))] base: Spanned, -) -> Value { - match value { +) -> SourceResult { + Ok(match value { ToStr::Str(s) => { if base.v != 10 { bail!(base.span, "base is only supported for integers"); } - Value::Str(s) + s } ToStr::Int(n) => { if base.v < 2 || base.v > 36 { @@ -504,18 +482,18 @@ pub fn str( } int_to_base(n, base.v).into() } - } + }) } /// A value that can be cast to a string. -enum ToStr { +pub enum ToStr { /// A string value ready to be used as-is. Str(Str), /// An integer about to be formatted in a given base. Int(i64), } -cast_from_value! { +cast! { ToStr, v: i64 => Self::Int(v), v: f64 => Self::Str(format_str!("{}", v)), @@ -572,16 +550,14 @@ fn int_to_base(mut n: i64, base: i64) -> EcoString { /// /// Display: String To Unicode /// Category: construct -/// Returns: int #[func] -pub fn to_unicode( +pub fn str_to_unicode( /// The character that should be converted. value: char, -) -> Value { - Value::Int(From::::from(value.into())) +) -> u32 { + value.into() } -#[func] /// Converts a unicode code point into its corresponding string. /// /// ```example @@ -590,18 +566,18 @@ pub fn to_unicode( /// /// Display: Sting From Unicode /// Category: construct -/// Returns: string -pub fn from_unicode( +#[func] +pub fn str_from_unicode( /// The code point that should be converted. value: CodePoint, -) -> Value { - Value::Str(format_str!("{}", value.0)) +) -> Str { + format_str!("{}", value.0) } /// The numeric representation of a single unicode code point. -struct CodePoint(char); +pub struct CodePoint(char); -cast_from_value! { +cast! { CodePoint, v: i64 => { Self(v.try_into().ok().and_then(|v: u32| v.try_into().ok()).ok_or_else( @@ -631,13 +607,12 @@ cast_from_value! { /// /// Display: Label /// Category: construct -/// Returns: label #[func] pub fn label( /// The name of the label. name: EcoString, -) -> Value { - Value::Label(Label(name)) +) -> Label { + Label(name) } /// Create a regular expression from a string. @@ -663,7 +638,6 @@ pub fn label( /// /// Display: Regex /// Category: construct -/// Returns: regex #[func] pub fn regex( /// The regular expression as a string. @@ -677,8 +651,8 @@ pub fn regex( /// and extract its text to use it for your regular expressions: /// ```{regex(`\d+\.\d+\.\d+`.text)}```. regex: Spanned, -) -> Value { - Regex::new(®ex.v).at(regex.span)?.into() +) -> SourceResult { + Regex::new(®ex.v).at(regex.span) } /// Create an array consisting of a sequence of numbers. @@ -698,7 +672,6 @@ pub fn regex( /// /// Display: Range /// Category: construct -/// Returns: array #[func] pub fn range( /// The start of the range (inclusive). @@ -712,7 +685,10 @@ pub fn range( #[named] #[default(NonZeroI64::new(1).unwrap())] step: NonZeroI64, -) -> Value { + /// The arguments. + args: Args, +) -> SourceResult { + let mut args = args; let first = args.expect::("end")?; let (start, end) = match args.eat::()? { Some(second) => (first, second), @@ -729,7 +705,7 @@ pub fn range( x += step; } - Value::Array(array) + Ok(array) } #[cfg(test)] diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs index e3918458..b236536d 100644 --- a/library/src/compute/data.rs +++ b/library/src/compute/data.rs @@ -14,21 +14,22 @@ use crate::prelude::*; /// #raw(text, lang: "html") /// ``` /// -/// Display: Plain text +/// Display: Read /// Category: data-loading -/// Returns: string #[func] pub fn read( /// Path to a file. path: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; let text = std::str::from_utf8(&data) .map_err(|_| "file is not valid utf-8") .at(span)?; - Value::Str(text.into()) + Ok(text.into()) } /// Read structured data from a CSV file. @@ -51,7 +52,6 @@ pub fn read( /// /// Display: CSV /// Category: data-loading -/// Returns: array #[func] pub fn csv( /// Path to a CSV file. @@ -61,7 +61,9 @@ pub fn csv( #[named] #[default] delimiter: Delimiter, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; @@ -74,22 +76,30 @@ pub fn csv( let mut array = Array::new(); for (line, result) in reader.records().enumerate() { - // Original solution use line from error, but that is incorrect with has_headers set to false - // See issue: https://github.com/BurntSushi/rust-csv/issues/184 + // Original solution use line from error, but that is incorrect with + // `has_headers` set to `false`. See issue: + // https://github.com/BurntSushi/rust-csv/issues/184 let line = line + 1; // Counting lines from 1 let row = result.map_err(|err| format_csv_error(err, line)).at(span)?; - let sub = row.into_iter().map(|field| Value::Str(field.into())).collect(); + let sub = row.into_iter().map(|field| field.into_value()).collect(); array.push(Value::Array(sub)) } - Value::Array(array) + Ok(array) } /// The delimiter to use when parsing CSV files. -struct Delimiter(char); +pub struct Delimiter(char); -cast_from_value! { +impl Default for Delimiter { + fn default() -> Self { + Self(',') + } +} + +cast! { Delimiter, + self => self.0.into_value(), v: EcoString => { let mut chars = v.chars(); let first = chars.next().ok_or("delimiter must not be empty")?; @@ -105,16 +115,6 @@ cast_from_value! { }, } -cast_to_value! { - v: Delimiter => v.0.into() -} - -impl Default for Delimiter { - fn default() -> Self { - Self(',') - } -} - /// Format the user-facing CSV error message. fn format_csv_error(error: csv::Error, line: usize) -> EcoString { match error.kind() { @@ -168,38 +168,39 @@ fn format_csv_error(error: csv::Error, line: usize) -> EcoString { /// /// Display: JSON /// Category: data-loading -/// Returns: array or dictionary #[func] pub fn json( /// Path to a JSON file. path: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; let value: serde_json::Value = serde_json::from_slice(&data).map_err(format_json_error).at(span)?; - convert_json(value) + Ok(convert_json(value)) } /// Convert a JSON value to a Typst value. fn convert_json(value: serde_json::Value) -> Value { match value { serde_json::Value::Null => Value::None, - serde_json::Value::Bool(v) => Value::Bool(v), + serde_json::Value::Bool(v) => v.into_value(), serde_json::Value::Number(v) => match v.as_i64() { - Some(int) => Value::Int(int), - None => Value::Float(v.as_f64().unwrap_or(f64::NAN)), + Some(int) => int.into_value(), + None => v.as_f64().unwrap_or(f64::NAN).into_value(), }, - serde_json::Value::String(v) => Value::Str(v.into()), + serde_json::Value::String(v) => v.into_value(), serde_json::Value::Array(v) => { - Value::Array(v.into_iter().map(convert_json).collect()) + v.into_iter().map(convert_json).collect::().into_value() } - serde_json::Value::Object(v) => Value::Dict( - v.into_iter() - .map(|(key, value)| (key.into(), convert_json(value))) - .collect(), - ), + serde_json::Value::Object(v) => v + .into_iter() + .map(|(key, value)| (key.into(), convert_json(value))) + .collect::() + .into_value(), } } @@ -233,12 +234,13 @@ fn format_json_error(error: serde_json::Error) -> EcoString { /// /// Display: TOML /// Category: data-loading -/// Returns: dictionary #[func] pub fn toml( /// Path to a TOML file. path: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; @@ -248,24 +250,26 @@ pub fn toml( .at(span)?; let value: toml::Value = toml::from_str(raw).map_err(format_toml_error).at(span)?; - convert_toml(value) + Ok(convert_toml(value)) } /// Convert a TOML value to a Typst value. fn convert_toml(value: toml::Value) -> Value { match value { - toml::Value::String(v) => Value::Str(v.into()), - toml::Value::Integer(v) => Value::Int(v), - toml::Value::Float(v) => Value::Float(v), - toml::Value::Boolean(v) => Value::Bool(v), - toml::Value::Array(v) => Value::Array(v.into_iter().map(convert_toml).collect()), - toml::Value::Table(v) => Value::Dict( - v.into_iter() - .map(|(key, value)| (key.into(), convert_toml(value))) - .collect(), - ), + toml::Value::String(v) => v.into_value(), + toml::Value::Integer(v) => v.into_value(), + toml::Value::Float(v) => v.into_value(), + toml::Value::Boolean(v) => v.into_value(), + toml::Value::Array(v) => { + v.into_iter().map(convert_toml).collect::().into_value() + } + toml::Value::Table(v) => v + .into_iter() + .map(|(key, value)| (key.into(), convert_toml(value))) + .collect::() + .into_value(), // TODO: Make it use native date/time object(s) once it is implemented. - toml::Value::Datetime(v) => Value::Str(v.to_string().into()), + toml::Value::Datetime(v) => v.to_string().into_value(), } } @@ -323,39 +327,40 @@ fn format_toml_error(error: toml::de::Error) -> EcoString { /// /// Display: YAML /// Category: data-loading -/// Returns: array or dictionary or none or boolean or integer or float or string #[func] pub fn yaml( /// Path to a YAML file. path: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; let value: serde_yaml::Value = serde_yaml::from_slice(&data).map_err(format_yaml_error).at(span)?; - convert_yaml(value) + Ok(convert_yaml(value)) } /// Convert a YAML value to a Typst value. fn convert_yaml(value: serde_yaml::Value) -> Value { match value { serde_yaml::Value::Null => Value::None, - serde_yaml::Value::Bool(v) => Value::Bool(v), + serde_yaml::Value::Bool(v) => v.into_value(), serde_yaml::Value::Number(v) => match v.as_i64() { - Some(int) => Value::Int(int), - None => Value::Float(v.as_f64().unwrap_or(f64::NAN)), + Some(int) => int.into_value(), + None => v.as_f64().unwrap_or(f64::NAN).into_value(), }, - serde_yaml::Value::String(v) => Value::Str(v.into()), + serde_yaml::Value::String(v) => v.into_value(), serde_yaml::Value::Sequence(v) => { - Value::Array(v.into_iter().map(convert_yaml).collect()) + v.into_iter().map(convert_yaml).collect::().into_value() } - serde_yaml::Value::Mapping(v) => Value::Dict( - v.into_iter() - .map(|(key, value)| (convert_yaml_key(key), convert_yaml(value))) - .filter_map(|(key, value)| key.map(|key| (key, value))) - .collect(), - ), + serde_yaml::Value::Mapping(v) => v + .into_iter() + .map(|(key, value)| (convert_yaml_key(key), convert_yaml(value))) + .filter_map(|(key, value)| key.map(|key| (key, value))) + .collect::() + .into_value(), } } @@ -425,24 +430,25 @@ fn format_yaml_error(error: serde_yaml::Error) -> EcoString { /// /// Display: XML /// Category: data-loading -/// Returns: array #[func] pub fn xml( /// Path to an XML file. path: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: path, span } = path; let path = vm.locate(&path).at(span)?; let data = vm.world().file(&path).at(span)?; let text = std::str::from_utf8(&data).map_err(FileError::from).at(span)?; let document = roxmltree::Document::parse(text).map_err(format_xml_error).at(span)?; - convert_xml(document.root()) + Ok(convert_xml(document.root())) } /// Convert an XML node to a Typst value. fn convert_xml(node: roxmltree::Node) -> Value { if node.is_text() { - return Value::Str(node.text().unwrap_or_default().into()); + return node.text().unwrap_or_default().into_value(); } let children: Array = node.children().map(convert_xml).collect(); @@ -453,7 +459,7 @@ fn convert_xml(node: roxmltree::Node) -> Value { let tag: Str = node.tag_name().name().into(); let attrs: Dict = node .attributes() - .map(|attr| (attr.name().into(), attr.value().into())) + .map(|attr| (attr.name().into(), attr.value().into_value())) .collect(); Value::Dict(dict! { diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs index 6a92c2a8..7964ac45 100644 --- a/library/src/compute/foundations.rs +++ b/library/src/compute/foundations.rs @@ -16,12 +16,11 @@ use crate::prelude::*; /// /// Display: Type /// Category: foundations -/// Returns: string #[func] pub fn type_( /// The value whose type's to determine. value: Value, -) -> Value { +) -> Str { value.type_name().into() } @@ -44,13 +43,12 @@ pub fn type_( /// /// Display: Representation /// Category: foundations -/// Returns: string #[func] pub fn repr( /// The value whose string representation to produce. value: Value, -) -> Value { - value.repr().into() +) -> Str { + value.repr() } /// Fail with an error. @@ -63,13 +61,12 @@ pub fn repr( /// /// Display: Panic /// Category: foundations -/// Returns: #[func] pub fn panic( /// The values to panic with. #[variadic] values: Vec, -) -> Value { +) -> StrResult { let mut msg = EcoString::from("panicked"); if !values.is_empty() { msg.push_str(" with: "); @@ -80,7 +77,7 @@ pub fn panic( msg.push_str(&value.repr()); } } - bail!(args.span, msg); + Err(msg) } /// Ensure that a condition is fulfilled. @@ -98,11 +95,10 @@ pub fn panic( /// /// Display: Assert /// Category: foundations -/// Returns: #[func] #[scope( - scope.define("eq", assert_eq); - scope.define("ne", assert_ne); + scope.define("eq", assert_eq_func()); + scope.define("ne", assert_ne_func()); scope )] pub fn assert( @@ -110,17 +106,16 @@ pub fn assert( condition: bool, /// The error message when the assertion fails. #[named] - #[default] message: Option, -) -> Value { +) -> StrResult { if !condition { if let Some(message) = message { - bail!(args.span, "assertion failed: {}", message); + bail!("assertion failed: {message}"); } else { - bail!(args.span, "assertion failed"); + bail!("assertion failed"); } } - Value::None + Ok(NoneValue) } /// Ensure that two values are equal. @@ -135,7 +130,6 @@ pub fn assert( /// /// Display: Assert Equals /// Category: foundations -/// Returns: #[func] pub fn assert_eq( /// The first value to compare. @@ -147,22 +141,16 @@ pub fn assert_eq( /// An optional message to display on error instead of the representations /// of the compared values. #[named] - #[default] message: Option, -) -> Value { +) -> StrResult { if left != right { if let Some(message) = message { - bail!(args.span, "equality assertion failed: {}", message); + bail!("equality assertion failed: {message}"); } else { - bail!( - args.span, - "equality assertion failed: value {:?} was not equal to {:?}", - left, - right - ); + bail!("equality assertion failed: value {left:?} was not equal to {right:?}"); } } - Value::None + Ok(NoneValue) } /// Ensure that two values are not equal. @@ -177,7 +165,6 @@ pub fn assert_eq( /// /// Display: Assert Not Equals /// Category: foundations -/// Returns: #[func] pub fn assert_ne( /// The first value to compare. @@ -189,22 +176,16 @@ pub fn assert_ne( /// An optional message to display on error instead of the representations /// of the compared values. #[named] - #[default] message: Option, -) -> Value { +) -> StrResult { if left == right { if let Some(message) = message { - bail!(args.span, "inequality assertion failed: {}", message); + bail!("inequality assertion failed: {message}"); } else { - bail!( - args.span, - "inequality assertion failed: value {:?} was equal to {:?}", - left, - right - ); + bail!("inequality assertion failed: value {left:?} was equal to {right:?}"); } } - Value::None + Ok(NoneValue) } /// Evaluate a string as Typst code. @@ -220,14 +201,15 @@ pub fn assert_ne( /// /// Display: Evaluate /// Category: foundations -/// Returns: any #[func] pub fn eval( /// A string of Typst code to evaluate. /// /// The code in the string cannot interact with the file system. source: Spanned, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult { let Spanned { v: text, span } = source; - typst::eval::eval_string(vm.world(), &text, span)? + typst::eval::eval_string(vm.world(), &text, span) } diff --git a/library/src/compute/mod.rs b/library/src/compute/mod.rs index 90730e02..e9e4870c 100644 --- a/library/src/compute/mod.rs +++ b/library/src/compute/mod.rs @@ -13,27 +13,27 @@ use crate::prelude::*; /// Hook up all compute definitions. pub(super) fn define(global: &mut Scope) { - global.define("type", type_); - global.define("repr", repr); - global.define("panic", panic); - global.define("assert", assert); - global.define("eval", eval); - global.define("int", int); - global.define("float", float); - global.define("luma", luma); - global.define("rgb", rgb); - global.define("cmyk", cmyk); - global.define("datetime", datetime); - global.define("symbol", symbol); - global.define("str", str); - global.define("label", label); - global.define("regex", regex); - global.define("range", range); - global.define("read", read); - global.define("csv", csv); - global.define("json", json); - global.define("toml", toml); - global.define("yaml", yaml); - global.define("xml", xml); + global.define("type", type_func()); + global.define("repr", repr_func()); + global.define("panic", panic_func()); + global.define("assert", assert_func()); + global.define("eval", eval_func()); + global.define("int", int_func()); + global.define("float", float_func()); + global.define("luma", luma_func()); + global.define("rgb", rgb_func()); + global.define("cmyk", cmyk_func()); + global.define("datetime", datetime_func()); + global.define("symbol", symbol_func()); + global.define("str", str_func()); + global.define("label", label_func()); + global.define("regex", regex_func()); + global.define("range", range_func()); + global.define("read", read_func()); + global.define("csv", csv_func()); + global.define("json", json_func()); + global.define("toml", toml_func()); + global.define("yaml", yaml_func()); + global.define("xml", xml_func()); global.define("calc", calc::module()); } -- cgit v1.2.3