summaryrefslogtreecommitdiff
path: root/src
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 /src
parentc38d72383d2068361d635d6c1c78ba97aa917801 (diff)
New #[func] macro
Diffstat (limited to 'src')
-rw-r--r--src/eval/cast.rs26
-rw-r--r--src/eval/func.rs160
-rw-r--r--src/eval/mod.rs5
-rw-r--r--src/eval/scope.rs7
-rw-r--r--src/ide/complete.rs6
-rw-r--r--src/model/content.rs17
-rw-r--r--src/model/mod.rs2
-rw-r--r--src/model/styles.rs2
8 files changed, 133 insertions, 92 deletions
diff --git a/src/eval/cast.rs b/src/eval/cast.rs
index 840ceb05..806f7e92 100644
--- a/src/eval/cast.rs
+++ b/src/eval/cast.rs
@@ -1,6 +1,6 @@
pub use typst_macros::{cast_from_value, cast_to_value};
-use std::num::NonZeroUsize;
+use std::num::{NonZeroI64, NonZeroUsize};
use std::ops::Add;
use ecow::EcoString;
@@ -128,6 +128,20 @@ cast_to_value! {
}
cast_from_value! {
+ NonZeroI64,
+ int: i64 => int.try_into()
+ .map_err(|_| if int <= 0 {
+ "number must be positive"
+ } else {
+ "number too large"
+ })?,
+}
+
+cast_to_value! {
+ v: NonZeroI64 => Value::Int(v.get())
+}
+
+cast_from_value! {
char,
string: Str => {
let mut chars = string.chars();
@@ -211,6 +225,16 @@ impl<T: Into<Value>> From<Vec<T>> for Value {
}
}
+/// A container for a variadic argument.
+pub trait Variadics {
+ /// The contained type.
+ type Inner;
+}
+
+impl<T> Variadics for Vec<T> {
+ type Inner = T;
+}
+
/// Describes a possible value for a cast.
#[derive(Debug, Clone, Hash)]
pub enum CastInfo {
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 6f98e316..79ae142c 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -6,10 +6,14 @@ use std::sync::Arc;
use comemo::{Prehashed, Track, Tracked, TrackedMut};
use ecow::EcoString;
+use once_cell::sync::Lazy;
-use super::{Args, CastInfo, Dict, Eval, Flow, Route, Scope, Scopes, Tracer, Value, Vm};
+use super::{
+ cast_to_value, Args, CastInfo, Dict, Eval, Flow, Route, Scope, Scopes, Tracer, Value,
+ Vm,
+};
use crate::diag::{bail, SourceResult, StrResult};
-use crate::model::{Node, NodeId, Selector, StyleMap};
+use crate::model::{Content, NodeId, Selector, StyleMap};
use crate::syntax::ast::{self, AstNode, Expr};
use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::util::hash128;
@@ -22,8 +26,10 @@ pub struct Func(Arc<Prehashed<Repr>>, Span);
/// The different kinds of function representations.
#[derive(Hash)]
enum Repr {
- /// A native rust function.
- Native(Native),
+ /// A native Rust function.
+ Native(NativeFunc),
+ /// A function for a node.
+ Node(NodeFunc),
/// A user-defined closure.
Closure(Closure),
/// A nested function with pre-applied arguments.
@@ -31,50 +37,11 @@ enum Repr {
}
impl Func {
- /// Create a new function from a type that can be turned into a function.
- pub fn from_type<T: FuncType>(name: &'static str) -> Self {
- T::create_func(name)
- }
-
- /// Create a new function from a native rust function.
- pub fn from_fn(
- func: fn(&Vm, &mut Args) -> SourceResult<Value>,
- info: FuncInfo,
- ) -> Self {
- Self(
- Arc::new(Prehashed::new(Repr::Native(Native {
- func,
- set: None,
- node: None,
- info,
- }))),
- Span::detached(),
- )
- }
-
- /// Create a new function from a native rust node.
- pub fn from_node<T: Node>(mut info: FuncInfo) -> Self {
- info.params.extend(T::params());
- Self(
- Arc::new(Prehashed::new(Repr::Native(Native {
- func: |vm, args| T::construct(vm, args).map(Value::Content),
- set: Some(T::set),
- node: Some(NodeId::of::<T>()),
- info,
- }))),
- Span::detached(),
- )
- }
-
- /// Create a new function from a closure.
- pub(super) fn from_closure(closure: Closure, span: Span) -> Self {
- Self(Arc::new(Prehashed::new(Repr::Closure(closure))), span)
- }
-
/// The name of the function.
pub fn name(&self) -> Option<&str> {
match &**self.0 {
Repr::Native(native) => Some(native.info.name),
+ Repr::Node(node) => Some(node.info.name),
Repr::Closure(closure) => closure.name.as_deref(),
Repr::With(func, _) => func.name(),
}
@@ -84,6 +51,7 @@ impl Func {
pub fn info(&self) -> Option<&FuncInfo> {
match &**self.0 {
Repr::Native(native) => Some(&native.info),
+ Repr::Node(node) => Some(&node.info),
Repr::With(func, _) => func.info(),
_ => None,
}
@@ -119,6 +87,11 @@ impl Func {
args.finish()?;
Ok(value)
}
+ Repr::Node(node) => {
+ let value = (node.construct)(vm, &mut args)?;
+ args.finish()?;
+ Ok(Value::Content(value))
+ }
Repr::Closure(closure) => {
// Determine the route inside the closure.
let fresh = Route::new(closure.location);
@@ -172,8 +145,8 @@ impl Func {
/// Execute the function's set rule and return the resulting style map.
pub fn set(&self, mut args: Args) -> SourceResult<StyleMap> {
Ok(match &**self.0 {
- Repr::Native(Native { set: Some(set), .. }) => {
- let styles = set(&mut args)?;
+ Repr::Node(node) => {
+ let styles = (node.set)(&mut args)?;
args.finish()?;
styles
}
@@ -183,13 +156,13 @@ impl Func {
/// Create a selector for this function's node type.
pub fn select(&self, fields: Option<Dict>) -> StrResult<Selector> {
- match **self.0 {
- Repr::Native(Native { node: Some(id), .. }) => {
- if id == item!(text_id) {
+ match &**self.0 {
+ Repr::Node(node) => {
+ if node.id == item!(text_id) {
Err("to select text, please use a string or regex instead")?;
}
- Ok(Selector::Node(id, fields))
+ Ok(Selector::Node(node.id, fields))
}
_ => Err("this function is not selectable")?,
}
@@ -211,32 +184,75 @@ impl PartialEq for Func {
}
}
-/// Types that can be turned into functions.
-pub trait FuncType {
- /// Create a function with the given name from this type.
- fn create_func(name: &'static str) -> Func;
+impl From<Repr> for Func {
+ fn from(repr: Repr) -> Self {
+ Self(Arc::new(Prehashed::new(repr)), Span::detached())
+ }
}
-/// A function defined by a native rust function or node.
-struct Native {
- /// The function pointer.
- func: fn(&Vm, &mut Args) -> SourceResult<Value>,
- /// The set rule.
- set: Option<fn(&mut Args) -> SourceResult<StyleMap>>,
- /// The id of the node to customize with this function's show rule.
- node: Option<NodeId>,
- /// Documentation of the function.
- info: FuncInfo,
+/// A native Rust function.
+pub struct NativeFunc {
+ /// The function's implementation.
+ pub func: fn(&Vm, &mut Args) -> SourceResult<Value>,
+ /// Details about the function.
+ pub info: Lazy<FuncInfo>,
}
-impl Hash for Native {
+impl Hash for NativeFunc {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.func as usize).hash(state);
- self.set.map(|set| set as usize).hash(state);
- self.node.hash(state);
}
}
+impl From<NativeFunc> for Func {
+ fn from(native: NativeFunc) -> Self {
+ Repr::Native(native).into()
+ }
+}
+
+cast_to_value! {
+ v: NativeFunc => Value::Func(v.into())
+}
+
+impl<F> From<F> for Value
+where
+ F: Fn() -> NativeFunc,
+{
+ fn from(f: F) -> Self {
+ f().into()
+ }
+}
+
+/// A function defined by a native Rust node.
+pub struct NodeFunc {
+ /// The node's id.
+ pub id: NodeId,
+ /// The node's constructor.
+ pub construct: fn(&Vm, &mut Args) -> SourceResult<Content>,
+ /// The node's set rule.
+ pub set: fn(&mut Args) -> SourceResult<StyleMap>,
+ /// Details about the function.
+ pub info: Lazy<FuncInfo>,
+}
+
+impl Hash for NodeFunc {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.id.hash(state);
+ (self.construct as usize).hash(state);
+ (self.set as usize).hash(state);
+ }
+}
+
+impl From<NodeFunc> for Func {
+ fn from(node: NodeFunc) -> Self {
+ Repr::Node(node).into()
+ }
+}
+
+cast_to_value! {
+ v: NodeFunc => Value::Func(v.into())
+}
+
/// Details about a function.
#[derive(Debug, Clone)]
pub struct FuncInfo {
@@ -375,6 +391,16 @@ impl Closure {
}
}
+impl From<Closure> for Func {
+ fn from(closure: Closure) -> Self {
+ Repr::Closure(closure).into()
+ }
+}
+
+cast_to_value! {
+ v: Closure => Value::Func(v.into())
+}
+
/// A visitor that determines which variables to capture for a closure.
pub(super) struct CapturesVisitor<'a> {
external: &'a Scopes<'a>,
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 0e0828e3..fcfda263 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -20,6 +20,9 @@ mod ops;
mod scope;
mod symbol;
+#[doc(hidden)]
+pub use once_cell::sync::Lazy;
+
pub use self::args::*;
pub use self::array::*;
pub use self::cast::*;
@@ -1152,7 +1155,7 @@ impl Eval for ast::Closure {
body: self.body(),
};
- Ok(Value::Func(Func::from_closure(closure, self.span())))
+ Ok(Value::Func(Func::from(closure).spanned(self.span())))
}
}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index f6bd2164..d1590063 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -4,7 +4,7 @@ use std::hash::Hash;
use ecow::EcoString;
-use super::{Func, FuncType, Library, Value};
+use super::{Library, Value};
use crate::diag::StrResult;
/// A stack of scopes.
@@ -96,11 +96,6 @@ impl Scope {
self.0.insert(name, Slot::new(value.into(), Kind::Normal));
}
- /// Define a function through a native rust function.
- pub fn def_func<T: FuncType>(&mut self, name: &'static str) {
- self.define(name, Func::from_type::<T>(name));
- }
-
/// Define a captured, immutable binding.
pub fn define_captured(
&mut self,
diff --git a/src/ide/complete.rs b/src/ide/complete.rs
index f5eece93..2e810ee9 100644
--- a/src/ide/complete.rs
+++ b/src/ide/complete.rs
@@ -627,10 +627,6 @@ fn param_completions(
}
}
- if callee.as_str() == "text" {
- ctx.font_completions();
- }
-
if ctx.before.ends_with(',') {
ctx.enrich(" ", "");
}
@@ -653,7 +649,7 @@ fn named_param_value_completions(
ctx.cast_completions(&param.cast);
- if callee.as_str() == "text" && name == "family" {
+ if callee.as_str() == "text" && name == "font" {
ctx.font_completions();
}
diff --git a/src/model/content.rs b/src/model/content.rs
index 6b4f5e5d..d845ce1e 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -9,7 +9,7 @@ use ecow::{EcoString, EcoVec};
use super::{node, Guard, Recipe, Style, StyleMap};
use crate::diag::{SourceResult, StrResult};
-use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm};
+use crate::eval::{cast_from_value, Args, Cast, NodeFunc, Value, Vm};
use crate::syntax::Span;
use crate::World;
@@ -330,12 +330,10 @@ impl Sum for Content {
#[node]
pub struct StyledNode {
/// The styles.
- #[positional]
#[required]
pub map: StyleMap,
/// The styled content.
- #[positional]
#[required]
pub body: Content,
}
@@ -353,7 +351,6 @@ cast_from_value! {
/// Category: special
#[node]
pub struct SequenceNode {
- #[positional]
#[variadic]
pub children: Vec<Content>,
}
@@ -370,14 +367,14 @@ impl Debug for Label {
/// A constructable, stylable content node.
pub trait Node: Construct + Set + Sized + 'static {
- /// Pack a node into type-erased content.
- fn pack(self) -> Content;
-
/// The node's ID.
fn id() -> NodeId;
- /// List the fields of the node.
- fn params() -> Vec<ParamInfo>;
+ /// Pack a node into type-erased content.
+ fn pack(self) -> Content;
+
+ /// The node's function.
+ fn func() -> NodeFunc;
}
/// A unique identifier for a node.
@@ -425,6 +422,7 @@ pub struct NodeMeta {
pub vtable: fn(of: TypeId) -> Option<*const ()>,
}
+/// A node's constructor function.
pub trait Construct {
/// Construct a node from the arguments.
///
@@ -433,6 +431,7 @@ pub trait Construct {
fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content>;
}
+/// A node's set rule.
pub trait Set {
/// Parse relevant arguments into style properties for this node.
fn set(args: &mut Args) -> SourceResult<StyleMap>;
diff --git a/src/model/mod.rs b/src/model/mod.rs
index 07329e3f..6015c365 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -11,6 +11,4 @@ pub use self::realize::*;
pub use self::styles::*;
pub use self::typeset::*;
-#[doc(hidden)]
-pub use once_cell;
pub use typst_macros::node;
diff --git a/src/model/styles.rs b/src/model/styles.rs
index 0b74e162..3239bb17 100644
--- a/src/model/styles.rs
+++ b/src/model/styles.rs
@@ -86,7 +86,7 @@ impl Debug for StyleMap {
}
}
-/// A single style property, recipe or barrier.
+/// A single style property or recipe.
#[derive(Clone, Hash)]
pub enum Style {
/// A style property originating from a set rule or constructor.