summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--library/src/base/calc.rs54
-rw-r--r--library/src/base/color.rs64
-rw-r--r--library/src/base/create.rs149
-rw-r--r--library/src/base/mod.rs13
-rw-r--r--library/src/base/numbering.rs (renamed from library/src/shared/numbering.rs)13
-rw-r--r--library/src/base/string.rs58
-rw-r--r--library/src/lib.rs22
-rw-r--r--library/src/shared/mod.rs2
-rw-r--r--library/src/structure/list.rs2
-rw-r--r--src/model/methods.rs14
-rw-r--r--tests/ref/base/numbering.pngbin10634 -> 7112 bytes
-rw-r--r--tests/ref/base/string.pngbin10614 -> 0 bytes
-rw-r--r--tests/typ/base/color.typ2
-rw-r--r--tests/typ/base/numbering.typ13
-rw-r--r--tests/typ/base/string.typ21
-rw-r--r--tests/typ/structure/enum.typ4
16 files changed, 202 insertions, 229 deletions
diff --git a/library/src/base/calc.rs b/library/src/base/calc.rs
index db40df06..3541e08c 100644
--- a/library/src/base/calc.rs
+++ b/library/src/base/calc.rs
@@ -2,35 +2,6 @@ use std::cmp::Ordering;
use crate::prelude::*;
-/// Convert a value to an integer.
-pub fn int(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Int(match v {
- Value::Bool(v) => v as i64,
- Value::Int(v) => v,
- Value::Float(v) => v as i64,
- Value::Str(v) => match v.parse() {
- Ok(v) => v,
- Err(_) => bail!(span, "invalid integer"),
- },
- v => bail!(span, "cannot convert {} to integer", v.type_name()),
- }))
-}
-
-/// Convert a value to a float.
-pub fn float(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Float(match v {
- Value::Int(v) => v as f64,
- Value::Float(v) => v,
- Value::Str(v) => match v.parse() {
- Ok(v) => v,
- Err(_) => bail!(span, "invalid float"),
- },
- v => bail!(span, "cannot convert {} to float", v.type_name()),
- }))
-}
-
/// The absolute value of a numeric value.
pub fn abs(_: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect("numeric value")?;
@@ -115,28 +86,3 @@ pub fn mod_(_: &Vm, args: &mut Args) -> SourceResult<Value> {
Ok(Value::Float(a % b))
}
-
-/// Create a sequence of numbers.
-pub fn range(_: &Vm, args: &mut Args) -> SourceResult<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 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/base/color.rs b/library/src/base/color.rs
deleted file mode 100644
index 2db41ebf..00000000
--- a/library/src/base/color.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use std::str::FromStr;
-
-use crate::prelude::*;
-
-/// Create a grayscale color.
-pub fn luma(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Component(luma) = args.expect("gray component")?;
- Ok(Value::Color(LumaColor::new(luma).into()))
-}
-
-/// Create an RGB(A) color.
-pub fn rgb(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- Ok(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),
- }
- } 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()
- }))
-}
-
-/// Create a CMYK color.
-pub fn cmyk(_: &Vm, 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()))
-}
-
-/// An integer or ratio component.
-struct Component(u8);
-
-castable! {
- Component,
- Expected: "integer or ratio",
- Value::Int(v) => match v {
- 0 ..= 255 => Self(v as u8),
- _ => Err("must be between 0 and 255")?,
- },
- Value::Ratio(v) => if (0.0 ..= 1.0).contains(&v.get()) {
- Self((v.get() * 255.0).round() as u8)
- } else {
- Err("must be between 0% and 100%")?
- },
-}
-
-/// A component that must be a ratio.
-struct RatioComponent(u8);
-
-castable! {
- RatioComponent,
- Expected: "ratio",
- Value::Ratio(v) => if (0.0 ..= 1.0).contains(&v.get()) {
- Self((v.get() * 255.0).round() as u8)
- } else {
- Err("must be between 0% and 100%")?
- },
-}
diff --git a/library/src/base/create.rs b/library/src/base/create.rs
new file mode 100644
index 00000000..be8e822f
--- /dev/null
+++ b/library/src/base/create.rs
@@ -0,0 +1,149 @@
+use std::str::FromStr;
+
+use typst::model::Regex;
+
+use crate::prelude::*;
+
+/// Convert a value to an integer.
+pub fn int(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect("value")?;
+ Ok(Value::Int(match v {
+ Value::Bool(v) => v as i64,
+ Value::Int(v) => v,
+ Value::Float(v) => v as i64,
+ Value::Str(v) => match v.parse() {
+ Ok(v) => v,
+ Err(_) => bail!(span, "invalid integer"),
+ },
+ v => bail!(span, "cannot convert {} to integer", v.type_name()),
+ }))
+}
+
+/// Convert a value to a float.
+pub fn float(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect("value")?;
+ Ok(Value::Float(match v {
+ Value::Int(v) => v as f64,
+ Value::Float(v) => v,
+ Value::Str(v) => match v.parse() {
+ Ok(v) => v,
+ Err(_) => bail!(span, "invalid float"),
+ },
+ v => bail!(span, "cannot convert {} to float", v.type_name()),
+ }))
+}
+
+/// Create a grayscale color.
+pub fn luma(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let Component(luma) = args.expect("gray component")?;
+ Ok(Value::Color(LumaColor::new(luma).into()))
+}
+
+/// Create an RGB(A) color.
+pub fn rgb(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ Ok(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),
+ }
+ } 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()
+ }))
+}
+
+/// Create a CMYK color.
+pub fn cmyk(_: &Vm, 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()))
+}
+
+/// An integer or ratio component.
+struct Component(u8);
+
+castable! {
+ Component,
+ Expected: "integer or ratio",
+ Value::Int(v) => match v {
+ 0 ..= 255 => Self(v as u8),
+ _ => Err("must be between 0 and 255")?,
+ },
+ Value::Ratio(v) => if (0.0 ..= 1.0).contains(&v.get()) {
+ Self((v.get() * 255.0).round() as u8)
+ } else {
+ Err("must be between 0% and 100%")?
+ },
+}
+
+/// A component that must be a ratio.
+struct RatioComponent(u8);
+
+castable! {
+ RatioComponent,
+ Expected: "ratio",
+ Value::Ratio(v) => if (0.0 ..= 1.0).contains(&v.get()) {
+ Self((v.get() * 255.0).round() as u8)
+ } else {
+ Err("must be between 0% and 100%")?
+ },
+}
+
+/// Convert a value to a string.
+pub fn str(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect("value")?;
+ Ok(Value::Str(match v {
+ Value::Int(v) => format_str!("{}", v),
+ Value::Float(v) => format_str!("{}", v),
+ Value::Label(label) => label.0.into(),
+ Value::Str(v) => v,
+ v => bail!(span, "cannot convert {} to string", v.type_name()),
+ }))
+}
+
+/// Create a blind text string.
+pub fn lorem(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let words: usize = args.expect("number of words")?;
+ Ok(Value::Str(lipsum::lipsum(words).into()))
+}
+
+/// Create a label from a string.
+pub fn label(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ Ok(Value::Label(Label(args.expect("string")?)))
+}
+
+/// Create a regular expression from a string.
+pub fn regex(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
+ Ok(Regex::new(&v).at(span)?.into())
+}
+
+/// Create an array consisting of a sequence of numbers.
+pub fn range(_: &Vm, args: &mut Args) -> SourceResult<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 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/base/mod.rs b/library/src/base/mod.rs
index 86ebd666..501edd71 100644
--- a/library/src/base/mod.rs
+++ b/library/src/base/mod.rs
@@ -1,14 +1,14 @@
//! Foundational functions.
mod calc;
-mod color;
+mod create;
mod data;
-mod string;
+mod numbering;
pub use self::calc::*;
-pub use self::color::*;
+pub use self::create::*;
pub use self::data::*;
-pub use self::string::*;
+pub use self::numbering::*;
use comemo::Track;
use typst::model::{self, Route, Vm};
@@ -21,6 +21,11 @@ pub fn type_(_: &Vm, args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<Value>("value")?.type_name().into())
}
+/// The string representation of a value.
+pub fn repr(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ Ok(args.expect::<Value>("value")?.repr().into())
+}
+
/// Ensure that a condition is fulfilled.
pub fn assert(_: &Vm, args: &mut Args) -> SourceResult<Value> {
let Spanned { v, span } = args.expect::<Spanned<bool>>("condition")?;
diff --git a/library/src/shared/numbering.rs b/library/src/base/numbering.rs
index 739edafe..ea45fbc6 100644
--- a/library/src/shared/numbering.rs
+++ b/library/src/base/numbering.rs
@@ -1,9 +1,16 @@
use std::str::FromStr;
-use typst::model::{castable, Value};
-use typst::util::{format_eco, EcoString};
use unscanny::Scanner;
+use crate::prelude::*;
+
+/// Apply a numbering pattern to a number.
+pub fn numbering(_: &Vm, args: &mut Args) -> SourceResult<Value> {
+ let number = args.expect::<usize>("number")?;
+ let pattern = args.expect::<NumberingPattern>("pattern")?;
+ Ok(Value::Str(pattern.apply(number).into()))
+}
+
/// A numbering pattern for lists or headings.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct NumberingPattern {
@@ -129,7 +136,7 @@ impl NumberingKind {
return '-'.into();
}
- const SYMBOLS: &[char] = &['*', '†', '‡', '§', '‖', '¶'];
+ const SYMBOLS: &[char] = &['*', '†', '‡', '§', '¶', '‖'];
let symbol = SYMBOLS[(n - 1) % SYMBOLS.len()];
let amount = ((n - 1) / SYMBOLS.len()) + 1;
std::iter::repeat(symbol).take(amount).collect()
diff --git a/library/src/base/string.rs b/library/src/base/string.rs
deleted file mode 100644
index 9c3b9562..00000000
--- a/library/src/base/string.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use typst::model::Regex;
-
-use crate::prelude::*;
-use crate::shared::NumberingKind;
-
-/// The string representation of a value.
-pub fn repr(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- Ok(args.expect::<Value>("value")?.repr().into())
-}
-
-/// Convert a value to a string.
-pub fn str(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect("value")?;
- Ok(Value::Str(match v {
- Value::Int(v) => format_str!("{}", v),
- Value::Float(v) => format_str!("{}", v),
- Value::Label(label) => label.0.into(),
- Value::Str(v) => v,
- v => bail!(span, "cannot convert {} to string", v.type_name()),
- }))
-}
-
-/// Create a label from a string.
-pub fn label(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- Ok(Value::Label(Label(args.expect("string")?)))
-}
-
-/// Create blind text.
-pub fn lorem(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let words: usize = args.expect("number of words")?;
- Ok(Value::Str(lipsum::lipsum(words).into()))
-}
-
-/// Create a regular expression.
-pub fn regex(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
- Ok(Regex::new(&v).at(span)?.into())
-}
-
-/// Converts an integer into one or multiple letters.
-pub fn letter(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- numbered(NumberingKind::Letter, args)
-}
-
-/// Converts an integer into a roman numeral.
-pub fn roman(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- numbered(NumberingKind::Roman, args)
-}
-
-/// Convert a number into a symbol.
-pub fn symbol(_: &Vm, args: &mut Args) -> SourceResult<Value> {
- numbered(NumberingKind::Symbol, args)
-}
-
-fn numbered(numbering: NumberingKind, args: &mut Args) -> SourceResult<Value> {
- let n = args.expect::<usize>("non-negative integer")?;
- Ok(Value::Str(numbering.apply(n).into()))
-}
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 11baacc3..6107cf42 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -88,28 +88,26 @@ fn scope() -> Scope {
// Base.
std.def_fn("type", base::type_);
+ std.def_fn("repr", base::repr);
std.def_fn("assert", base::assert);
std.def_fn("eval", base::eval);
std.def_fn("int", base::int);
std.def_fn("float", base::float);
- std.def_fn("abs", base::abs);
- std.def_fn("min", base::min);
- std.def_fn("max", base::max);
- std.def_fn("even", base::even);
- std.def_fn("odd", base::odd);
- std.def_fn("mod", base::mod_);
- std.def_fn("range", base::range);
std.def_fn("luma", base::luma);
std.def_fn("rgb", base::rgb);
std.def_fn("cmyk", base::cmyk);
- std.def_fn("repr", base::repr);
std.def_fn("str", base::str);
+ std.def_fn("lorem", base::lorem);
std.def_fn("label", base::label);
std.def_fn("regex", base::regex);
- std.def_fn("letter", base::letter);
- std.def_fn("roman", base::roman);
- std.def_fn("symbol", base::symbol);
- std.def_fn("lorem", base::lorem);
+ std.def_fn("range", base::range);
+ std.def_fn("numbering", base::numbering);
+ std.def_fn("abs", base::abs);
+ std.def_fn("min", base::min);
+ std.def_fn("max", base::max);
+ std.def_fn("even", base::even);
+ std.def_fn("odd", base::odd);
+ std.def_fn("mod", base::mod_);
std.def_fn("csv", base::csv);
std.def_fn("json", base::json);
std.def_fn("xml", base::xml);
diff --git a/library/src/shared/mod.rs b/library/src/shared/mod.rs
index 55522190..f54241cf 100644
--- a/library/src/shared/mod.rs
+++ b/library/src/shared/mod.rs
@@ -2,8 +2,6 @@
mod behave;
mod ext;
-mod numbering;
pub use behave::*;
pub use ext::*;
-pub use numbering::*;
diff --git a/library/src/structure/list.rs b/library/src/structure/list.rs
index d1727087..462e0c32 100644
--- a/library/src/structure/list.rs
+++ b/library/src/structure/list.rs
@@ -1,6 +1,6 @@
+use crate::base::NumberingPattern;
use crate::layout::{BlockNode, GridNode, HNode, Spacing, TrackSizing};
use crate::prelude::*;
-use crate::shared::NumberingPattern;
use crate::text::{ParNode, SpaceNode, TextNode};
/// An unordered (bulleted) or ordered (numbered) list.
diff --git a/src/model/methods.rs b/src/model/methods.rs
index c5c12ed2..fc33bea9 100644
--- a/src/model/methods.rs
+++ b/src/model/methods.rs
@@ -17,6 +17,13 @@ pub fn call(
let missing = || Err(missing_method(name, method)).at(span);
let output = match value {
+ Value::Color(color) => match method {
+ "lighten" => Value::Color(color.lighten(args.expect("amount")?)),
+ "darken" => Value::Color(color.darken(args.expect("amount")?)),
+ "negate" => Value::Color(color.negate()),
+ _ => return missing(),
+ },
+
Value::Str(string) => match method {
"len" => Value::Int(string.len() as i64),
"slice" => {
@@ -108,13 +115,6 @@ pub fn call(
_ => return missing(),
},
- Value::Color(color) => match method {
- "lighten" => Value::Color(color.lighten(args.expect("amount")?)),
- "darken" => Value::Color(color.darken(args.expect("amount")?)),
- "negate" => Value::Color(color.negate()),
- _ => return missing(),
- },
-
_ => return missing(),
};
diff --git a/tests/ref/base/numbering.png b/tests/ref/base/numbering.png
index d4d575d9..aa34de29 100644
--- a/tests/ref/base/numbering.png
+++ b/tests/ref/base/numbering.png
Binary files differ
diff --git a/tests/ref/base/string.png b/tests/ref/base/string.png
deleted file mode 100644
index 02184316..00000000
--- a/tests/ref/base/string.png
+++ /dev/null
Binary files differ
diff --git a/tests/typ/base/color.typ b/tests/typ/base/color.typ
index 96d76063..aecd86b8 100644
--- a/tests/typ/base/color.typ
+++ b/tests/typ/base/color.typ
@@ -1,4 +1,4 @@
-// Test color creation functions.
+// Test color creation functions and modification methods.
// Ref: false
---
diff --git a/tests/typ/base/numbering.typ b/tests/typ/base/numbering.typ
new file mode 100644
index 00000000..200850bc
--- /dev/null
+++ b/tests/typ/base/numbering.typ
@@ -0,0 +1,13 @@
+// Test integrated numbering patterns.
+
+---
+#for i in range(9) {
+ numbering(i, "* and ")
+ numbering(i, "I")
+ [ for #i]
+ parbreak()
+}
+
+---
+// Error: 12-14 must be at least zero
+#numbering(-1, "1")
diff --git a/tests/typ/base/string.typ b/tests/typ/base/string.typ
index 3104a3ea..e724f563 100644
--- a/tests/typ/base/string.typ
+++ b/tests/typ/base/string.typ
@@ -136,26 +136,5 @@
#test(upper("Ελλάδα"), "ΕΛΛΆΔΑ")
---
-// Test integrated lower, upper and symbols.
-// Ref: true
-
-#upper("Abc 8")
-#upper[def]
-
-#lower("SCREAMING MUST BE SILENCED in " + roman(1672) + " years")
-
-#for i in range(9) {
- symbol(i)
- [ and ]
- roman(i)
- [ for #i]
- parbreak()
-}
-
----
// Error: 8-9 expected string or content, found integer
#upper(1)
-
----
-// Error: 9-11 must be at least zero
-#symbol(-1)
diff --git a/tests/typ/structure/enum.typ b/tests/typ/structure/enum.typ
index 40aad625..d4c30385 100644
--- a/tests/typ/structure/enum.typ
+++ b/tests/typ/structure/enum.typ
@@ -12,7 +12,7 @@
---
// Test automatic numbering in summed content.
#for i in range(5) {
- [+ #roman(1 + i)]
+ [+ #numbering(1 + i, "I")]
}
---
@@ -42,7 +42,7 @@
start: 4,
spacing: 0.65em - 3pt,
tight: false,
- label: n => text(fill: (red, green, blue)(mod(n, 3)), [#upper(letter(n))]),
+ label: n => text(fill: (red, green, blue)(mod(n, 3)), numbering(n, "A")),
[Red], [Green], [Blue],
)