summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-03-12 14:24:24 +0100
committerLaurenz <laurmaedje@gmail.com>2022-03-12 14:24:24 +0100
commit2890a156d27c02a101137bf01dc2046597110bd1 (patch)
treec6bdeb48242c0fbd5b5e13120ca3c8f502d41b75 /src/eval
parent5ac7eb3860ebd3247f6486c227e816894cb8fd91 (diff)
Remove classes and improve naming
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/args.rs202
-rw-r--r--src/eval/class.rs138
-rw-r--r--src/eval/content.rs14
-rw-r--r--src/eval/func.rs282
-rw-r--r--src/eval/mod.rs31
-rw-r--r--src/eval/scope.rs17
-rw-r--r--src/eval/styles.rs64
-rw-r--r--src/eval/value.rs12
8 files changed, 343 insertions, 417 deletions
diff --git a/src/eval/args.rs b/src/eval/args.rs
new file mode 100644
index 00000000..67da9865
--- /dev/null
+++ b/src/eval/args.rs
@@ -0,0 +1,202 @@
+use std::fmt::{self, Debug, Formatter, Write};
+
+use super::{Cast, Value};
+use crate::diag::{At, TypResult};
+use crate::syntax::{Span, Spanned};
+use crate::util::EcoString;
+
+/// Evaluated arguments to a function.
+#[derive(Clone, PartialEq, Hash)]
+pub struct Args {
+ /// The span of the whole argument list.
+ pub span: Span,
+ /// The positional and named arguments.
+ pub items: Vec<Arg>,
+}
+
+/// An argument to a function call: `12` or `draw: false`.
+#[derive(Clone, PartialEq, Hash)]
+pub struct Arg {
+ /// The span of the whole argument.
+ pub span: Span,
+ /// The name of the argument (`None` for positional arguments).
+ pub name: Option<EcoString>,
+ /// The value of the argument.
+ pub value: Spanned<Value>,
+}
+
+impl Args {
+ /// Create positional arguments from a span and values.
+ pub fn from_values(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
+ Self {
+ span,
+ items: values
+ .into_iter()
+ .map(|value| Arg {
+ span,
+ name: None,
+ value: Spanned::new(value, span),
+ })
+ .collect(),
+ }
+ }
+
+ /// Consume and cast the first positional argument.
+ ///
+ /// Returns a `missing argument: {what}` error if no positional argument is
+ /// left.
+ pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ match self.eat()? {
+ Some(v) => Ok(v),
+ None => bail!(self.span, "missing argument: {}", what),
+ }
+ }
+
+ /// Consume and cast the first positional argument if there is one.
+ pub fn eat<T>(&mut self) -> TypResult<Option<T>>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ for (i, slot) in self.items.iter().enumerate() {
+ if slot.name.is_none() {
+ let value = self.items.remove(i).value;
+ let span = value.span;
+ return T::cast(value).at(span).map(Some);
+ }
+ }
+ Ok(None)
+ }
+
+ /// Find and consume the first castable positional argument.
+ pub fn find<T>(&mut self) -> TypResult<Option<T>>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ for (i, slot) in self.items.iter().enumerate() {
+ if slot.name.is_none() && T::is(&slot.value) {
+ let value = self.items.remove(i).value;
+ let span = value.span;
+ return T::cast(value).at(span).map(Some);
+ }
+ }
+ Ok(None)
+ }
+
+ /// Find and consume all castable positional arguments.
+ pub fn all<T>(&mut self) -> TypResult<Vec<T>>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ let mut list = vec![];
+ while let Some(value) = self.find()? {
+ list.push(value);
+ }
+ Ok(list)
+ }
+
+ /// Cast and remove the value for the given named argument, returning an
+ /// error if the conversion fails.
+ pub fn named<T>(&mut self, name: &str) -> TypResult<Option<T>>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ // We don't quit once we have a match because when multiple matches
+ // exist, we want to remove all of them and use the last one.
+ let mut i = 0;
+ let mut found = None;
+ while i < self.items.len() {
+ if self.items[i].name.as_deref() == Some(name) {
+ let value = self.items.remove(i).value;
+ let span = value.span;
+ found = Some(T::cast(value).at(span)?);
+ } else {
+ i += 1;
+ }
+ }
+ Ok(found)
+ }
+
+ /// Same as named, but with fallback to find.
+ pub fn named_or_find<T>(&mut self, name: &str) -> TypResult<Option<T>>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ match self.named(name)? {
+ Some(value) => Ok(Some(value)),
+ None => self.find(),
+ }
+ }
+
+ /// Take out all arguments into a new instance.
+ pub fn take(&mut self) -> Self {
+ Self {
+ span: self.span,
+ items: std::mem::take(&mut self.items),
+ }
+ }
+
+ /// Return an "unexpected argument" error if there is any remaining
+ /// argument.
+ pub fn finish(self) -> TypResult<()> {
+ if let Some(arg) = self.items.first() {
+ bail!(arg.span, "unexpected argument");
+ }
+ Ok(())
+ }
+
+ /// Reinterpret these arguments as actually being an array index.
+ pub fn into_index(self) -> TypResult<i64> {
+ self.into_castable("index")
+ }
+
+ /// Reinterpret these arguments as actually being a dictionary key.
+ pub fn into_key(self) -> TypResult<EcoString> {
+ self.into_castable("key")
+ }
+
+ /// Reinterpret these arguments as actually being a single castable thing.
+ fn into_castable<T: Cast>(self, what: &str) -> TypResult<T> {
+ let mut iter = self.items.into_iter();
+ let value = match iter.next() {
+ Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
+ None => {
+ bail!(self.span, "missing {}", what);
+ }
+ Some(Arg { name: Some(_), span, .. }) => {
+ bail!(span, "named pair is not allowed here");
+ }
+ };
+
+ if let Some(arg) = iter.next() {
+ bail!(arg.span, "only one {} is allowed", what);
+ }
+
+ Ok(value)
+ }
+}
+
+impl Debug for Args {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.write_char('(')?;
+ for (i, arg) in self.items.iter().enumerate() {
+ arg.fmt(f)?;
+ if i + 1 < self.items.len() {
+ f.write_str(", ")?;
+ }
+ }
+ f.write_char(')')
+ }
+}
+
+impl Debug for Arg {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ if let Some(name) = &self.name {
+ f.write_str(name)?;
+ f.write_str(": ")?;
+ }
+ Debug::fmt(&self.value.v, f)
+ }
+}
diff --git a/src/eval/class.rs b/src/eval/class.rs
deleted file mode 100644
index 05191667..00000000
--- a/src/eval/class.rs
+++ /dev/null
@@ -1,138 +0,0 @@
-use std::any::TypeId;
-use std::fmt::{self, Debug, Formatter, Write};
-use std::hash::{Hash, Hasher};
-
-use super::{Args, Content, Func, StyleMap, Value};
-use crate::diag::TypResult;
-use crate::Context;
-
-/// A class of nodes.
-///
-/// You can [construct] an instance of a class in Typst code by invoking the
-/// class as a callable. This always produces a content value, but not
-/// necessarily a simple inline or block node. For example, the `text`
-/// constructor does not actually create a [`TextNode`]. Instead it applies
-/// styling to whatever content you pass in and returns it structurally
-/// unchanged.
-///
-/// The arguments you can pass to a class constructor fall into two categories:
-/// Data that is inherent to the instance (e.g. the text/content of a heading)
-/// and style properties (e.g. the fill color of a heading). As the latter are
-/// often shared by many instances throughout a document, they can also be
-/// conveniently configured through class's [`set`] rule. Then, they apply to
-/// all nodes that are instantiated into the content block where the `set` was
-/// executed.
-///
-/// ```typst
-/// This is normal.
-/// [
-/// #set text(weight: "bold")
-/// #set heading(fill: blue)
-/// = A blue & bold heading
-/// ]
-/// Normal again.
-/// ```
-///
-/// [construct]: Self::construct
-/// [`TextNode`]: crate::library::text::TextNode
-/// [`set`]: Self::set
-#[derive(Clone)]
-pub struct Class {
- name: &'static str,
- id: TypeId,
- construct: fn(&mut Context, &mut Args) -> TypResult<Value>,
- set: fn(&mut Args, &mut StyleMap) -> TypResult<()>,
-}
-
-impl Class {
- /// Create a new class.
- pub fn new<T>(name: &'static str) -> Self
- where
- T: Construct + Set + 'static,
- {
- Self {
- name,
- id: TypeId::of::<T>(),
- construct: |ctx, args| {
- let mut styles = StyleMap::new();
- T::set(args, &mut styles)?;
- let content = T::construct(ctx, args)?;
- Ok(Value::Content(content.styled_with_map(styles.scoped())))
- },
- set: T::set,
- }
- }
-
- /// The name of the class.
- pub fn name(&self) -> &'static str {
- self.name
- }
-
- /// The type id of the class.
- pub fn id(&self) -> TypeId {
- self.id
- }
-
- /// Return the class constructor as a function.
- pub fn constructor(&self) -> Func {
- Func::native(self.name, self.construct)
- }
-
- /// Construct an instance of the class.
- ///
- /// This parses both property and data arguments (in this order), styles the
- /// content constructed from the data with the style properties and wraps it
- /// in a value.
- pub fn construct(&self, ctx: &mut Context, mut args: Args) -> TypResult<Value> {
- let value = (self.construct)(ctx, &mut args)?;
- args.finish()?;
- Ok(value)
- }
-
- /// Execute the class's set rule.
- ///
- /// This parses property arguments and return the resulting styles.
- pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
- let mut styles = StyleMap::new();
- (self.set)(&mut args, &mut styles)?;
- args.finish()?;
- Ok(styles)
- }
-}
-
-impl Debug for Class {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_str("<class ")?;
- f.write_str(self.name())?;
- f.write_char('>')
- }
-}
-
-impl PartialEq for Class {
- fn eq(&self, other: &Self) -> bool {
- self.name == other.name
- }
-}
-
-impl Hash for Class {
- fn hash<H: Hasher>(&self, state: &mut H) {
- (self.construct as usize).hash(state);
- (self.set as usize).hash(state);
- }
-}
-
-/// Construct an instance of a class.
-pub trait Construct {
- /// Construct an instance of this class from the arguments.
- ///
- /// This is passed only the arguments that remain after execution of the
- /// class's set rule.
- fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
-}
-
-/// Set style properties of a class.
-pub trait Set {
- /// Parse the arguments and insert style properties of this class into the
- /// given style map.
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()>;
-}
diff --git a/src/eval/content.rs b/src/eval/content.rs
index b6d60957..5f8c5861 100644
--- a/src/eval/content.rs
+++ b/src/eval/content.rs
@@ -6,11 +6,11 @@ use std::ops::{Add, AddAssign};
use typed_arena::Arena;
use super::{
- CollapsingBuilder, Interruption, Layout, LayoutNode, Property, Show, ShowNode,
- StyleMap, StyleVecBuilder,
+ CollapsingBuilder, Interruption, Key, Layout, LayoutNode, Show, ShowNode, StyleMap,
+ StyleVecBuilder,
};
use crate::diag::StrResult;
-use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, SpacingKind};
+use crate::library::layout::{FlowChild, FlowNode, PageNode, PlaceNode, Spacing};
use crate::library::prelude::*;
use crate::library::structure::{ListItem, ListKind, ListNode, ORDERED, UNORDERED};
use crate::library::text::{DecoNode, ParChild, ParNode, TextNode, UNDERLINE};
@@ -42,7 +42,7 @@ pub enum Content {
/// A line break.
Linebreak,
/// Horizontal spacing.
- Horizontal(SpacingKind),
+ Horizontal(Spacing),
/// Plain text.
Text(EcoString),
/// An inline-level node.
@@ -52,7 +52,7 @@ pub enum Content {
/// A column break.
Colbreak,
/// Vertical spacing.
- Vertical(SpacingKind),
+ Vertical(Spacing),
/// A block-level node.
Block(LayoutNode),
/// An item in an unordered list.
@@ -102,7 +102,7 @@ impl Content {
}
/// Style this content with a single style property.
- pub fn styled<P: Property>(mut self, key: P, value: P::Value) -> Self {
+ pub fn styled<P: Key>(mut self, key: P, value: P::Value) -> Self {
if let Self::Styled(styled) = &mut self {
if let Some((_, map)) = Arc::get_mut(styled) {
if !map.has_scoped() {
@@ -465,7 +465,7 @@ impl<'a> Builder<'a> {
})
.unwrap_or_default()
{
- par.push_front(ParChild::Spacing(SpacingKind::Linear(indent)))
+ par.push_front(ParChild::Spacing(Spacing::Linear(indent)))
}
let node = ParNode(par).pack();
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 451dcbbb..3eae453e 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -1,11 +1,12 @@
+use std::any::TypeId;
use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::{Cast, Control, Eval, Scope, Scopes, Value};
-use crate::diag::{At, TypResult};
+use super::{Args, Content, Control, Eval, Scope, Scopes, StyleMap, Value};
+use crate::diag::{StrResult, TypResult};
use crate::syntax::ast::Expr;
-use crate::syntax::{Span, Spanned};
+use crate::syntax::Span;
use crate::util::EcoString;
use crate::Context;
@@ -26,18 +27,50 @@ enum Repr {
impl Func {
/// Create a new function from a native rust function.
- pub fn native(
+ pub fn from_fn(
name: &'static str,
func: fn(&mut Context, &mut Args) -> TypResult<Value>,
) -> Self {
- Self(Arc::new(Repr::Native(Native { name, func })))
+ Self(Arc::new(Repr::Native(Native {
+ name,
+ func,
+ set: None,
+ show: None,
+ })))
+ }
+
+ /// Create a new function from a native rust node.
+ pub fn from_node<T: Node>(name: &'static str) -> Self {
+ Self(Arc::new(Repr::Native(Native {
+ name,
+ func: |ctx, args| {
+ let styles = T::set(args)?;
+ let content = T::construct(ctx, args)?;
+ Ok(Value::Content(content.styled_with_map(styles.scoped())))
+ },
+ set: Some(T::set),
+ show: if T::SHOWABLE {
+ Some(|recipe, span| {
+ let mut styles = StyleMap::new();
+ styles.set_recipe(TypeId::of::<T>(), recipe, span);
+ styles
+ })
+ } else {
+ None
+ },
+ })))
}
/// Create a new function from a closure.
- pub fn closure(closure: Closure) -> Self {
+ pub fn from_closure(closure: Closure) -> Self {
Self(Arc::new(Repr::Closure(closure)))
}
+ /// Apply the given arguments to the function.
+ pub fn with(self, args: Args) -> Self {
+ Self(Arc::new(Repr::With(self, args)))
+ }
+
/// The name of the function.
pub fn name(&self) -> Option<&str> {
match self.0.as_ref() {
@@ -61,9 +94,22 @@ impl Func {
Ok(value)
}
- /// Apply the given arguments to the function.
- pub fn with(self, args: Args) -> Self {
- Self(Arc::new(Repr::With(self, args)))
+ /// Execute the function's set rule.
+ pub fn set(&self, mut args: Args) -> TypResult<StyleMap> {
+ let styles = match self.0.as_ref() {
+ Repr::Native(Native { set: Some(set), .. }) => set(&mut args)?,
+ _ => StyleMap::new(),
+ };
+ args.finish()?;
+ Ok(styles)
+ }
+
+ /// Execute the function's show rule.
+ pub fn show(&self, recipe: Func, span: Span) -> StrResult<StyleMap> {
+ match self.0.as_ref() {
+ Repr::Native(Native { show: Some(show), .. }) => Ok(show(recipe, span)),
+ _ => Err("this function cannot be customized with show")?,
+ }
}
}
@@ -90,14 +136,36 @@ struct Native {
pub name: &'static str,
/// The function pointer.
pub func: fn(&mut Context, &mut Args) -> TypResult<Value>,
+ /// The set rule.
+ pub set: Option<fn(&mut Args) -> TypResult<StyleMap>>,
+ /// The show rule.
+ pub show: Option<fn(Func, Span) -> StyleMap>,
}
impl Hash for Native {
fn hash<H: Hasher>(&self, state: &mut H) {
+ self.name.hash(state);
(self.func as usize).hash(state);
+ self.set.map(|set| set as usize).hash(state);
+ self.show.map(|show| show as usize).hash(state);
}
}
+/// A constructable, stylable content node.
+pub trait Node: 'static {
+ /// Whether this node can be customized through a show rule.
+ const SHOWABLE: bool;
+
+ /// Construct a node from the arguments.
+ ///
+ /// This is passed only the arguments that remain after execution of the
+ /// node's set rule.
+ fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
+
+ /// Parse the arguments into style properties for this node.
+ fn set(args: &mut Args) -> TypResult<StyleMap>;
+}
+
/// A user-defined closure.
#[derive(Hash)]
pub struct Closure {
@@ -146,199 +214,3 @@ impl Closure {
Ok(value)
}
}
-
-/// Evaluated arguments to a function.
-#[derive(Clone, PartialEq, Hash)]
-pub struct Args {
- /// The span of the whole argument list.
- pub span: Span,
- /// The positional and named arguments.
- pub items: Vec<Arg>,
-}
-
-/// An argument to a function call: `12` or `draw: false`.
-#[derive(Clone, PartialEq, Hash)]
-pub struct Arg {
- /// The span of the whole argument.
- pub span: Span,
- /// The name of the argument (`None` for positional arguments).
- pub name: Option<EcoString>,
- /// The value of the argument.
- pub value: Spanned<Value>,
-}
-
-impl Args {
- /// Create positional arguments from a span and values.
- pub fn from_values(span: Span, values: impl IntoIterator<Item = Value>) -> Self {
- Self {
- span,
- items: values
- .into_iter()
- .map(|value| Arg {
- span,
- name: None,
- value: Spanned::new(value, span),
- })
- .collect(),
- }
- }
-
- /// Consume and cast the first positional argument.
- ///
- /// Returns a `missing argument: {what}` error if no positional argument is
- /// left.
- pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
- where
- T: Cast<Spanned<Value>>,
- {
- match self.eat()? {
- Some(v) => Ok(v),
- None => bail!(self.span, "missing argument: {}", what),
- }
- }
-
- /// Consume and cast the first positional argument if there is one.
- pub fn eat<T>(&mut self) -> TypResult<Option<T>>
- where
- T: Cast<Spanned<Value>>,
- {
- for (i, slot) in self.items.iter().enumerate() {
- if slot.name.is_none() {
- let value = self.items.remove(i).value;
- let span = value.span;
- return T::cast(value).at(span).map(Some);
- }
- }
- Ok(None)
- }
-
- /// Find and consume the first castable positional argument.
- pub fn find<T>(&mut self) -> TypResult<Option<T>>
- where
- T: Cast<Spanned<Value>>,
- {
- for (i, slot) in self.items.iter().enumerate() {
- if slot.name.is_none() && T::is(&slot.value) {
- let value = self.items.remove(i).value;
- let span = value.span;
- return T::cast(value).at(span).map(Some);
- }
- }
- Ok(None)
- }
-
- /// Find and consume all castable positional arguments.
- pub fn all<T>(&mut self) -> TypResult<Vec<T>>
- where
- T: Cast<Spanned<Value>>,
- {
- let mut list = vec![];
- while let Some(value) = self.find()? {
- list.push(value);
- }
- Ok(list)
- }
-
- /// Cast and remove the value for the given named argument, returning an
- /// error if the conversion fails.
- pub fn named<T>(&mut self, name: &str) -> TypResult<Option<T>>
- where
- T: Cast<Spanned<Value>>,
- {
- // We don't quit once we have a match because when multiple matches
- // exist, we want to remove all of them and use the last one.
- let mut i = 0;
- let mut found = None;
- while i < self.items.len() {
- if self.items[i].name.as_deref() == Some(name) {
- let value = self.items.remove(i).value;
- let span = value.span;
- found = Some(T::cast(value).at(span)?);
- } else {
- i += 1;
- }
- }
- Ok(found)
- }
-
- /// Same as named, but with fallback to find.
- pub fn named_or_find<T>(&mut self, name: &str) -> TypResult<Option<T>>
- where
- T: Cast<Spanned<Value>>,
- {
- match self.named(name)? {
- Some(value) => Ok(Some(value)),
- None => self.find(),
- }
- }
-
- /// Take out all arguments into a new instance.
- pub fn take(&mut self) -> Self {
- Self {
- span: self.span,
- items: std::mem::take(&mut self.items),
- }
- }
-
- /// Return an "unexpected argument" error if there is any remaining
- /// argument.
- pub fn finish(self) -> TypResult<()> {
- if let Some(arg) = self.items.first() {
- bail!(arg.span, "unexpected argument");
- }
- Ok(())
- }
-
- /// Reinterpret these arguments as actually being an array index.
- pub fn into_index(self) -> TypResult<i64> {
- self.into_castable("index")
- }
-
- /// Reinterpret these arguments as actually being a dictionary key.
- pub fn into_key(self) -> TypResult<EcoString> {
- self.into_castable("key")
- }
-
- /// Reinterpret these arguments as actually being a single castable thing.
- fn into_castable<T: Cast>(self, what: &str) -> TypResult<T> {
- let mut iter = self.items.into_iter();
- let value = match iter.next() {
- Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?,
- None => {
- bail!(self.span, "missing {}", what);
- }
- Some(Arg { name: Some(_), span, .. }) => {
- bail!(span, "named pair is not allowed here");
- }
- };
-
- if let Some(arg) = iter.next() {
- bail!(arg.span, "only one {} is allowed", what);
- }
-
- Ok(value)
- }
-}
-
-impl Debug for Args {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_char('(')?;
- for (i, arg) in self.items.iter().enumerate() {
- arg.fmt(f)?;
- if i + 1 < self.items.len() {
- f.write_str(", ")?;
- }
- }
- f.write_char(')')
- }
-}
-
-impl Debug for Arg {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- if let Some(name) = &self.name {
- f.write_str(name)?;
- f.write_str(": ")?;
- }
- Debug::fmt(&self.value.v, f)
- }
-}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 79c0ad83..e00a40f2 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -8,8 +8,8 @@ mod dict;
mod value;
#[macro_use]
mod styles;
+mod args;
mod capture;
-mod class;
mod collapse;
mod content;
mod control;
@@ -20,9 +20,9 @@ mod ops;
mod scope;
mod show;
+pub use args::*;
pub use array::*;
pub use capture::*;
-pub use class::*;
pub use collapse::*;
pub use content::*;
pub use control::*;
@@ -417,11 +417,6 @@ impl Eval for CallExpr {
func.call(ctx, args).trace(point, self.span())
}
- Value::Class(class) => {
- let point = || Tracepoint::Call(Some(class.name().to_string()));
- class.construct(ctx, args).trace(point, self.span())
- }
-
v => bail!(
span,
"expected callable or collection, found {}",
@@ -520,7 +515,7 @@ impl Eval for ClosureExpr {
}
// Define the actual function.
- Ok(Value::Func(Func::closure(Closure {
+ Ok(Value::Func(Func::from_closure(Closure {
name,
captured,
params,
@@ -558,10 +553,10 @@ impl Eval for SetExpr {
type Output = StyleMap;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
- let class = self.class();
- let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?;
+ let target = self.target();
+ let target = target.eval(ctx, scp)?.cast::<Func>().at(target.span())?;
let args = self.args().eval(ctx, scp)?;
- Ok(class.set(args)?)
+ Ok(target.set(args)?)
}
}
@@ -569,13 +564,13 @@ impl Eval for ShowExpr {
type Output = StyleMap;
fn eval(&self, ctx: &mut Context, scp: &mut Scopes) -> EvalResult<Self::Output> {
- let class = self.class();
- let class = class.eval(ctx, scp)?.cast::<Class>().at(class.span())?;
- let closure = self.closure();
- let func = closure.eval(ctx, scp)?.cast::<Func>().at(closure.span())?;
- let mut styles = StyleMap::new();
- styles.set_recipe(class.id(), func, self.span());
- Ok(styles)
+ let target = self.target();
+ let target_span = target.span();
+ let target = target.eval(ctx, scp)?.cast::<Func>().at(target_span)?;
+ let recipe = self.recipe();
+ let recipe_span = recipe.span();
+ let recipe = recipe.eval(ctx, scp)?.cast::<Func>().at(recipe_span)?;
+ Ok(target.show(recipe, recipe_span).at(target_span)?)
}
}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index e09d05c8..19899cae 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher};
use std::iter;
use std::sync::{Arc, RwLock};
-use super::{Args, Class, Construct, Func, Set, Value};
+use super::{Args, Func, Node, Value};
use crate::diag::TypResult;
use crate::util::EcoString;
use crate::Context;
@@ -83,21 +83,18 @@ impl Scope {
self.values.insert(var.into(), slot);
}
- /// Define a constant native function.
- pub fn def_func(
+ /// Define a function through a native rust function.
+ pub fn def_fn(
&mut self,
name: &'static str,
func: fn(&mut Context, &mut Args) -> TypResult<Value>,
) {
- self.def_const(name, Func::native(name, func));
+ self.def_const(name, Func::from_fn(name, func));
}
- /// Define a constant class.
- pub fn def_class<T>(&mut self, name: &'static str)
- where
- T: Construct + Set + 'static,
- {
- self.def_const(name, Class::new::<T>(name));
+ /// 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));
}
/// Look up the value of a variable.
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index 20c36ae2..e52aa9f3 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -30,19 +30,19 @@ impl StyleMap {
}
/// Create a style map from a single property-value pair.
- pub fn with<P: Property>(key: P, value: P::Value) -> Self {
+ pub fn with<K: Key>(key: K, value: K::Value) -> Self {
let mut styles = Self::new();
styles.set(key, value);
styles
}
/// Set the value for a style property.
- pub fn set<P: Property>(&mut self, key: P, value: P::Value) {
+ pub fn set<K: Key>(&mut self, key: K, value: K::Value) {
self.props.push(Entry::new(key, value));
}
/// Set a value for a style property if it is `Some(_)`.
- pub fn set_opt<P: Property>(&mut self, key: P, value: Option<P::Value>) {
+ pub fn set_opt<K: Key>(&mut self, key: K, value: Option<K::Value>) {
if let Some(value) = value {
self.set(key, value);
}
@@ -126,8 +126,8 @@ pub enum Interruption {
/// Style property keys.
///
/// This trait is not intended to be implemented manually, but rather through
-/// the `#[class]` proc-macro.
-pub trait Property: Sync + Send + 'static {
+/// the `#[node]` proc-macro.
+pub trait Key: Sync + Send + 'static {
/// The type of value that is returned when getting this property from a
/// style map. For example, this could be [`Length`](crate::geom::Length)
/// for a `WIDTH` property.
@@ -148,7 +148,7 @@ pub trait Property: Sync + Send + 'static {
/// A static reference to the default value of the property.
///
/// This is automatically implemented through lazy-initialization in the
- /// `#[class]` macro. This way, expensive defaults don't need to be
+ /// `#[node]` macro. This way, expensive defaults don't need to be
/// recreated all the time.
fn default_ref() -> &'static Self::Value;
@@ -162,8 +162,12 @@ pub trait Property: Sync + Send + 'static {
}
}
-/// Marker trait that indicates that a property doesn't need folding.
-pub trait Nonfolding {}
+/// Marker trait indicating that a property can be accessed by reference.
+///
+/// This is implemented by a key if and only if `K::FOLDING` if false.
+/// Unfortunately, Rust's type system doesn't allow use to use an associated
+/// constant to bound a function, so we need this trait.
+pub trait Referencable {}
/// An entry for a single style property.
#[derive(Clone)]
@@ -173,14 +177,14 @@ struct Entry {
}
impl Entry {
- fn new<P: Property>(key: P, value: P::Value) -> Self {
+ fn new<K: Key>(key: K, value: K::Value) -> Self {
Self {
pair: Arc::new((key, value)),
scoped: false,
}
}
- fn is<P: Property>(&self) -> bool {
+ fn is<P: Key>(&self) -> bool {
self.pair.style_id() == TypeId::of::<P>()
}
@@ -192,7 +196,7 @@ impl Entry {
self.pair.node_id() == node
}
- fn downcast<P: Property>(&self) -> Option<&P::Value> {
+ fn downcast<K: Key>(&self) -> Option<&K::Value> {
self.pair.as_any().downcast_ref()
}
@@ -244,18 +248,18 @@ trait Bounds: Sync + Send + 'static {
fn style_id(&self) -> TypeId;
}
-impl<P: Property> Bounds for (P, P::Value) {
+impl<K: Key> Bounds for (K, K::Value) {
fn as_any(&self) -> &dyn Any {
&self.1
}
fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{} = {:?}", P::NAME, self.1)
+ write!(f, "{} = {:?}", K::NAME, self.1)
}
fn dyn_eq(&self, other: &Entry) -> bool {
self.style_id() == other.pair.style_id()
- && if let Some(other) = other.downcast::<P>() {
+ && if let Some(other) = other.downcast::<K>() {
&self.1 == other
} else {
false
@@ -270,11 +274,11 @@ impl<P: Property> Bounds for (P, P::Value) {
}
fn node_id(&self) -> TypeId {
- P::node_id()
+ K::node_id()
}
fn style_id(&self) -> TypeId {
- TypeId::of::<P>()
+ TypeId::of::<K>()
}
}
@@ -366,9 +370,9 @@ impl<'a> StyleChain<'a> {
///
/// Returns the property's default value if no map in the chain contains an
/// entry for it.
- pub fn get<P: Property>(self, key: P) -> P::Value
+ pub fn get<K: Key>(self, key: K) -> K::Value
where
- P::Value: Copy,
+ K::Value: Copy,
{
self.get_cloned(key)
}
@@ -381,11 +385,11 @@ impl<'a> StyleChain<'a> {
///
/// Returns a lazily-initialized reference to the property's default value
/// if no map in the chain contains an entry for it.
- pub fn get_ref<P: Property>(self, key: P) -> &'a P::Value
+ pub fn get_ref<K: Key>(self, key: K) -> &'a K::Value
where
- P: Nonfolding,
+ K: Referencable,
{
- self.values(key).next().unwrap_or_else(|| P::default_ref())
+ self.values(key).next().unwrap_or_else(|| K::default_ref())
}
/// Get the (folded) value of any style property.
@@ -396,15 +400,15 @@ impl<'a> StyleChain<'a> {
///
/// Returns the property's default value if no map in the chain contains an
/// entry for it.
- pub fn get_cloned<P: Property>(self, key: P) -> P::Value {
- if P::FOLDING {
+ pub fn get_cloned<K: Key>(self, key: K) -> K::Value {
+ if K::FOLDING {
self.values(key)
.cloned()
- .chain(std::iter::once(P::default()))
- .reduce(P::fold)
+ .chain(std::iter::once(K::default()))
+ .reduce(K::fold)
.unwrap()
} else {
- self.values(key).next().cloned().unwrap_or_else(P::default)
+ self.values(key).next().cloned().unwrap_or_else(K::default)
}
}
@@ -445,19 +449,19 @@ impl<'a> StyleChain<'a> {
impl<'a> StyleChain<'a> {
/// Iterate over all values for the given property in the chain.
- fn values<P: Property>(self, _: P) -> impl Iterator<Item = &'a P::Value> {
+ fn values<K: Key>(self, _: K) -> impl Iterator<Item = &'a K::Value> {
let mut depth = 0;
self.links().flat_map(move |link| {
let mut entries: &[Entry] = &[];
match link {
Link::Map(map) => entries = &map.props,
- Link::Barrier(id) => depth += (id == P::node_id()) as usize,
+ Link::Barrier(id) => depth += (id == K::node_id()) as usize,
}
entries
.iter()
.rev()
- .filter(move |entry| entry.is::<P>() && (!entry.scoped || depth <= 1))
- .filter_map(|entry| entry.downcast::<P>())
+ .filter(move |entry| entry.is::<K>() && (!entry.scoped || depth <= 1))
+ .filter_map(|entry| entry.downcast::<K>())
})
}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 48b2139f..8867b38a 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
-use super::{ops, Args, Array, Class, Content, Dict, Func, Layout};
+use super::{ops, Args, Array, Content, Dict, Func, Layout};
use crate::diag::{with_alternative, StrResult};
use crate::geom::{Angle, Color, Fractional, Length, Linear, Relative, RgbaColor};
use crate::syntax::Spanned;
@@ -47,8 +47,6 @@ pub enum Value {
Func(Func),
/// Captured arguments to a function.
Args(Args),
- /// A class of nodes.
- Class(Class),
/// A dynamic value.
Dyn(Dynamic),
}
@@ -90,7 +88,6 @@ impl Value {
Self::Dict(_) => Dict::TYPE_NAME,
Self::Func(_) => Func::TYPE_NAME,
Self::Args(_) => Args::TYPE_NAME,
- Self::Class(_) => Class::TYPE_NAME,
Self::Dyn(v) => v.type_name(),
}
}
@@ -151,7 +148,6 @@ impl Debug for Value {
Self::Dict(v) => Debug::fmt(v, f),
Self::Func(v) => Debug::fmt(v, f),
Self::Args(v) => Debug::fmt(v, f),
- Self::Class(v) => Debug::fmt(v, f),
Self::Dyn(v) => Debug::fmt(v, f),
}
}
@@ -190,7 +186,6 @@ impl Hash for Value {
Self::Dict(v) => v.hash(state),
Self::Func(v) => v.hash(state),
Self::Args(v) => v.hash(state),
- Self::Class(v) => v.hash(state),
Self::Dyn(v) => v.hash(state),
}
}
@@ -444,9 +439,8 @@ primitive! { EcoString: "string", Str }
primitive! { Content: "content", Content, None => Content::new() }
primitive! { Array: "array", Array }
primitive! { Dict: "dictionary", Dict }
-primitive! { Func: "function", Func, Class(v) => v.constructor() }
+primitive! { Func: "function", Func }
primitive! { Args: "arguments", Args }
-primitive! { Class: "class", Class }
impl Cast for Value {
fn is(_: &Value) -> bool {
@@ -570,7 +564,7 @@ mod tests {
// Functions.
test(
- Func::native("nil", |_, _| Ok(Value::None)),
+ Func::from_fn("nil", |_, _| Ok(Value::None)),
"<function nil>",
);