Reorganize library base

This commit is contained in:
Laurenz 2022-11-26 16:59:20 +01:00
parent 9db6e533cd
commit 0579fd4409
16 changed files with 202 additions and 229 deletions

View File

@ -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)))
}

View File

@ -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%")?
},
}

149
library/src/base/create.rs Normal file
View File

@ -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)))
}

View File

@ -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")?;

View File

@ -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()

View File

@ -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()))
}

View File

@ -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("luma", base::luma);
std.def_fn("rgb", base::rgb);
std.def_fn("cmyk", base::cmyk);
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("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("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("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("csv", base::csv);
std.def_fn("json", base::json);
std.def_fn("xml", base::xml);

View File

@ -2,8 +2,6 @@
mod behave;
mod ext;
mod numbering;
pub use behave::*;
pub use ext::*;
pub use numbering::*;

View File

@ -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.

View File

@ -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(),
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,4 +1,4 @@
// Test color creation functions.
// Test color creation functions and modification methods.
// Ref: false
---

View File

@ -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")

View File

@ -135,27 +135,6 @@
#test(upper(memes), "ARE MEMES GREAT?")
#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)

View File

@ -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],
)