From 4c92ab4ace41a20ccea59bbdf6917b5dfa29121f Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 21 Dec 2022 23:51:15 +0100 Subject: Rename the `create` category to `construct` --- library/src/compute/construct.rs | 392 +++++++++++++++++++++++++++++++++++++++ library/src/compute/create.rs | 392 --------------------------------------- library/src/compute/mod.rs | 4 +- 3 files changed, 394 insertions(+), 394 deletions(-) create mode 100644 library/src/compute/construct.rs delete mode 100644 library/src/compute/create.rs (limited to 'library/src') diff --git a/library/src/compute/construct.rs b/library/src/compute/construct.rs new file mode 100644 index 00000000..bfe42cbe --- /dev/null +++ b/library/src/compute/construct.rs @@ -0,0 +1,392 @@ +use std::str::FromStr; + +use typst::model::Regex; + +use crate::prelude::*; + +/// # Integer +/// Convert a value to an integer. +/// +/// - Booleans are converted to `0` or `1`. +/// - Floats are floored to the next 64-bit integer. +/// - Strings are parsed in base 10. +/// +/// ## Example +/// ``` +/// #int(false) \ +/// #int(true) \ +/// #int(2.7) \ +/// { int("27") + int("4") } +/// ``` +/// +/// ## Parameters +/// - value: ToInt (positional, required) +/// The value that should be converted to an integer. +/// +/// ## Category +/// construct +#[func] +pub fn int(args: &mut Args) -> SourceResult { + Ok(Value::Int(args.expect::("value")?.0)) +} + +/// A value that can be cast to an integer. +struct ToInt(i64); + +castable! { + ToInt, + v: bool => Self(v as i64), + v: i64 => Self(v), + v: f64 => Self(v as i64), + v: EcoString => Self(v.parse().map_err(|_| "not a valid integer")?), +} + +/// # Float +/// Convert a value to a float. +/// +/// - Booleans are converted to `0.0` or `1.0`. +/// - Integers are converted to the closest 64-bit float. +/// - Strings are parsed in base 10 to the closest 64-bit float. +/// Exponential notation is supported. +/// +/// ## Example +/// ``` +/// #float(false) \ +/// #float(true) \ +/// #float(4) \ +/// #float("2.7") \ +/// #float("1e5") +/// ``` +/// +/// ## Parameters +/// - value: ToFloat (positional, required) +/// The value that should be converted to a float. +/// +/// ## Category +/// construct +#[func] +pub fn float(args: &mut Args) -> SourceResult { + Ok(Value::Float(args.expect::("value")?.0)) +} + +/// A value that can be cast to a float. +struct ToFloat(f64); + +castable! { + ToFloat, + v: bool => Self(v as i64 as f64), + v: i64 => Self(v as f64), + v: f64 => Self(v), + v: EcoString => Self(v.parse().map_err(|_| "not a valid float")?), +} + +/// # Luma +/// Create a grayscale color. +/// +/// ## Example +/// ``` +/// #for x in range(250, step: 50) { +/// square(fill: luma(x)) +/// } +/// ``` +/// +/// ## Parameters +/// - gray: Component (positional, required) +/// The gray component. +/// +/// ## Category +/// construct +#[func] +pub fn luma(args: &mut Args) -> SourceResult { + let Component(luma) = args.expect("gray component")?; + Ok(Value::Color(LumaColor::new(luma).into())) +} + +/// # RGBA +/// Create an RGB(A) color. +/// +/// The color is specified in the sRGB color space. +/// +/// _Note:_ While you can specify transparent colors and Typst's preview will +/// render them correctly, the PDF export does not handle them properly at the +/// moment. This will be fixed in the future. +/// +/// ## Example +/// ``` +/// #square(fill: rgb("#b1f2eb")) +/// #square(fill: rgb(87, 127, 230)) +/// #square(fill: rgb(25%, 13%, 65%)) +/// ``` +/// +/// ## Parameters +/// - hex: EcoString (positional) +/// The color in hexademical 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. +/// +/// ## Category +/// construct +#[func] +pub fn rgb(args: &mut Args) -> SourceResult { + Ok(Value::Color(if let Some(string) = args.find::>()? { + match RgbaColor::from_str(&string.v) { + Ok(color) => color.into(), + Err(msg) => bail!(string.span, msg), + } + } else { + let Component(r) = args.expect("red component")?; + let Component(g) = args.expect("green component")?; + 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. +struct Component(u8); + +castable! { + Component, + v: i64 => match v { + 0 ..= 255 => Self(v as u8), + _ => Err("must be between 0 and 255")?, + }, + v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) { + Self((v.get() * 255.0).round() as u8) + } else { + Err("must be between 0% and 100%")? + }, +} + +/// # CMYK +/// Create a CMYK color. +/// +/// This is useful if you want to target a specific printer. The conversion +/// to RGB for display preview might differ from how your printer reproduces +/// the color. +/// +/// ## Example +/// ``` +/// #square( +/// fill: cmyk(27%, 0%, 3%, 5%) +/// ) +/// ```` +/// +/// ## 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. +/// +/// ## Category +/// construct +#[func] +pub fn cmyk(args: &mut Args) -> SourceResult { + 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())) +} + +/// A component that must be a ratio. +struct RatioComponent(u8); + +castable! { + RatioComponent, + v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) { + Self((v.get() * 255.0).round() as u8) + } else { + Err("must be between 0% and 100%")? + }, +} + +/// # String +/// Convert a value to a string. +/// +/// - Integers are formatted in base 10. +/// - Floats are formatted in base 10 and never in exponential notation. +/// - From labels the name is extracted. +/// +/// ## Example +/// ``` +/// #str(10) \ +/// #str(2.7) \ +/// #str(1e8) \ +/// #str() +/// ``` +/// +/// ## Parameters +/// - value: ToStr (positional, required) +/// The value that should be converted to a string. +/// +/// ## Category +/// construct +#[func] +pub fn str(args: &mut Args) -> SourceResult { + Ok(Value::Str(args.expect::("value")?.0)) +} + +/// A value that can be cast to a string. +struct ToStr(Str); + +castable! { + ToStr, + v: i64 => Self(format_str!("{}", v)), + v: f64 => Self(format_str!("{}", v)), + v: Label => Self(v.0.into()), + v: Str => Self(v), +} + +/// # Label +/// Create a label from a string. +/// +/// Inserting a label into content attaches it to the closest previous element +/// that is not a space. Then, the element can be [referenced](@ref) and styled +/// through the label. +/// +/// ## Example +/// ``` +/// #show : set text(blue) +/// #show label("b"): set text(red) +/// +/// = Heading +/// *Strong* #label("b") +/// ``` +/// +/// ## Syntax +/// 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. +/// +/// ## Category +/// construct +#[func] +pub fn label(args: &mut Args) -> SourceResult { + Ok(Value::Label(Label(args.expect("string")?))) +} + +/// # Regex +/// Create a regular expression from a string. +/// +/// The result can be used as a show rule +/// [selector](/docs/reference/concepts/#selector) and with +/// [string methods](/docs/reference/concepts/#methods) like `find`, `split`, +/// and `replace`. +/// +/// [See here](https://docs.rs/regex/latest/regex/#syntax) for a specification +/// of the supported syntax. +/// +/// ## Example +/// ``` +/// // Works with show rules. +/// #show regex("\d+"): set text(red) +/// +/// The numbers 1 to 10. +/// +/// // Works with string methods. +/// { "a,b;c" +/// .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("\\\\")}`. +/// +/// ## Category +/// construct +#[func] +pub fn regex(args: &mut Args) -> SourceResult { + let Spanned { v, span } = args.expect::>("regular expression")?; + Ok(Regex::new(&v).at(span)?.into()) +} + +/// # Range +/// Create an array consisting of a sequence of numbers. +/// +/// If you pass just one positional parameter, it is interpreted as the `end` of +/// the range. If you pass two, they describe the `start` and `end` of the +/// range. +/// +/// ## Example +/// ``` +/// #range(5) \ +/// #range(2, 5) \ +/// #range(20, step: 4) \ +/// #range(21, step: 4) \ +/// #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. +/// +/// ## Category +/// construct +#[func] +pub fn range(args: &mut Args) -> SourceResult { + let first = args.expect::("end")?; + let (start, end) = match args.eat::()? { + 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 mut x = start; + let mut seq = vec![]; + + while x.cmp(&end) == 0.cmp(&step) { + seq.push(Value::Int(x)); + x += step; + } + + Ok(Value::Array(Array::from_vec(seq))) +} diff --git a/library/src/compute/create.rs b/library/src/compute/create.rs deleted file mode 100644 index a34c0ba6..00000000 --- a/library/src/compute/create.rs +++ /dev/null @@ -1,392 +0,0 @@ -use std::str::FromStr; - -use typst::model::Regex; - -use crate::prelude::*; - -/// # Integer -/// Convert a value to an integer. -/// -/// - Booleans are converted to `0` or `1`. -/// - Floats are floored to the next 64-bit integer. -/// - Strings are parsed in base 10. -/// -/// ## Example -/// ``` -/// #int(false) \ -/// #int(true) \ -/// #int(2.7) \ -/// { int("27") + int("4") } -/// ``` -/// -/// ## Parameters -/// - value: ToInt (positional, required) -/// The value that should be converted to an integer. -/// -/// ## Category -/// create -#[func] -pub fn int(args: &mut Args) -> SourceResult { - Ok(Value::Int(args.expect::("value")?.0)) -} - -/// A value that can be cast to an integer. -struct ToInt(i64); - -castable! { - ToInt, - v: bool => Self(v as i64), - v: i64 => Self(v), - v: f64 => Self(v as i64), - v: EcoString => Self(v.parse().map_err(|_| "not a valid integer")?), -} - -/// # Float -/// Convert a value to a float. -/// -/// - Booleans are converted to `0.0` or `1.0`. -/// - Integers are converted to the closest 64-bit float. -/// - Strings are parsed in base 10 to the closest 64-bit float. -/// Exponential notation is supported. -/// -/// ## Example -/// ``` -/// #float(false) \ -/// #float(true) \ -/// #float(4) \ -/// #float("2.7") \ -/// #float("1e5") -/// ``` -/// -/// ## Parameters -/// - value: ToFloat (positional, required) -/// The value that should be converted to a float. -/// -/// ## Category -/// create -#[func] -pub fn float(args: &mut Args) -> SourceResult { - Ok(Value::Float(args.expect::("value")?.0)) -} - -/// A value that can be cast to a float. -struct ToFloat(f64); - -castable! { - ToFloat, - v: bool => Self(v as i64 as f64), - v: i64 => Self(v as f64), - v: f64 => Self(v), - v: EcoString => Self(v.parse().map_err(|_| "not a valid float")?), -} - -/// # Luma -/// Create a grayscale color. -/// -/// ## Example -/// ``` -/// #for x in range(250, step: 50) { -/// square(fill: luma(x)) -/// } -/// ``` -/// -/// ## Parameters -/// - gray: Component (positional, required) -/// The gray component. -/// -/// ## Category -/// create -#[func] -pub fn luma(args: &mut Args) -> SourceResult { - let Component(luma) = args.expect("gray component")?; - Ok(Value::Color(LumaColor::new(luma).into())) -} - -/// # RGBA -/// Create an RGB(A) color. -/// -/// The color is specified in the sRGB color space. -/// -/// _Note:_ While you can specify transparent colors and Typst's preview will -/// render them correctly, the PDF export does not handle them properly at the -/// moment. This will be fixed in the future. -/// -/// ## Example -/// ``` -/// #square(fill: rgb("#b1f2eb")) -/// #square(fill: rgb(87, 127, 230)) -/// #square(fill: rgb(25%, 13%, 65%)) -/// ``` -/// -/// ## Parameters -/// - hex: EcoString (positional) -/// The color in hexademical 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. -/// -/// ## Category -/// create -#[func] -pub fn rgb(args: &mut Args) -> SourceResult { - Ok(Value::Color(if let Some(string) = args.find::>()? { - match RgbaColor::from_str(&string.v) { - Ok(color) => color.into(), - Err(msg) => bail!(string.span, msg), - } - } else { - let Component(r) = args.expect("red component")?; - let Component(g) = args.expect("green component")?; - 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. -struct Component(u8); - -castable! { - Component, - v: i64 => match v { - 0 ..= 255 => Self(v as u8), - _ => Err("must be between 0 and 255")?, - }, - v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) { - Self((v.get() * 255.0).round() as u8) - } else { - Err("must be between 0% and 100%")? - }, -} - -/// # CMYK -/// Create a CMYK color. -/// -/// This is useful if you want to target a specific printer. The conversion -/// to RGB for display preview might differ from how your printer reproduces -/// the color. -/// -/// ## Example -/// ``` -/// #square( -/// fill: cmyk(27%, 0%, 3%, 5%) -/// ) -/// ```` -/// -/// ## 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. -/// -/// ## Category -/// create -#[func] -pub fn cmyk(args: &mut Args) -> SourceResult { - 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())) -} - -/// A component that must be a ratio. -struct RatioComponent(u8); - -castable! { - RatioComponent, - v: Ratio => if (0.0 ..= 1.0).contains(&v.get()) { - Self((v.get() * 255.0).round() as u8) - } else { - Err("must be between 0% and 100%")? - }, -} - -/// # String -/// Convert a value to a string. -/// -/// - Integers are formatted in base 10. -/// - Floats are formatted in base 10 and never in exponential notation. -/// - From labels the name is extracted. -/// -/// ## Example -/// ``` -/// #str(10) \ -/// #str(2.7) \ -/// #str(1e8) \ -/// #str() -/// ``` -/// -/// ## Parameters -/// - value: ToStr (positional, required) -/// The value that should be converted to a string. -/// -/// ## Category -/// create -#[func] -pub fn str(args: &mut Args) -> SourceResult { - Ok(Value::Str(args.expect::("value")?.0)) -} - -/// A value that can be cast to a string. -struct ToStr(Str); - -castable! { - ToStr, - v: i64 => Self(format_str!("{}", v)), - v: f64 => Self(format_str!("{}", v)), - v: Label => Self(v.0.into()), - v: Str => Self(v), -} - -/// # Label -/// Create a label from a string. -/// -/// Inserting a label into content attaches it to the closest previous element -/// that is not a space. Then, the element can be [referenced](@ref) and styled -/// through the label. -/// -/// ## Example -/// ``` -/// #show : set text(blue) -/// #show label("b"): set text(red) -/// -/// = Heading -/// *Strong* #label("b") -/// ``` -/// -/// ## Syntax -/// 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. -/// -/// ## Category -/// create -#[func] -pub fn label(args: &mut Args) -> SourceResult { - Ok(Value::Label(Label(args.expect("string")?))) -} - -/// # Regex -/// Create a regular expression from a string. -/// -/// The result can be used as a show rule -/// [selector](/docs/reference/concepts/#selector) and with -/// [string methods](/docs/reference/concepts/#methods) like `find`, `split`, -/// and `replace`. -/// -/// [See here](https://docs.rs/regex/latest/regex/#syntax) for a specification -/// of the supported syntax. -/// -/// ## Example -/// ``` -/// // Works with show rules. -/// #show regex("\d+"): set text(red) -/// -/// The numbers 1 to 10. -/// -/// // Works with string methods. -/// { "a,b;c" -/// .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("\\\\")}`. -/// -/// ## Category -/// create -#[func] -pub fn regex(args: &mut Args) -> SourceResult { - let Spanned { v, span } = args.expect::>("regular expression")?; - Ok(Regex::new(&v).at(span)?.into()) -} - -/// # Range -/// Create an array consisting of a sequence of numbers. -/// -/// If you pass just one positional parameter, it is interpreted as the `end` of -/// the range. If you pass two, they describe the `start` and `end` of the -/// range. -/// -/// ## Example -/// ``` -/// #range(5) \ -/// #range(2, 5) \ -/// #range(20, step: 4) \ -/// #range(21, step: 4) \ -/// #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. -/// -/// ## Category -/// create -#[func] -pub fn range(args: &mut Args) -> SourceResult { - let first = args.expect::("end")?; - let (start, end) = match args.eat::()? { - 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 mut x = start; - let mut seq = vec![]; - - while x.cmp(&end) == 0.cmp(&step) { - seq.push(Value::Int(x)); - x += step; - } - - Ok(Value::Array(Array::from_vec(seq))) -} diff --git a/library/src/compute/mod.rs b/library/src/compute/mod.rs index 70690d44..5cfbe158 100644 --- a/library/src/compute/mod.rs +++ b/library/src/compute/mod.rs @@ -1,13 +1,13 @@ //! Computational functions. mod calc; -mod create; +mod construct; mod data; mod foundations; mod utility; pub use self::calc::*; -pub use self::create::*; +pub use self::construct::*; pub use self::data::*; pub use self::foundations::*; pub use self::utility::*; -- cgit v1.2.3