diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-03-12 14:24:24 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-03-12 14:24:24 +0100 |
| commit | 2890a156d27c02a101137bf01dc2046597110bd1 (patch) | |
| tree | c6bdeb48242c0fbd5b5e13120ca3c8f502d41b75 /src/eval | |
| parent | 5ac7eb3860ebd3247f6486c227e816894cb8fd91 (diff) | |
Remove classes and improve naming
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/args.rs | 202 | ||||
| -rw-r--r-- | src/eval/class.rs | 138 | ||||
| -rw-r--r-- | src/eval/content.rs | 14 | ||||
| -rw-r--r-- | src/eval/func.rs | 282 | ||||
| -rw-r--r-- | src/eval/mod.rs | 31 | ||||
| -rw-r--r-- | src/eval/scope.rs | 17 | ||||
| -rw-r--r-- | src/eval/styles.rs | 64 | ||||
| -rw-r--r-- | src/eval/value.rs | 12 |
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>", ); |
