summaryrefslogtreecommitdiff
path: root/library/src/compute
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-10 12:55:21 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-10 12:55:21 +0100
commit62f35602a87574dcc607f1637aeae1be574981ff (patch)
tree363a1918006e06d7d79dc2ace5f8e59cd3b6bb19 /library/src/compute
parentc38d72383d2068361d635d6c1c78ba97aa917801 (diff)
New #[func] macro
Diffstat (limited to 'library/src/compute')
-rw-r--r--library/src/compute/calc.rs443
-rw-r--r--library/src/compute/construct.rs264
-rw-r--r--library/src/compute/data.rs97
-rw-r--r--library/src/compute/foundations.rs83
4 files changed, 414 insertions, 473 deletions
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs
index d4a4bcf6..25df817d 100644
--- a/library/src/compute/calc.rs
+++ b/library/src/compute/calc.rs
@@ -10,28 +10,28 @@ use crate::prelude::*;
/// A module with computational functions.
pub fn module() -> Module {
let mut scope = Scope::new();
- scope.def_func::<AbsFunc>("abs");
- scope.def_func::<PowFunc>("pow");
- scope.def_func::<SqrtFunc>("sqrt");
- scope.def_func::<SinFunc>("sin");
- scope.def_func::<CosFunc>("cos");
- scope.def_func::<TanFunc>("tan");
- scope.def_func::<AsinFunc>("asin");
- scope.def_func::<AcosFunc>("acos");
- scope.def_func::<AtanFunc>("atan");
- scope.def_func::<SinhFunc>("sinh");
- scope.def_func::<CoshFunc>("cosh");
- scope.def_func::<TanhFunc>("tanh");
- scope.def_func::<LogFunc>("log");
- scope.def_func::<FloorFunc>("floor");
- scope.def_func::<CeilFunc>("ceil");
- scope.def_func::<RoundFunc>("round");
- scope.def_func::<ClampFunc>("clamp");
- scope.def_func::<MinFunc>("min");
- scope.def_func::<MaxFunc>("max");
- scope.def_func::<EvenFunc>("even");
- scope.def_func::<OddFunc>("odd");
- scope.def_func::<ModFunc>("mod");
+ 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("sinh", sinh);
+ scope.define("cosh", cosh);
+ scope.define("tanh", tanh);
+ scope.define("log", log);
+ scope.define("floor", floor);
+ scope.define("ceil", ceil);
+ 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("mod", mod_);
scope.define("inf", Value::Float(f64::INFINITY));
scope.define("nan", Value::Float(f64::NAN));
scope.define("pi", Value::Float(std::f64::consts::PI));
@@ -48,15 +48,15 @@ pub fn module() -> Module {
/// #calc.abs(2fr)
/// ```
///
-/// ## Parameters
-/// - value: `ToAbs` (positional, required)
-/// The value whose absolute value to calculate.
-///
/// Display: Absolute
/// Category: calculate
+/// Returns: any
#[func]
-pub fn abs(args: &mut Args) -> SourceResult<Value> {
- Ok(args.expect::<ToAbs>("value")?.0)
+pub fn abs(
+ /// The value whose absolute value to calculate.
+ value: ToAbs,
+) -> Value {
+ value.0
}
/// A value of which the absolute value can be taken.
@@ -80,27 +80,27 @@ cast_from_value! {
/// #calc.pow(2, 3)
/// ```
///
-/// ## Parameters
-/// - base: `Num` (positional, required)
-/// The base of the power.
-/// - exponent: `Num` (positional, required)
-/// The exponent of the power. Must be non-negative.
-///
/// Display: Power
/// Category: calculate
+/// Returns: integer or float
#[func]
-pub fn pow(args: &mut Args) -> SourceResult<Value> {
- let base = args.expect::<Num>("base")?;
- let exponent = args
- .expect::<Spanned<Num>>("exponent")
- .and_then(|n| match n.v {
- Num::Int(i) if i > u32::MAX as i64 => bail!(n.span, "exponent too large"),
- Num::Int(i) if i >= 0 => Ok(n),
- Num::Float(f) if f >= 0.0 => Ok(n),
- _ => bail!(n.span, "exponent must be non-negative"),
- })?
- .v;
- Ok(base.apply2(exponent, |a, b| a.pow(b as u32), f64::powf))
+pub fn pow(
+ /// The base of the power.
+ base: Num,
+ /// The exponent of the power. Must be non-negative.
+ exponent: Spanned<Num>,
+) -> Value {
+ let exponent = match exponent.v {
+ Num::Int(i) if i > u32::MAX as i64 => {
+ bail!(exponent.span, "exponent too large");
+ }
+ Num::Int(0..) => exponent.v,
+ Num::Float(f) if f >= 0.0 => exponent.v,
+ _ => {
+ bail!(exponent.span, "exponent must be non-negative");
+ }
+ };
+ base.apply2(exponent, |a, b| a.pow(b as u32), f64::powf)
}
/// Calculate the square root of a number.
@@ -111,19 +111,18 @@ pub fn pow(args: &mut Args) -> SourceResult<Value> {
/// #calc.sqrt(2.5)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number whose square root to calculate. Must be non-negative.
-///
/// Display: Square Root
/// Category: calculate
+/// Returns: float
#[func]
-pub fn sqrt(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Spanned<Num>>("value")?;
+pub fn sqrt(
+ /// The number whose square root to calculate. Must be non-negative.
+ value: Spanned<Num>,
+) -> Value {
if value.v.float() < 0.0 {
bail!(value.span, "cannot take square root of negative number");
}
- Ok(Value::Float(value.v.float().sqrt()))
+ Value::Float(value.v.float().sqrt())
}
/// Calculate the sine of an angle.
@@ -138,20 +137,19 @@ pub fn sqrt(args: &mut Args) -> SourceResult<Value> {
/// #calc.sin(90deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose sine to calculate.
-///
/// Display: Sine
/// Category: calculate
+/// Returns: float
#[func]
-pub fn sin(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn sin(
+ /// The angle whose sine to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -161,25 +159,24 @@ pub fn sin(args: &mut Args) -> SourceResult<Value> {
///
/// ## Example
/// ```example
-/// #calc.cos(90deg)
+/// #calc.cos(90deg) \
/// #calc.cos(1.5) \
/// #calc.cos(90deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose cosine to calculate.
-///
/// Display: Cosine
/// Category: calculate
+/// Returns: float
#[func]
-pub fn cos(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn cos(
+ /// The angle whose cosine to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -193,20 +190,19 @@ pub fn cos(args: &mut Args) -> SourceResult<Value> {
/// #calc.tan(90deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose tangent to calculate.
-///
/// Display: Tangent
/// Category: calculate
+/// Returns: float
#[func]
-pub fn tan(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn tan(
+ /// The angle whose tangent to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -217,20 +213,19 @@ pub fn tan(args: &mut Args) -> SourceResult<Value> {
/// #calc.asin(1)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number whose arcsine to calculate. Must be between -1 and 1.
-///
/// Display: Arcsine
/// Category: calculate
+/// Returns: angle
#[func]
-pub fn asin(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect::<Spanned<Num>>("value")?;
- let val = v.float();
+pub fn asin(
+ /// The number whose arcsine to calculate. Must be between -1 and 1.
+ value: Spanned<Num>,
+) -> Value {
+ let val = value.v.float();
if val < -1.0 || val > 1.0 {
- bail!(span, "arcsin must be between -1 and 1");
+ bail!(value.span, "arcsin must be between -1 and 1");
}
- Ok(Value::Angle(Angle::rad(val.asin())))
+ Value::Angle(Angle::rad(val.asin()))
}
/// Calculate the arccosine of a number.
@@ -241,20 +236,19 @@ pub fn asin(args: &mut Args) -> SourceResult<Value> {
/// #calc.acos(1)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number whose arccosine to calculate. Must be between -1 and 1.
-///
/// Display: Arccosine
/// Category: calculate
+/// Returns: angle
#[func]
-pub fn acos(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect::<Spanned<Num>>("value")?;
- let val = v.float();
+pub fn acos(
+ /// The number whose arcsine to calculate. Must be between -1 and 1.
+ value: Spanned<Num>,
+) -> Value {
+ let val = value.v.float();
if val < -1.0 || val > 1.0 {
- bail!(span, "arccos must be between -1 and 1");
+ bail!(value.span, "arccos must be between -1 and 1");
}
- Ok(Value::Angle(Angle::rad(val.acos())))
+ Value::Angle(Angle::rad(val.acos()))
}
/// Calculate the arctangent of a number.
@@ -265,16 +259,15 @@ pub fn acos(args: &mut Args) -> SourceResult<Value> {
/// #calc.atan(1)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number whose arctangent to calculate.
-///
/// Display: Arctangent
/// Category: calculate
+/// Returns: angle
#[func]
-pub fn atan(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Num>("value")?;
- Ok(Value::Angle(Angle::rad(value.float().atan())))
+pub fn atan(
+ /// The number whose arctangent to calculate.
+ value: Num,
+) -> Value {
+ Value::Angle(Angle::rad(value.float().atan()))
}
/// Calculate the hyperbolic sine of an angle.
@@ -287,20 +280,19 @@ pub fn atan(args: &mut Args) -> SourceResult<Value> {
/// #calc.sinh(45deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose hyperbolic sine to calculate.
-///
/// Display: Hyperbolic sine
/// Category: calculate
+/// Returns: float
#[func]
-pub fn sinh(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn sinh(
+ /// The angle whose hyperbolic sine to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -313,20 +305,19 @@ pub fn sinh(args: &mut Args) -> SourceResult<Value> {
/// #calc.cosh(45deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose hyperbolic cosine to calculate.
-///
/// Display: Hyperbolic cosine
/// Category: calculate
+/// Returns: float
#[func]
-pub fn cosh(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn cosh(
+ /// The angle whose hyperbolic cosine to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -339,20 +330,19 @@ pub fn cosh(args: &mut Args) -> SourceResult<Value> {
/// #calc.tanh(45deg)
/// ```
///
-/// ## Parameters
-/// - angle: `AngleLike` (positional, required)
-/// The angle whose hyperbolic tangent to calculate.
-///
/// Display: Hyperbolic tangent
/// Category: calculate
+/// Returns: float
#[func]
-pub fn tanh(args: &mut Args) -> SourceResult<Value> {
- let arg = args.expect::<AngleLike>("angle")?;
- Ok(Value::Float(match arg {
+pub fn tanh(
+ /// The angle whose hyperbolic tangent to calculate.
+ angle: AngleLike,
+) -> Value {
+ Value::Float(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.
@@ -361,22 +351,22 @@ pub fn tanh(args: &mut Args) -> SourceResult<Value> {
///
/// ## Example
/// ```example
-/// #calc.log(100) \
+/// #calc.log(100)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number whose logarithm to calculate.
-/// - base: `Num` (named)
-/// The base of the logarithm.
-///
/// Display: Logarithm
/// Category: calculate
+/// Returns: float
#[func]
-pub fn log(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<f64>("value")?;
- let base = args.named::<f64>("base")?.unwrap_or_else(|| 10.0);
- Ok(Value::Float(value.log(base)))
+pub fn log(
+ /// The number whose logarithm to calculate.
+ value: f64,
+ /// The base of the logarithm.
+ #[named]
+ #[default(10.0)]
+ base: f64,
+) -> Value {
+ Value::Float(value.log(base))
}
/// Round a number down to the nearest integer.
@@ -390,19 +380,18 @@ pub fn log(args: &mut Args) -> SourceResult<Value> {
/// #calc.floor(500.1)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number to round down.
-///
/// Display: Round down
/// Category: calculate
+/// Returns: integer
#[func]
-pub fn floor(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Num>("value")?;
- Ok(match value {
+pub fn floor(
+ /// The number to round down.
+ value: Num,
+) -> Value {
+ match value {
Num::Int(n) => Value::Int(n),
Num::Float(n) => Value::Int(n.floor() as i64),
- })
+ }
}
/// Round a number up to the nearest integer.
@@ -416,19 +405,18 @@ pub fn floor(args: &mut Args) -> SourceResult<Value> {
/// #calc.ceil(500.1)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number to round up.
-///
/// Display: Round up
/// Category: calculate
+/// Returns: integer
#[func]
-pub fn ceil(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Num>("value")?;
- Ok(match value {
+pub fn ceil(
+ /// The number to round up.
+ value: Num,
+) -> Value {
+ match value {
Num::Int(n) => Value::Int(n),
Num::Float(n) => Value::Int(n.ceil() as i64),
- })
+ }
}
/// Round a number to the nearest integer.
@@ -442,25 +430,26 @@ pub fn ceil(args: &mut Args) -> SourceResult<Value> {
/// #calc.round(3.1415, digits: 2)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number to round.
-/// - digits: `i64` (named)
-///
/// Display: Round
/// Category: calculate
+/// Returns: integer or float
#[func]
-pub fn round(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Num>("value")?;
- let digits = args.named::<u32>("digits")?.unwrap_or(0);
- Ok(match value {
+pub fn round(
+ /// The number to round.
+ value: Num,
+ /// The number of decimal places.
+ #[named]
+ #[default(0)]
+ digits: i64,
+) -> Value {
+ match value {
Num::Int(n) if digits == 0 => Value::Int(n),
_ => {
let n = value.float();
let factor = 10.0_f64.powi(digits as i32) as f64;
Value::Float((n * factor).round() / factor)
}
- })
+ }
}
/// Clamp a number between a minimum and maximum value.
@@ -472,25 +461,22 @@ pub fn round(args: &mut Args) -> SourceResult<Value> {
/// #calc.clamp(5, 0, 4)
/// ```
///
-/// ## Parameters
-/// - value: `Num` (positional, required)
-/// The number to clamp.
-/// - min: `Num` (positional, required)
-/// The inclusive minimum value.
-/// - max: `Num` (positional, required)
-/// The inclusive maximum value.
-///
/// Display: Clamp
/// Category: calculate
+/// Returns: integer or float
#[func]
-pub fn clamp(args: &mut Args) -> SourceResult<Value> {
- let value = args.expect::<Num>("value")?;
- let min = args.expect::<Num>("min")?;
- let max = args.expect::<Spanned<Num>>("max")?;
+pub fn clamp(
+ /// The number to clamp.
+ value: Num,
+ /// The inclusive minimum value.
+ min: Num,
+ /// The inclusive maximum value.
+ max: Spanned<Num>,
+) -> Value {
if max.v.float() < min.float() {
bail!(max.span, "max must be greater than or equal to min")
}
- Ok(value.apply3(min, max.v, i64::clamp, f64::clamp))
+ value.apply3(min, max.v, i64::clamp, f64::clamp)
}
/// Determine the minimum of a sequence of values.
@@ -501,18 +487,17 @@ pub fn clamp(args: &mut Args) -> SourceResult<Value> {
/// #calc.min("typst", "in", "beta")
/// ```
///
-/// ## Parameters
-/// - values: `Value` (positional, variadic)
-/// The sequence of values from which to extract the minimum.
-/// Must not be empty.
-///
-/// - returns: any
-///
/// Display: Minimum
/// Category: calculate
+/// Returns: any
#[func]
-pub fn min(args: &mut Args) -> SourceResult<Value> {
- minmax(args, Ordering::Less)
+pub fn min(
+ /// The sequence of values from which to extract the minimum.
+ /// Must not be empty.
+ #[variadic]
+ values: Vec<Spanned<Value>>,
+) -> Value {
+ minmax(args.span, values, Ordering::Less)?
}
/// Determine the maximum of a sequence of values.
@@ -523,24 +508,31 @@ pub fn min(args: &mut Args) -> SourceResult<Value> {
/// #calc.max("typst", "in", "beta")
/// ```
///
-/// ## Parameters
-/// - values: `Value` (positional, variadic)
-/// The sequence of values from which to extract the maximum.
-/// Must not be empty.
-///
-/// - returns: any
-///
/// Display: Maximum
/// Category: calculate
+/// Returns: any
#[func]
-pub fn max(args: &mut Args) -> SourceResult<Value> {
- minmax(args, Ordering::Greater)
+pub fn max(
+ /// The sequence of values from which to extract the maximum.
+ /// Must not be empty.
+ #[variadic]
+ values: Vec<Spanned<Value>>,
+) -> Value {
+ minmax(args.span, values, Ordering::Greater)?
}
/// Find the minimum or maximum of a sequence of values.
-fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
- let mut extremum = args.expect::<Value>("value")?;
- for Spanned { v, span } in args.all::<Spanned<Value>>()? {
+fn minmax(
+ span: Span,
+ values: Vec<Spanned<Value>>,
+ goal: Ordering,
+) -> SourceResult<Value> {
+ let mut iter = values.into_iter();
+ let Some(Spanned { v: mut extremum, ..}) = iter.next() else {
+ bail!(span, "expected at least one value");
+ };
+
+ for Spanned { v, span } in iter {
match v.partial_cmp(&extremum) {
Some(ordering) => {
if ordering == goal {
@@ -555,6 +547,7 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
),
}
}
+
Ok(extremum)
}
@@ -567,17 +560,15 @@ fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
/// #range(10).filter(calc.even)
/// ```
///
-/// ## Parameters
-/// - value: `i64` (positional, required)
-/// The number to check for evenness.
-///
-/// - returns: boolean
-///
/// Display: Even
/// Category: calculate
+/// Returns: boolean
#[func]
-pub fn even(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Bool(args.expect::<i64>("value")? % 2 == 0))
+pub fn even(
+ /// The number to check for evenness.
+ value: i64,
+) -> Value {
+ Value::Bool(value % 2 == 0)
}
/// Determine whether an integer is odd.
@@ -589,18 +580,15 @@ pub fn even(args: &mut Args) -> SourceResult<Value> {
/// #range(10).filter(calc.odd)
/// ```
///
-///
-/// ## Parameters
-/// - value: `i64` (positional, required)
-/// The number to check for oddness.
-///
-/// - returns: boolean
-///
/// Display: Odd
/// Category: calculate
+/// Returns: boolean
#[func]
-pub fn odd(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Bool(args.expect::<i64>("value")? % 2 != 0))
+pub fn odd(
+ /// The number to check for oddness.
+ value: i64,
+) -> Value {
+ Value::Bool(value % 2 != 0)
}
/// Calculate the modulus of two numbers.
@@ -611,25 +599,20 @@ pub fn odd(args: &mut Args) -> SourceResult<Value> {
/// #calc.mod(1.75, 0.5)
/// ```
///
-/// ## Parameters
-/// - dividend: `Num` (positional, required)
-/// The dividend of the modulus.
-///
-/// - divisor: `Num` (positional, required)
-/// The divisor of the modulus.
-///
-/// - returns: integer or float
-///
/// Display: Modulus
/// Category: calculate
+/// Returns: integer or float
#[func]
-pub fn mod_(args: &mut Args) -> SourceResult<Value> {
- let dividend = args.expect::<Num>("dividend")?;
- let Spanned { v: divisor, span } = args.expect::<Spanned<Num>>("divisor")?;
- if divisor.float() == 0.0 {
- bail!(span, "divisor must not be zero");
+pub fn mod_(
+ /// The dividend of the modulus.
+ dividend: Num,
+ /// The divisor of the modulus.
+ divisor: Spanned<Num>,
+) -> Value {
+ if divisor.v.float() == 0.0 {
+ bail!(divisor.span, "divisor must not be zero");
}
- Ok(dividend.apply2(divisor, Rem::rem, Rem::rem))
+ dividend.apply2(divisor.v, Rem::rem, Rem::rem)
}
/// A value which can be passed to functions that work with integers and floats.
diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs
index db442327..4d6068a1 100644
--- a/library/src/compute/construct.rs
+++ b/library/src/compute/construct.rs
@@ -1,3 +1,4 @@
+use std::num::NonZeroI64;
use std::str::FromStr;
use ecow::EcoVec;
@@ -19,17 +20,15 @@ use crate::prelude::*;
/// #{ int("27") + int("4") }
/// ```
///
-/// ## Parameters
-/// - value: `ToInt` (positional, required)
-/// The value that should be converted to an integer.
-///
-/// - returns: integer
-///
/// Display: Integer
/// Category: construct
+/// Returns: integer
#[func]
-pub fn int(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Int(args.expect::<ToInt>("value")?.0))
+pub fn int(
+ /// The value that should be converted to an integer.
+ value: ToInt,
+) -> Value {
+ Value::Int(value.0)
}
/// A value that can be cast to an integer.
@@ -59,17 +58,15 @@ cast_from_value! {
/// #float("1e5")
/// ```
///
-/// ## Parameters
-/// - value: `ToFloat` (positional, required)
-/// The value that should be converted to a float.
-///
-/// - returns: float
-///
/// Display: Float
/// Category: construct
+/// Returns: float
#[func]
-pub fn float(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Float(args.expect::<ToFloat>("value")?.0))
+pub fn float(
+ /// The value that should be converted to a float.
+ value: ToFloat,
+) -> Value {
+ Value::Float(value.0)
}
/// A value that can be cast to a float.
@@ -92,18 +89,15 @@ cast_from_value! {
/// }
/// ```
///
-/// ## Parameters
-/// - gray: `Component` (positional, required)
-/// The gray component.
-///
-/// - returns: color
-///
/// Display: Luma
/// Category: construct
+/// Returns: color
#[func]
-pub fn luma(args: &mut Args) -> SourceResult<Value> {
- let Component(luma) = args.expect("gray component")?;
- Ok(Value::Color(LumaColor::new(luma).into()))
+pub fn luma(
+ /// The gray component.
+ gray: Component,
+) -> Value {
+ Value::Color(LumaColor::new(gray.0).into())
}
/// Create an RGB(A) color.
@@ -121,40 +115,44 @@ pub fn luma(args: &mut Args) -> SourceResult<Value> {
/// #square(fill: rgb(25%, 13%, 65%))
/// ```
///
-/// ## Parameters
-/// - hex: `EcoString` (positional)
-/// The color in hexadecimal notation.
-///
-/// Accepts three, four, six or eight hexadecimal digits and optionally
-/// a leading hashtag.
-///
-/// If this string is given, the individual components should not be given.
-///
-/// ```example
-/// #text(16pt, rgb("#239dad"))[
-/// *Typst*
-/// ]
-/// ```
-///
-/// - red: `Component` (positional)
-/// The red component.
-///
-/// - green: `Component` (positional)
-/// The green component.
-///
-/// - blue: `Component` (positional)
-/// The blue component.
-///
-/// - alpha: `Component` (positional)
-/// The alpha component.
-///
-/// - returns: color
-///
-/// Display: RGBA
+/// Display: RGB
/// Category: construct
+/// Returns: color
#[func]
-pub fn rgb(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
+pub fn rgb(
+ /// The color in hexadecimal notation.
+ ///
+ /// Accepts three, four, six or eight hexadecimal digits and optionally
+ /// a leading hashtag.
+ ///
+ /// If this string is given, the individual components should not be given.
+ ///
+ /// ```example
+ /// #text(16pt, rgb("#239dad"))[
+ /// *Typst*
+ /// ]
+ /// ```
+ #[external]
+ #[default]
+ hex: EcoString,
+ /// The red component.
+ #[external]
+ #[default]
+ red: Component,
+ /// The green component.
+ #[external]
+ #[default]
+ green: Component,
+ /// The blue component.
+ #[external]
+ #[default]
+ blue: Component,
+ /// The alpha component.
+ #[external]
+ #[default]
+ alpha: Component,
+) -> Value {
+ Value::Color(if let Some(string) = args.find::<Spanned<EcoString>>()? {
match RgbaColor::from_str(&string.v) {
Ok(color) => color.into(),
Err(msg) => bail!(string.span, msg),
@@ -165,7 +163,7 @@ pub fn rgb(args: &mut Args) -> SourceResult<Value> {
let Component(b) = args.expect("blue component")?;
let Component(a) = args.eat()?.unwrap_or(Component(255));
RgbaColor::new(r, g, b, a).into()
- }))
+ })
}
/// An integer or ratio component.
@@ -197,30 +195,21 @@ cast_from_value! {
/// )
/// ````
///
-/// ## Parameters
-/// - cyan: `RatioComponent` (positional, required)
-/// The cyan component.
-///
-/// - magenta: `RatioComponent` (positional, required)
-/// The magenta component.
-///
-/// - yellow: `RatioComponent` (positional, required)
-/// The yellow component.
-///
-/// - key: `RatioComponent` (positional, required)
-/// The key component.
-///
-/// - returns: color
-///
/// Display: CMYK
/// Category: construct
+/// Returns: color
#[func]
-pub fn cmyk(args: &mut Args) -> SourceResult<Value> {
- let RatioComponent(c) = args.expect("cyan component")?;
- let RatioComponent(m) = args.expect("magenta component")?;
- let RatioComponent(y) = args.expect("yellow component")?;
- let RatioComponent(k) = args.expect("key component")?;
- Ok(Value::Color(CmykColor::new(c, m, y, k).into()))
+pub fn cmyk(
+ /// The cyan component.
+ cyan: RatioComponent,
+ /// The magenta component.
+ magenta: RatioComponent,
+ /// The yellow component.
+ yellow: RatioComponent,
+ /// The key component.
+ key: RatioComponent,
+) -> Value {
+ Value::Color(CmykColor::new(cyan.0, magenta.0, yellow.0, key.0).into())
}
/// A component that must be a ratio.
@@ -254,30 +243,29 @@ cast_from_value! {
/// #envelope.fly
/// ```
///
-/// ## Parameters
-/// - variants: `Variant` (positional, variadic)
-/// The variants of the symbol.
-///
-/// Can be a just a string consisting of a single character for the
-/// modifierless variant or an array with two strings specifying the modifiers
-/// and the symbol. Individual modifiers should be separated by dots. When
-/// displaying a symbol, Typst selects the first from the variants that have
-/// all attached modifiers and the minimum number of other modifiers.
-///
-/// - returns: symbol
-///
/// Display: Symbol
/// Category: construct
+/// Returns: symbol
#[func]
-pub fn symbol(args: &mut Args) -> SourceResult<Value> {
+pub fn symbol(
+ /// The variants of the symbol.
+ ///
+ /// Can be a just a string consisting of a single character for the
+ /// modifierless variant or an array with two strings specifying the modifiers
+ /// and the symbol. Individual modifiers should be separated by dots. When
+ /// displaying a symbol, Typst selects the first from the variants that have
+ /// all attached modifiers and the minimum number of other modifiers.
+ #[variadic]
+ variants: Vec<Spanned<Variant>>,
+) -> Value {
let mut list = EcoVec::new();
- for Spanned { v, span } in args.all::<Spanned<Variant>>()? {
+ for Spanned { v, span } in variants {
if list.iter().any(|(prev, _)| &v.0 == prev) {
bail!(span, "duplicate variant");
}
list.push((v.0, v.1));
}
- Ok(Value::Symbol(Symbol::runtime(list)))
+ Value::Symbol(Symbol::runtime(list))
}
/// A value that can be cast to a symbol.
@@ -309,17 +297,15 @@ cast_from_value! {
/// #str(<intro>)
/// ```
///
-/// ## Parameters
-/// - value: `ToStr` (positional, required)
-/// The value that should be converted to a string.
-///
-/// - returns: string
-///
/// Display: String
/// Category: construct
+/// Returns: string
#[func]
-pub fn str(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Str(args.expect::<ToStr>("value")?.0))
+pub fn str(
+ /// The value that should be converted to a string.
+ value: ToStr,
+) -> Value {
+ Value::Str(value.0)
}
/// A value that can be cast to a string.
@@ -352,17 +338,15 @@ cast_from_value! {
/// This function also has dedicated syntax: You can create a label by enclosing
/// its name in angle brackets. This works both in markup and code.
///
-/// ## Parameters
-/// - name: `EcoString` (positional, required)
-/// The name of the label.
-///
-/// - returns: label
-///
/// Display: Label
/// Category: construct
+/// Returns: label
#[func]
-pub fn label(args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Label(Label(args.expect("string")?)))
+pub fn label(
+ /// The name of the label.
+ name: EcoString,
+) -> Value {
+ Value::Label(Label(name))
}
/// Create a regular expression from a string.
@@ -386,23 +370,20 @@ pub fn label(args: &mut Args) -> SourceResult<Value> {
/// .split(regex("[,;]")))
/// ```
///
-/// ## Parameters
-/// - regex: `EcoString` (positional, required)
-/// The regular expression as a string.
-///
-/// Most regex escape sequences just work because they are not valid Typst
-/// escape sequences. To produce regex escape sequences that are also valid in
-/// Typst (e.g. `[\\]`), you need to escape twice. Thus, to match a verbatim
-/// backslash, you would need to write `{regex("\\\\")}`.
-///
-/// - returns: regex
-///
/// Display: Regex
/// Category: construct
+/// Returns: regex
#[func]
-pub fn regex(args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
- Ok(Regex::new(&v).at(span)?.into())
+pub fn regex(
+ /// The regular expression as a string.
+ ///
+ /// Most regex escape sequences just work because they are not valid Typst
+ /// escape sequences. To produce regex escape sequences that are also valid in
+ /// Typst (e.g. `[\\]`), you need to escape twice. Thus, to match a verbatim
+ /// backslash, you would need to write `{regex("\\\\")}`.
+ regex: Spanned<EcoString>,
+) -> Value {
+ Regex::new(&regex.v).at(regex.span)?.into()
}
/// Create an array consisting of a sequence of numbers.
@@ -420,33 +401,30 @@ pub fn regex(args: &mut Args) -> SourceResult<Value> {
/// #range(5, 2, step: -1)
/// ```
///
-/// ## Parameters
-/// - start: `i64` (positional)
-/// The start of the range (inclusive).
-///
-/// - end: `i64` (positional, required)
-/// The end of the range (exclusive).
-///
-/// - step: `i64` (named)
-/// The distance between the generated numbers.
-///
-/// - returns: array
-///
/// Display: Range
/// Category: construct
+/// Returns: array
#[func]
-pub fn range(args: &mut Args) -> SourceResult<Value> {
+pub fn range(
+ /// The start of the range (inclusive).
+ #[external]
+ #[default]
+ start: i64,
+ /// The end of the range (exclusive).
+ #[external]
+ end: i64,
+ /// The distance between the generated numbers.
+ #[named]
+ #[default(NonZeroI64::new(1).unwrap())]
+ step: NonZeroI64,
+) -> Value {
let first = args.expect::<i64>("end")?;
let (start, end) = match args.eat::<i64>()? {
Some(second) => (first, second),
None => (0, first),
};
- let step: i64 = match args.named("step")? {
- Some(Spanned { v: 0, span }) => bail!(span, "step must not be zero"),
- Some(Spanned { v, .. }) => v,
- None => 1,
- };
+ let step = step.get();
let mut x = start;
let mut array = Array::new();
@@ -456,5 +434,5 @@ pub fn range(args: &mut Args) -> SourceResult<Value> {
x += step;
}
- Ok(Value::Array(array))
+ Value::Array(array)
}
diff --git a/library/src/compute/data.rs b/library/src/compute/data.rs
index 90d72ade..7addff78 100644
--- a/library/src/compute/data.rs
+++ b/library/src/compute/data.rs
@@ -16,25 +16,21 @@ use crate::prelude::*;
/// #raw(text, lang: "html")
/// ```
///
-/// ## Parameters
-/// - path: `EcoString` (positional, required)
-/// Path to a file.
-///
-/// - returns: string
-///
/// Display: Plain text
/// Category: data-loading
+/// Returns: string
#[func]
-pub fn read(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: path, span } = args.expect::<Spanned<EcoString>>("path to file")?;
-
+pub fn read(
+ /// Path to a file.
+ path: Spanned<EcoString>,
+) -> Value {
+ let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
-
let text = String::from_utf8(data.to_vec())
.map_err(|_| "file is not valid utf-8")
.at(span)?;
- Ok(Value::Str(text.into()))
+ Value::Str(text.into())
}
/// Read structured data from a CSV file.
@@ -55,33 +51,27 @@ pub fn read(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
/// )
/// ```
///
-/// ## Parameters
-/// - path: `EcoString` (positional, required)
-/// Path to a CSV file.
-///
-/// - delimiter: `Delimiter` (named)
-/// The delimiter that separates columns in the CSV file.
-/// Must be a single ASCII character.
-/// Defaults to a comma.
-///
-/// - returns: array
-///
/// Display: CSV
/// Category: data-loading
+/// Returns: array
#[func]
-pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: path, span } =
- args.expect::<Spanned<EcoString>>("path to csv file")?;
-
+pub fn csv(
+ /// Path to a CSV file.
+ path: Spanned<EcoString>,
+ /// The delimiter that separates columns in the CSV file.
+ /// Must be a single ASCII character.
+ /// Defaults to a comma.
+ #[named]
+ #[default]
+ delimiter: Delimiter,
+) -> Value {
+ let Spanned { v: path, span } = path;
let path = vm.locate(&path).at(span)?;
let data = vm.world().file(&path).at(span)?;
let mut builder = csv::ReaderBuilder::new();
builder.has_headers(false);
-
- if let Some(delimiter) = args.named::<Delimiter>("delimiter")? {
- builder.delimiter(delimiter.0);
- }
+ builder.delimiter(delimiter.0);
let mut reader = builder.from_reader(data.as_slice());
let mut array = Array::new();
@@ -92,7 +82,7 @@ pub fn csv(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
array.push(Value::Array(sub))
}
- Ok(Value::Array(array))
+ Value::Array(array)
}
/// The delimiter to use when parsing CSV files.
@@ -115,6 +105,12 @@ cast_from_value! {
},
}
+impl Default for Delimiter {
+ fn default() -> Self {
+ Self(b',')
+ }
+}
+
/// Format the user-facing CSV error message.
fn format_csv_error(error: csv::Error) -> String {
match error.kind() {
@@ -170,25 +166,20 @@ fn format_csv_error(error: csv::Error) -> String {
/// #forecast(json("tuesday.json"))
/// ```
///
-/// ## Parameters
-/// - path: `EcoString` (positional, required)
-/// Path to a JSON file.
-///
-/// - returns: dictionary or array
-///
/// Display: JSON
/// Category: data-loading
+/// Returns: array or dictionary
#[func]
-pub fn json(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: path, span } =
- args.expect::<Spanned<EcoString>>("path to json file")?;
-
+pub fn json(
+ /// Path to a JSON file.
+ path: Spanned<EcoString>,
+) -> Value {
+ 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)?;
-
- Ok(convert_json(value))
+ convert_json(value)
}
/// Convert a JSON value to a Typst value.
@@ -268,26 +259,20 @@ fn format_json_error(error: serde_json::Error) -> String {
/// }
/// ```
///
-/// ## Parameters
-/// - path: `EcoString` (positional, required)
-/// Path to an XML file.
-///
-/// - returns: array
-///
/// Display: XML
/// Category: data-loading
+/// Returns: array
#[func]
-pub fn xml(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: path, span } =
- args.expect::<Spanned<EcoString>>("path to xml file")?;
-
+pub fn xml(
+ /// Path to an XML file.
+ path: Spanned<EcoString>,
+) -> Value {
+ 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)?;
-
- Ok(convert_xml(document.root()))
+ convert_xml(document.root())
}
/// Convert an XML node to a Typst value.
diff --git a/library/src/compute/foundations.rs b/library/src/compute/foundations.rs
index 710ec68e..41a6bc35 100644
--- a/library/src/compute/foundations.rs
+++ b/library/src/compute/foundations.rs
@@ -14,17 +14,15 @@ use crate::prelude::*;
/// #type(x => x + 1)
/// ```
///
-/// ## Parameters
-/// - value: `Value` (positional, required)
-/// The value whose type's to determine.
-///
-/// - returns: string
-///
/// Display: Type
/// Category: foundations
+/// Returns: string
#[func]
-pub fn type_(args: &mut Args) -> SourceResult<Value> {
- Ok(args.expect::<Value>("value")?.type_name().into())
+pub fn type_(
+ /// The value whose type's to determine.
+ value: Value,
+) -> Value {
+ value.type_name().into()
}
/// The string representation of a value.
@@ -41,17 +39,15 @@ pub fn type_(args: &mut Args) -> SourceResult<Value> {
/// #[*Hi*] vs #repr([*Hi*])
/// ```
///
-/// ## Parameters
-/// - value: `Value` (positional, required)
-/// The value whose string representation to produce.
-///
-/// - returns: string
-///
/// Display: Representation
/// Category: foundations
+/// Returns: string
#[func]
-pub fn repr(args: &mut Args) -> SourceResult<Value> {
- Ok(args.expect::<Value>("value")?.repr().into())
+pub fn repr(
+ /// The value whose string representation to produce.
+ value: Value,
+) -> Value {
+ value.repr().into()
}
/// Fail with an error.
@@ -62,15 +58,16 @@ pub fn repr(args: &mut Args) -> SourceResult<Value> {
/// #panic("this is wrong")
/// ```
///
-/// ## Parameters
-/// - payload: `Value` (positional)
-/// The value (or message) to panic with.
-///
/// Display: Panic
/// Category: foundations
+/// Returns:
#[func]
-pub fn panic(args: &mut Args) -> SourceResult<Value> {
- match args.eat::<Value>()? {
+pub fn panic(
+ /// The value (or message) to panic with.
+ #[default]
+ payload: Option<Value>,
+) -> Value {
+ match payload {
Some(v) => bail!(args.span, "panicked with: {}", v.repr()),
None => bail!(args.span, "panicked"),
}
@@ -86,26 +83,26 @@ pub fn panic(args: &mut Args) -> SourceResult<Value> {
/// #assert(1 < 2, message: "math broke")
/// ```
///
-/// ## Parameters
-/// - condition: `bool` (positional, required)
-/// The condition that must be true for the assertion to pass.
-/// - message: `EcoString` (named)
-/// The error message when the assertion fails.
-///
/// Display: Assert
/// Category: foundations
+/// Returns:
#[func]
-pub fn assert(args: &mut Args) -> SourceResult<Value> {
- let check = args.expect::<bool>("condition")?;
- let message = args.named::<EcoString>("message")?;
- if !check {
+pub fn assert(
+ /// The condition that must be true for the assertion to pass.
+ condition: bool,
+ /// The error message when the assertion fails.
+ #[named]
+ #[default]
+ message: Option<EcoString>,
+) -> Value {
+ if !condition {
if let Some(message) = message {
bail!(args.span, "assertion failed: {}", message);
} else {
bail!(args.span, "assertion failed");
}
}
- Ok(Value::None)
+ Value::None
}
/// Evaluate a string as Typst code.
@@ -119,18 +116,16 @@ pub fn assert(args: &mut Args) -> SourceResult<Value> {
/// #eval("[*Strong text*]")
/// ```
///
-/// ## Parameters
-/// - source: `String` (positional, required)
-/// A string of Typst code to evaluate.
-///
-/// The code in the string cannot interact with the file system.
-///
-/// - returns: any
-///
/// Display: Evaluate
/// Category: foundations
+/// Returns: any
#[func]
-pub fn eval(vm: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v: text, span } = args.expect::<Spanned<String>>("source")?;
- typst::eval::eval_code_str(vm.world(), &text, span)
+pub fn eval(
+ /// A string of Typst code to evaluate.
+ ///
+ /// The code in the string cannot interact with the file system.
+ source: Spanned<String>,
+) -> Value {
+ let Spanned { v: text, span } = source;
+ typst::eval::eval_code_str(vm.world(), &text, span)?
}