summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-26 17:14:44 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-26 17:14:44 +0200
commit806d9f0d9ab381500318f3e106b9c20c5eabccb7 (patch)
tree7466967220be358c4fd8c5e26f0c3ca501fafa97
parent22214a1e0a79666caefd486e41828f015878ecb0 (diff)
Pure functions!
-rw-r--r--Cargo.lock83
-rw-r--r--Cargo.toml1
-rw-r--r--src/eval/capture.rs16
-rw-r--r--src/eval/func.rs4
-rw-r--r--src/eval/mod.rs100
-rw-r--r--src/eval/scope.rs138
-rw-r--r--src/library/mod.rs60
-rw-r--r--tests/typ/code/closure.typ13
-rw-r--r--tests/typ/code/return.typ24
-rw-r--r--tests/typ/code/spread.typ10
-rw-r--r--tests/typ/layout/stack-1.typ11
-rw-r--r--tests/typeset.rs6
12 files changed, 186 insertions, 280 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6f54d5f7..2f35c121 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -434,16 +434,6 @@ dependencies = [
]
[[package]]
-name = "lock_api"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -569,29 +559,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
-name = "parking_lot"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-sys",
-]
-
-[[package]]
name = "pathfinder_color"
version = "0.5.0"
source = "git+https://github.com/servo/pathfinder/#038a3476d803fd77a6e66a74117b5b8803a2cb49"
@@ -944,12 +911,6 @@ dependencies = [
]
[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1177,7 +1138,6 @@ dependencies = [
"memmap2",
"miniz_oxide 0.4.4",
"once_cell",
- "parking_lot",
"pdf-writer",
"pico-args",
"pixglyph",
@@ -1421,49 +1381,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
-name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
-
-[[package]]
name = "xi-unicode"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 585e85ee..494a912f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,6 @@ lipsum = { git = "https://github.com/reknih/lipsum", default-features = false }
once_cell = "1"
serde = { version = "1", features = ["derive"] }
typed-arena = "2"
-parking_lot = "0.12"
unscanny = { git = "https://github.com/typst/unscanny" }
regex = "1"
diff --git a/src/eval/capture.rs b/src/eval/capture.rs
index 24fc7abc..1192eaa7 100644
--- a/src/eval/capture.rs
+++ b/src/eval/capture.rs
@@ -1,5 +1,3 @@
-use std::sync::Arc;
-
use super::{Scope, Scopes, Value};
use crate::syntax::ast::{ClosureParam, Expr, Ident, Imports, TypedNode};
use crate::syntax::RedRef;
@@ -28,14 +26,14 @@ impl<'a> CapturesVisitor<'a> {
/// Bind a new internal variable.
pub fn bind(&mut self, ident: Ident) {
- self.internal.top.def_mut(ident.take(), Value::None);
+ self.internal.top.define(ident.take(), Value::None);
}
/// Capture a variable if it isn't internal.
pub fn capture(&mut self, ident: Ident) {
- if self.internal.get(&ident).is_none() {
- if let Some(slot) = self.external.get(&ident) {
- self.captures.def_slot(ident.take(), Arc::clone(slot));
+ if self.internal.get(&ident).is_err() {
+ if let Ok(value) = self.external.get(&ident) {
+ self.captures.define_captured(ident.take(), value.clone());
}
}
}
@@ -145,9 +143,9 @@ mod tests {
let red = RedNode::from_root(green, SourceId::from_raw(0));
let mut scopes = Scopes::new(None);
- scopes.top.def_const("x", 0);
- scopes.top.def_const("y", 0);
- scopes.top.def_const("z", 0);
+ scopes.top.define("x", 0);
+ scopes.top.define("y", 0);
+ scopes.top.define("z", 0);
let mut visitor = CapturesVisitor::new(&scopes);
visitor.visit(red.as_ref());
diff --git a/src/eval/func.rs b/src/eval/func.rs
index f15549dc..12dbfb2e 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -206,7 +206,7 @@ impl Closure {
// Parse the arguments according to the parameter list.
for (param, default) in &self.params {
- scopes.top.def_mut(param, match default {
+ scopes.top.define(param, match default {
None => args.expect::<Value>(param)?,
Some(default) => {
args.named::<Value>(param)?.unwrap_or_else(|| default.clone())
@@ -216,7 +216,7 @@ impl Closure {
// Put the remaining arguments into the sink.
if let Some(sink) = &self.sink {
- scopes.top.def_mut(sink, args.take());
+ scopes.top.define(sink, args.take());
}
// Determine the route inside the closure.
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index d76257fa..db7595f9 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -31,7 +31,6 @@ pub use value::*;
use std::collections::BTreeMap;
-use parking_lot::{MappedRwLockWriteGuard, RwLockWriteGuard};
use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{At, StrResult, Trace, Tracepoint, TypResult};
@@ -165,7 +164,7 @@ fn eval_markup(
}
MarkupNode::Expr(Expr::Wrap(wrap)) => {
let tail = eval_markup(vm, nodes)?;
- vm.scopes.top.def_mut(wrap.binding().take(), tail);
+ vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)?.display()
}
@@ -354,10 +353,7 @@ impl Eval for Ident {
type Output = Value;
fn eval(&self, vm: &mut Machine) -> TypResult<Self::Output> {
- match vm.scopes.get(self) {
- Some(slot) => Ok(slot.read().clone()),
- None => bail!(self.span(), "unknown variable"),
- }
+ vm.scopes.get(self).cloned().at(self.span())
}
}
@@ -404,7 +400,7 @@ fn eval_code(
}
Expr::Wrap(wrap) => {
let tail = eval_code(vm, exprs)?;
- vm.scopes.top.def_mut(wrap.binding().take(), tail);
+ vm.scopes.top.define(wrap.binding().take(), tail);
wrap.body().eval(vm)?
}
@@ -565,8 +561,7 @@ impl BinaryExpr {
op: fn(Value, Value) -> StrResult<Value>,
) -> TypResult<Value> {
let rhs = self.rhs().eval(vm)?;
- let lhs = self.lhs();
- let mut location = lhs.access(vm)?;
+ let location = self.lhs().access(vm)?;
let lhs = std::mem::take(&mut *location);
*location = op(lhs, rhs).at(self.span())?;
Ok(Value::None)
@@ -748,7 +743,7 @@ impl Eval for LetExpr {
Some(expr) => expr.eval(vm)?,
None => Value::None,
};
- vm.scopes.top.def_mut(self.binding().take(), value);
+ vm.scopes.top.define(self.binding().take(), value);
Ok(Value::None)
}
}
@@ -860,7 +855,7 @@ impl Eval for ForExpr {
(for ($($binding:ident => $value:ident),*) in $iter:expr) => {{
#[allow(unused_parens)]
for ($($value),*) in $iter {
- $(vm.scopes.top.def_mut(&$binding, $value);)*
+ $(vm.scopes.top.define(&$binding, $value);)*
let body = self.body();
let value = body.eval(vm)?;
@@ -937,14 +932,14 @@ impl Eval for ImportExpr {
match self.imports() {
Imports::Wildcard => {
- for (var, slot) in module.scope.iter() {
- vm.scopes.top.def_mut(var, slot.read().clone());
+ for (var, value) in module.scope.iter() {
+ vm.scopes.top.define(var, value.clone());
}
}
Imports::Items(idents) => {
for ident in idents {
- if let Some(slot) = module.scope.get(&ident) {
- vm.scopes.top.def_mut(ident.take(), slot.read().clone());
+ if let Some(value) = module.scope.get(&ident) {
+ vm.scopes.top.define(ident.take(), value.clone());
} else {
bail!(ident.span(), "unresolved import");
}
@@ -1028,11 +1023,11 @@ impl Eval for ReturnExpr {
/// Access an expression mutably.
pub trait Access {
/// Access the value.
- fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<Location<'a>>;
+ fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<&'a mut Value>;
}
impl Access for Expr {
- fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<Location<'a>> {
+ fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<&'a mut Value> {
match self {
Expr::Ident(v) => v.access(vm),
Expr::FieldAccess(v) => v.access(vm),
@@ -1043,68 +1038,35 @@ impl Access for Expr {
}
impl Access for Ident {
- fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<Location<'a>> {
- match vm.scopes.get(self) {
- Some(slot) => match slot.try_write() {
- Some(guard) => Ok(RwLockWriteGuard::map(guard, |v| v)),
- None => bail!(self.span(), "cannot mutate a constant"),
- },
- None => bail!(self.span(), "unknown variable"),
- }
+ fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<&'a mut Value> {
+ vm.scopes.get_mut(self).at(self.span())
}
}
impl Access for FieldAccess {
- fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<Location<'a>> {
- let guard = self.object().access(vm)?;
- try_map(guard, |value| {
- Ok(match value {
- Value::Dict(dict) => dict.get_mut(self.field().take()),
- v => bail!(
- self.object().span(),
- "expected dictionary, found {}",
- v.type_name(),
- ),
- })
+ fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<&'a mut Value> {
+ Ok(match self.object().access(vm)? {
+ Value::Dict(dict) => dict.get_mut(self.field().take()),
+ v => bail!(
+ self.object().span(),
+ "expected dictionary, found {}",
+ v.type_name(),
+ ),
})
}
}
impl Access for FuncCall {
- fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<Location<'a>> {
+ fn access<'a>(&self, vm: &'a mut Machine) -> TypResult<&'a mut Value> {
let args = self.args().eval(vm)?;
- let guard = self.callee().access(vm)?;
- try_map(guard, |value| {
- Ok(match value {
- Value::Array(array) => {
- array.get_mut(args.into_index()?).at(self.span())?
- }
- Value::Dict(dict) => dict.get_mut(args.into_key()?),
- v => bail!(
- self.callee().span(),
- "expected collection, found {}",
- v.type_name(),
- ),
- })
+ Ok(match self.callee().access(vm)? {
+ Value::Array(array) => array.get_mut(args.into_index()?).at(self.span())?,
+ Value::Dict(dict) => dict.get_mut(args.into_key()?),
+ v => bail!(
+ self.callee().span(),
+ "expected collection, found {}",
+ v.type_name(),
+ ),
})
}
}
-
-/// A mutable location.
-type Location<'a> = MappedRwLockWriteGuard<'a, Value>;
-
-/// Map a reader-writer lock with a function.
-fn try_map<F>(location: Location, f: F) -> TypResult<Location>
-where
- F: FnOnce(&mut Value) -> TypResult<&mut Value>,
-{
- let mut error = None;
- MappedRwLockWriteGuard::try_map(location, |value| match f(value) {
- Ok(value) => Some(value),
- Err(err) => {
- error = Some(err);
- None
- }
- })
- .map_err(|_| error.unwrap())
-}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index 8a0b8165..29778a90 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -1,18 +1,11 @@
use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter};
-use std::hash::{Hash, Hasher};
-use std::iter;
-use std::sync::Arc;
-
-use parking_lot::RwLock;
+use std::hash::Hash;
use super::{Args, Func, Machine, Node, Value};
-use crate::diag::TypResult;
+use crate::diag::{StrResult, TypResult};
use crate::util::EcoString;
-/// A slot where a variable is stored.
-pub type Slot = Arc<RwLock<Value>>;
-
/// A stack of scopes.
#[derive(Debug, Default, Clone)]
pub struct Scopes<'a> {
@@ -42,46 +35,43 @@ impl<'a> Scopes<'a> {
self.top = self.scopes.pop().expect("no pushed scope");
}
- /// Look up the slot of a variable.
- pub fn get(&self, var: &str) -> Option<&Slot> {
- iter::once(&self.top)
+ /// Try to access a variable immutably.
+ pub fn get(&self, var: &str) -> StrResult<&Value> {
+ Ok(std::iter::once(&self.top)
.chain(self.scopes.iter().rev())
.chain(self.base.into_iter())
.find_map(|scope| scope.get(var))
+ .ok_or("unknown variable")?)
}
-}
-/// A map from variable names to variable slots.
-#[derive(Default, Clone)]
-pub struct Scope {
- /// The mapping from names to slots.
- values: BTreeMap<EcoString, Slot>,
+ /// Try to access a variable mutably.
+ pub fn get_mut(&mut self, var: &str) -> StrResult<&mut Value> {
+ std::iter::once(&mut self.top)
+ .chain(&mut self.scopes.iter_mut().rev())
+ .find_map(|scope| scope.get_mut(var))
+ .ok_or_else(|| {
+ if self.base.map_or(false, |base| base.get(var).is_some()) {
+ "cannot mutate a constant"
+ } else {
+ "unknown variable"
+ }
+ })?
+ }
}
+/// A map from binding names to values.
+#[derive(Default, Clone, Hash)]
+pub struct Scope(BTreeMap<EcoString, Slot>);
+
impl Scope {
/// Create a new empty scope.
pub fn new() -> Self {
Self::default()
}
- /// Define a constant variable with a value.
- pub fn def_const(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) {
- let cell = RwLock::new(value.into());
-
- // Make it impossible to write to this value again.
- std::mem::forget(cell.read());
-
- self.values.insert(var.into(), Arc::new(cell));
- }
-
- /// Define a mutable variable with a value.
- pub fn def_mut(&mut self, var: impl Into<EcoString>, value: impl Into<Value>) {
- self.values.insert(var.into(), Arc::new(RwLock::new(value.into())));
- }
-
- /// Define a variable with a slot.
- pub fn def_slot(&mut self, var: impl Into<EcoString>, slot: Slot) {
- self.values.insert(var.into(), slot);
+ /// Bind a value to a name.
+ pub fn define(&mut self, name: impl Into<EcoString>, value: impl Into<Value>) {
+ self.0.insert(name.into(), Slot::new(value.into(), Kind::Normal));
}
/// Define a function through a native rust function.
@@ -90,32 +80,36 @@ impl Scope {
name: &'static str,
func: fn(&mut Machine, &mut Args) -> TypResult<Value>,
) {
- self.def_const(name, Func::from_fn(name, func));
+ self.define(name, Func::from_fn(name, func));
}
/// Define a function through a native rust node.
pub fn def_node<T: Node>(&mut self, name: &'static str) {
- self.def_const(name, Func::from_node::<T>(name));
+ self.define(name, Func::from_node::<T>(name));
}
- /// Look up the value of a variable.
- pub fn get(&self, var: &str) -> Option<&Slot> {
- self.values.get(var)
+ /// Define a captured, immutable binding.
+ pub fn define_captured(
+ &mut self,
+ var: impl Into<EcoString>,
+ value: impl Into<Value>,
+ ) {
+ self.0.insert(var.into(), Slot::new(value.into(), Kind::Captured));
}
- /// Iterate over all definitions.
- pub fn iter(&self) -> impl Iterator<Item = (&str, &Slot)> {
- self.values.iter().map(|(k, v)| (k.as_str(), v))
+ /// Try to access a variable immutably.
+ pub fn get(&self, var: &str) -> Option<&Value> {
+ self.0.get(var).map(Slot::read)
}
-}
-impl Hash for Scope {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.values.len().hash(state);
- for (name, value) in self.values.iter() {
- name.hash(state);
- value.read().hash(state);
- }
+ /// Try to access a variable mutably.
+ pub fn get_mut(&mut self, var: &str) -> Option<StrResult<&mut Value>> {
+ self.0.get_mut(var).map(Slot::write)
+ }
+
+ /// Iterate over all definitions.
+ pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
+ self.0.iter().map(|(k, v)| (k.as_str(), v.read()))
}
}
@@ -123,7 +117,45 @@ impl Debug for Scope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Scope ")?;
f.debug_map()
- .entries(self.values.iter().map(|(k, v)| (k, v.read())))
+ .entries(self.0.iter().map(|(k, v)| (k, v.read())))
.finish()
}
}
+
+/// A slot where a variable is stored.
+#[derive(Clone, Hash)]
+struct Slot {
+ /// The stored value.
+ value: Value,
+ /// The kind of slot, determines how the value can be accessed.
+ kind: Kind,
+}
+
+/// The different kinds of slots.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+enum Kind {
+ /// A normal, mutable binding.
+ Normal,
+ /// A captured copy of another variable.
+ Captured,
+}
+
+impl Slot {
+ /// Create a new constant slot.
+ fn new(value: Value, kind: Kind) -> Self {
+ Self { value, kind }
+ }
+
+ /// Read the variable.
+ fn read(&self) -> &Value {
+ &self.value
+ }
+
+ /// Try to write to the variable.
+ fn write(&mut self) -> StrResult<&mut Value> {
+ match self.kind {
+ Kind::Normal => Ok(&mut self.value),
+ Kind::Captured => Err("cannot mutate a captured variable")?,
+ }
+ }
+}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index cf5d6e64..27658189 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -96,38 +96,38 @@ pub fn new() -> Scope {
std.def_fn("lorem", utility::lorem);
// Predefined colors.
- std.def_const("black", Color::BLACK);
- std.def_const("gray", Color::GRAY);
- std.def_const("silver", Color::SILVER);
- std.def_const("white", Color::WHITE);
- std.def_const("navy", Color::NAVY);
- std.def_const("blue", Color::BLUE);
- std.def_const("aqua", Color::AQUA);
- std.def_const("teal", Color::TEAL);
- std.def_const("eastern", Color::EASTERN);
- std.def_const("purple", Color::PURPLE);
- std.def_const("fuchsia", Color::FUCHSIA);
- std.def_const("maroon", Color::MAROON);
- std.def_const("red", Color::RED);
- std.def_const("orange", Color::ORANGE);
- std.def_const("yellow", Color::YELLOW);
- std.def_const("olive", Color::OLIVE);
- std.def_const("green", Color::GREEN);
- std.def_const("lime", Color::LIME);
+ std.define("black", Color::BLACK);
+ std.define("gray", Color::GRAY);
+ std.define("silver", Color::SILVER);
+ std.define("white", Color::WHITE);
+ std.define("navy", Color::NAVY);
+ std.define("blue", Color::BLUE);
+ std.define("aqua", Color::AQUA);
+ std.define("teal", Color::TEAL);
+ std.define("eastern", Color::EASTERN);
+ std.define("purple", Color::PURPLE);
+ std.define("fuchsia", Color::FUCHSIA);
+ std.define("maroon", Color::MAROON);
+ std.define("red", Color::RED);
+ std.define("orange", Color::ORANGE);
+ std.define("yellow", Color::YELLOW);
+ std.define("olive", Color::OLIVE);
+ std.define("green", Color::GREEN);
+ std.define("lime", Color::LIME);
// Other constants.
- std.def_const("ltr", Dir::LTR);
- std.def_const("rtl", Dir::RTL);
- std.def_const("ttb", Dir::TTB);
- std.def_const("btt", Dir::BTT);
- std.def_const("start", RawAlign::Start);
- std.def_const("end", RawAlign::End);
- std.def_const("left", RawAlign::Specific(Align::Left));
- std.def_const("center", RawAlign::Specific(Align::Center));
- std.def_const("right", RawAlign::Specific(Align::Right));
- std.def_const("top", RawAlign::Specific(Align::Top));
- std.def_const("horizon", RawAlign::Specific(Align::Horizon));
- std.def_const("bottom", RawAlign::Specific(Align::Bottom));
+ std.define("ltr", Dir::LTR);
+ std.define("rtl", Dir::RTL);
+ std.define("ttb", Dir::TTB);
+ std.define("btt", Dir::BTT);
+ std.define("start", RawAlign::Start);
+ std.define("end", RawAlign::End);
+ std.define("left", RawAlign::Specific(Align::Left));
+ std.define("center", RawAlign::Specific(Align::Center));
+ std.define("right", RawAlign::Specific(Align::Right));
+ std.define("top", RawAlign::Specific(Align::Top));
+ std.define("horizon", RawAlign::Specific(Align::Horizon));
+ std.define("bottom", RawAlign::Specific(Align::Bottom));
std
}
diff --git a/tests/typ/code/closure.typ b/tests/typ/code/closure.typ
index aa7bc5b9..e9389e13 100644
--- a/tests/typ/code/closure.typ
+++ b/tests/typ/code/closure.typ
@@ -31,7 +31,7 @@
---
// Capture environment.
{
- let mark = "?"
+ let mark = "!"
let greet = {
let hi = "Hi"
name => {
@@ -39,9 +39,10 @@
}
}
- test(greet("Typst"), "Hi, Typst?")
+ test(greet("Typst"), "Hi, Typst!")
- mark = "!"
+ // Changing the captured variable after the closure definition has no effect.
+ mark = "?"
test(greet("Typst"), "Hi, Typst!")
}
@@ -71,12 +72,12 @@
// For loop bindings.
{
let v = (1, 2, 3)
- let s = 0
let f() = {
+ let s = 0
for v in v { s += v }
+ s
}
- f()
- test(s, 6)
+ test(f(), 6)
}
---
diff --git a/tests/typ/code/return.typ b/tests/typ/code/return.typ
index 8db99a81..0eea394e 100644
--- a/tests/typ/code/return.typ
+++ b/tests/typ/code/return.typ
@@ -55,30 +55,28 @@
---
// Test that the expression is evaluated to the end.
-#let y = 1
-#let identity(x, ..rest) = x
-#let f(x) = {
- identity(
- ..return,
- x + 1,
- y = 2,
- )
+#let sum(..args) = {
+ let s = 0
+ for v in args.positional() {
+ s += v
+ }
+ s
+}
+
+#let f() = {
+ sum(..return, 1, 2, 3)
"nope"
}
-#test(f(1), 2)
-#test(y, 2)
+#test(f(), 6)
---
// Test value return from content.
#let x = 3
#let f() = [
Hello 😀
- { x = 1 }
#return "nope"
- { x = 2 }
World
]
#test(f(), "nope")
-#test(x, 1)
diff --git a/tests/typ/code/spread.typ b/tests/typ/code/spread.typ
index 86dbfd98..ff661ead 100644
--- a/tests/typ/code/spread.typ
+++ b/tests/typ/code/spread.typ
@@ -23,16 +23,14 @@
}
---
-// Test storing arguments in a variable.
+// Test doing things with arguments.
{
- let args
- let save(..sink) = {
- args = sink
+ let save(..args) = {
+ test(type(args), "arguments")
+ test(repr(args), "(1, 2, three: true)")
}
save(1, 2, three: true)
- test(type(args), "arguments")
- test(repr(args), "(1, 2, three: true)")
}
---
diff --git a/tests/typ/layout/stack-1.typ b/tests/typ/layout/stack-1.typ
index 19a00de5..0ecbe246 100644
--- a/tests/typ/layout/stack-1.typ
+++ b/tests/typ/layout/stack-1.typ
@@ -7,13 +7,14 @@
30pt, 50%, 20pt, 100%,
)
-#let shaded = {
- let v = 0%
- let next() = { v += 10%; rgb(v, v, v) }
- w => rect(width: w, height: 10pt, fill: next())
+#let shaded(i, w) = {
+ let v = (i + 1) * 10%
+ rect(width: w, height: 10pt, fill: rgb(v, v, v))
}
-#let items = for w in widths { (align(right, shaded(w)),) }
+#let items = for i, w in widths {
+ (align(right, shaded(i, w)),)
+}
#set page(width: 50pt, margins: 0pt)
#stack(dir: btt, ..items)
diff --git a/tests/typeset.rs b/tests/typeset.rs
index b334ae9a..72bdb431 100644
--- a/tests/typeset.rs
+++ b/tests/typeset.rs
@@ -70,10 +70,10 @@ fn main() {
);
styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));
- // Hook up an assert function into the global scope.
+ // Hook up two more colors and an assert function into the global scope.
let mut std = typst::library::new();
- std.def_const("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF));
- std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
+ std.define("conifer", RgbaColor::new(0x9f, 0xEB, 0x52, 0xFF));
+ std.define("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
std.def_fn("test", move |_, args| {
let lhs = args.expect::<Value>("left-hand side")?;
let rhs = args.expect::<Value>("right-hand side")?;