From 2890a156d27c02a101137bf01dc2046597110bd1 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 12 Mar 2022 14:24:24 +0100 Subject: [PATCH] Remove classes and improve naming --- macros/src/lib.rs | 36 ++-- src/eval/args.rs | 202 +++++++++++++++++++++ src/eval/class.rs | 138 --------------- src/eval/content.rs | 14 +- src/eval/func.rs | 282 ++++++++---------------------- src/eval/mod.rs | 31 ++-- src/eval/scope.rs | 17 +- src/eval/styles.rs | 64 +++---- src/eval/value.rs | 12 +- src/library/graphics/hide.rs | 2 +- src/library/graphics/image.rs | 2 +- src/library/graphics/shape.rs | 2 +- src/library/graphics/transform.rs | 2 +- src/library/layout/align.rs | 2 +- src/library/layout/columns.rs | 4 +- src/library/layout/container.rs | 4 +- src/library/layout/flow.rs | 10 +- src/library/layout/grid.rs | 2 +- src/library/layout/pad.rs | 2 +- src/library/layout/page.rs | 10 +- src/library/layout/place.rs | 2 +- src/library/layout/spacing.rs | 12 +- src/library/layout/stack.rs | 22 +-- src/library/math/mod.rs | 2 +- src/library/mod.rs | 122 ++++++------- src/library/prelude.rs | 7 +- src/library/structure/heading.rs | 2 +- src/library/structure/list.rs | 2 +- src/library/structure/table.rs | 7 +- src/library/text/deco.rs | 2 +- src/library/text/link.rs | 2 +- src/library/text/mod.rs | 6 +- src/library/text/par.rs | 23 +-- src/library/text/raw.rs | 2 +- src/syntax/ast.rs | 20 +-- tests/ref/code/repr.png | Bin 29315 -> 28698 bytes tests/typ/style/show.typ | 8 +- tests/typeset.rs | 2 +- 38 files changed, 509 insertions(+), 572 deletions(-) create mode 100644 src/eval/args.rs delete mode 100644 src/eval/class.rs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 774770a3b..7478c8cb8 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -8,15 +8,16 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{Error, Ident, Result}; -/// Turn a node into a class. #[proc_macro_attribute] -pub fn class(_: TokenStream, item: TokenStream) -> TokenStream { +pub fn node(stream: TokenStream, item: TokenStream) -> TokenStream { let impl_block = syn::parse_macro_input!(item as syn::ItemImpl); - expand(impl_block).unwrap_or_else(|err| err.to_compile_error()).into() + expand(TokenStream2::from(stream), impl_block) + .unwrap_or_else(|err| err.to_compile_error()) + .into() } /// Expand an impl block for a node. -fn expand(mut impl_block: syn::ItemImpl) -> Result { +fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result { // Split the node type into name and generic type arguments. let params = &impl_block.generics.params; let self_ty = &*impl_block.self_ty; @@ -77,13 +78,20 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { }); parse_quote! { - fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { + fn set(args: &mut Args) -> TypResult { + let mut styles = StyleMap::new(); #(#sets)* - Ok(()) + Ok(styles) } } }); + let showable = match stream.to_string().as_str() { + "" => false, + "showable" => true, + _ => return Err(Error::new(stream.span(), "unrecognized argument")), + }; + // Put everything into a module with a hopefully unique type to isolate // it from the outside. Ok(quote! { @@ -92,16 +100,14 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { use std::any::TypeId; use std::marker::PhantomData; use once_cell::sync::Lazy; - use crate::eval::{Construct, Nonfolding, Property, Set}; + use crate::eval; use super::*; #impl_block - impl<#params> Construct for #self_ty { + impl<#params> eval::Node for #self_ty { + const SHOWABLE: bool = #showable; #construct - } - - impl<#params> Set for #self_ty { #set } @@ -206,8 +212,8 @@ fn process_const( )); } - let nonfolding = fold.is_none().then(|| { - quote! { impl<#params> Nonfolding for #key {} } + let referencable = fold.is_none().then(|| { + quote! { impl<#params> eval::Referencable for #key {} } }); // Generate the module code. @@ -226,7 +232,7 @@ fn process_const( } } - impl<#params> Property for #key { + impl<#params> eval::Key for #key { type Value = #value_ty; const NAME: &'static str = #name; @@ -247,7 +253,7 @@ fn process_const( #fold } - #nonfolding + #referencable } }; diff --git a/src/eval/args.rs b/src/eval/args.rs new file mode 100644 index 000000000..67da9865f --- /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, +} + +/// 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, + /// The value of the argument. + pub value: Spanned, +} + +impl Args { + /// Create positional arguments from a span and values. + pub fn from_values(span: Span, values: impl IntoIterator) -> 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(&mut self, what: &str) -> TypResult + where + T: Cast>, + { + 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(&mut self) -> TypResult> + where + T: Cast>, + { + 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(&mut self) -> TypResult> + where + T: Cast>, + { + 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(&mut self) -> TypResult> + where + T: Cast>, + { + 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(&mut self, name: &str) -> TypResult> + where + T: Cast>, + { + // 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(&mut self, name: &str) -> TypResult> + where + T: Cast>, + { + 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 { + self.into_castable("index") + } + + /// Reinterpret these arguments as actually being a dictionary key. + pub fn into_key(self) -> TypResult { + self.into_castable("key") + } + + /// Reinterpret these arguments as actually being a single castable thing. + fn into_castable(self, what: &str) -> TypResult { + 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 051916673..000000000 --- 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, - set: fn(&mut Args, &mut StyleMap) -> TypResult<()>, -} - -impl Class { - /// Create a new class. - pub fn new(name: &'static str) -> Self - where - T: Construct + Set + 'static, - { - Self { - name, - id: TypeId::of::(), - 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 { - 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 { - 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("') - } -} - -impl PartialEq for Class { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -impl Hash for Class { - fn hash(&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; -} - -/// 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 b6d60957f..5f8c5861c 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(mut self, key: P, value: P::Value) -> Self { + pub fn styled(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 451dcbbb7..3eae453e0 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, ) -> 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(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::(), 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 { + 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 { + 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, + /// The set rule. + pub set: Option TypResult>, + /// The show rule. + pub show: Option StyleMap>, } impl Hash for Native { fn hash(&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; + + /// Parse the arguments into style properties for this node. + fn set(args: &mut Args) -> TypResult; +} + /// 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, -} - -/// 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, - /// The value of the argument. - pub value: Spanned, -} - -impl Args { - /// Create positional arguments from a span and values. - pub fn from_values(span: Span, values: impl IntoIterator) -> 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(&mut self, what: &str) -> TypResult - where - T: Cast>, - { - 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(&mut self) -> TypResult> - where - T: Cast>, - { - 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(&mut self) -> TypResult> - where - T: Cast>, - { - 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(&mut self) -> TypResult> - where - T: Cast>, - { - 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(&mut self, name: &str) -> TypResult> - where - T: Cast>, - { - // 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(&mut self, name: &str) -> TypResult> - where - T: Cast>, - { - 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 { - self.into_castable("index") - } - - /// Reinterpret these arguments as actually being a dictionary key. - pub fn into_key(self) -> TypResult { - self.into_castable("key") - } - - /// Reinterpret these arguments as actually being a single castable thing. - fn into_castable(self, what: &str) -> TypResult { - 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 79c0ad83f..e00a40f28 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 { - let class = self.class(); - let class = class.eval(ctx, scp)?.cast::().at(class.span())?; + let target = self.target(); + let target = target.eval(ctx, scp)?.cast::().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 { - let class = self.class(); - let class = class.eval(ctx, scp)?.cast::().at(class.span())?; - let closure = self.closure(); - let func = closure.eval(ctx, scp)?.cast::().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::().at(target_span)?; + let recipe = self.recipe(); + let recipe_span = recipe.span(); + let recipe = recipe.eval(ctx, scp)?.cast::().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 e09d05c82..19899cae9 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, ) { - 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(&mut self, name: &'static str) - where - T: Construct + Set + 'static, - { - self.def_const(name, Class::new::(name)); + /// Define a function through a native rust node. + pub fn def_node(&mut self, name: &'static str) { + self.def_const(name, Func::from_node::(name)); } /// Look up the value of a variable. diff --git a/src/eval/styles.rs b/src/eval/styles.rs index 20c36ae20..e52aa9f3d 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(key: P, value: P::Value) -> Self { + pub fn with(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(&mut self, key: P, value: P::Value) { + pub fn set(&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(&mut self, key: P, value: Option) { + pub fn set_opt(&mut self, key: K, value: Option) { 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(key: P, value: P::Value) -> Self { + fn new(key: K, value: K::Value) -> Self { Self { pair: Arc::new((key, value)), scoped: false, } } - fn is(&self) -> bool { + fn is(&self) -> bool { self.pair.style_id() == TypeId::of::

() } @@ -192,7 +196,7 @@ impl Entry { self.pair.node_id() == node } - fn downcast(&self) -> Option<&P::Value> { + fn downcast(&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 Bounds for (P, P::Value) { +impl 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::

() { + && if let Some(other) = other.downcast::() { &self.1 == other } else { false @@ -270,11 +274,11 @@ impl Bounds for (P, P::Value) { } fn node_id(&self) -> TypeId { - P::node_id() + K::node_id() } fn style_id(&self) -> TypeId { - TypeId::of::

() + TypeId::of::() } } @@ -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(self, key: P) -> P::Value + pub fn get(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(self, key: P) -> &'a P::Value + pub fn get_ref(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(self, key: P) -> P::Value { - if P::FOLDING { + pub fn get_cloned(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(self, _: P) -> impl Iterator { + fn values(self, _: K) -> impl Iterator { 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::

() && (!entry.scoped || depth <= 1)) - .filter_map(|entry| entry.downcast::

()) + .filter(move |entry| entry.is::() && (!entry.scoped || depth <= 1)) + .filter_map(|entry| entry.downcast::()) }) } diff --git a/src/eval/value.rs b/src/eval/value.rs index 48b2139f1..8867b38ae 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)), "", ); diff --git a/src/library/graphics/hide.rs b/src/library/graphics/hide.rs index 21fc58c25..85971c36e 100644 --- a/src/library/graphics/hide.rs +++ b/src/library/graphics/hide.rs @@ -4,7 +4,7 @@ use crate::library::prelude::*; #[derive(Debug, Hash)] pub struct HideNode(pub LayoutNode); -#[class] +#[node] impl HideNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::inline(Self(args.expect("body")?))) diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs index faf89850f..54793754d 100644 --- a/src/library/graphics/image.rs +++ b/src/library/graphics/image.rs @@ -7,7 +7,7 @@ use crate::library::text::TextNode; #[derive(Debug, Hash)] pub struct ImageNode(pub ImageId); -#[class] +#[node] impl ImageNode { /// How the image should adjust itself to a given area. pub const FIT: ImageFit = ImageFit::Cover; diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs index fbe213479..9f9ff889e 100644 --- a/src/library/graphics/shape.rs +++ b/src/library/graphics/shape.rs @@ -19,7 +19,7 @@ pub type CircleNode = ShapeNode; /// Place a node into an ellipse. pub type EllipseNode = ShapeNode; -#[class] +#[node] impl ShapeNode { /// How to fill the shape. pub const FILL: Option = None; diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs index fcd7528d4..0dc501668 100644 --- a/src/library/graphics/transform.rs +++ b/src/library/graphics/transform.rs @@ -19,7 +19,7 @@ pub type RotateNode = TransformNode; /// Transform a node by scaling it without affecting layout. pub type ScaleNode = TransformNode; -#[class] +#[node] impl TransformNode { /// The origin of the transformation. pub const ORIGIN: Spec> = Spec::default(); diff --git a/src/library/layout/align.rs b/src/library/layout/align.rs index 2a9695248..b08e5fcee 100644 --- a/src/library/layout/align.rs +++ b/src/library/layout/align.rs @@ -10,7 +10,7 @@ pub struct AlignNode { pub child: LayoutNode, } -#[class] +#[node] impl AlignNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { let aligns: Spec<_> = args.find()?.unwrap_or_default(); diff --git a/src/library/layout/columns.rs b/src/library/layout/columns.rs index 9e461108d..b5ec7de9c 100644 --- a/src/library/layout/columns.rs +++ b/src/library/layout/columns.rs @@ -11,7 +11,7 @@ pub struct ColumnsNode { pub child: LayoutNode, } -#[class] +#[node] impl ColumnsNode { /// The size of the gutter space between each column. pub const GUTTER: Linear = Relative::new(0.04).into(); @@ -103,7 +103,7 @@ impl Layout for ColumnsNode { /// A column break. pub struct ColbreakNode; -#[class] +#[node] impl ColbreakNode { fn construct(_: &mut Context, _: &mut Args) -> TypResult { Ok(Content::Colbreak) diff --git a/src/library/layout/container.rs b/src/library/layout/container.rs index f7f4017c3..6689dd487 100644 --- a/src/library/layout/container.rs +++ b/src/library/layout/container.rs @@ -3,7 +3,7 @@ use crate::library::prelude::*; /// An inline-level container that sizes content and places it into a paragraph. pub struct BoxNode; -#[class] +#[node] impl BoxNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { let width = args.named("width")?; @@ -16,7 +16,7 @@ impl BoxNode { /// A block-level container that places content into a separate flow. pub struct BlockNode; -#[class] +#[node] impl BlockNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::Block(args.find()?.unwrap_or_default())) diff --git a/src/library/layout/flow.rs b/src/library/layout/flow.rs index f4b885b1a..3602bea68 100644 --- a/src/library/layout/flow.rs +++ b/src/library/layout/flow.rs @@ -1,4 +1,4 @@ -use super::{AlignNode, PlaceNode, SpacingKind}; +use super::{AlignNode, PlaceNode, Spacing}; use crate::library::prelude::*; use crate::library::text::{ParNode, TextNode}; @@ -19,7 +19,7 @@ pub enum FlowChild { /// A column / region break. Colbreak, /// Vertical spacing between other children. - Spacing(SpacingKind), + Spacing(Spacing), /// An arbitrary block-level node. Node(LayoutNode), } @@ -142,9 +142,9 @@ impl FlowLayouter { } /// Layout spacing. - pub fn layout_spacing(&mut self, spacing: SpacingKind) { + pub fn layout_spacing(&mut self, spacing: Spacing) { match spacing { - SpacingKind::Linear(v) => { + Spacing::Linear(v) => { // Resolve the linear and limit it to the remaining space. let resolved = v.resolve(self.full.y); let limited = resolved.min(self.regions.first.y); @@ -152,7 +152,7 @@ impl FlowLayouter { self.used.y += limited; self.items.push(FlowItem::Absolute(resolved)); } - SpacingKind::Fractional(v) => { + Spacing::Fractional(v) => { self.items.push(FlowItem::Fractional(v)); self.fr += v; } diff --git a/src/library/layout/grid.rs b/src/library/layout/grid.rs index 90cf6da3a..716ac8539 100644 --- a/src/library/layout/grid.rs +++ b/src/library/layout/grid.rs @@ -11,7 +11,7 @@ pub struct GridNode { pub children: Vec, } -#[class] +#[node] impl GridNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { let columns = args.named("columns")?.unwrap_or_default(); diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs index 835beef98..664c63acb 100644 --- a/src/library/layout/pad.rs +++ b/src/library/layout/pad.rs @@ -9,7 +9,7 @@ pub struct PadNode { pub child: LayoutNode, } -#[class] +#[node] impl PadNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { let all = args.find()?; diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index def2940e9..b1008febc 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -7,7 +7,7 @@ use crate::library::prelude::*; #[derive(Clone, PartialEq, Hash)] pub struct PageNode(pub LayoutNode); -#[class] +#[node] impl PageNode { /// The unflipped width of the page. pub const WIDTH: Smart = Smart::Custom(Paper::A4.width()); @@ -36,7 +36,9 @@ impl PageNode { Ok(Content::Page(Self(args.expect("body")?))) } - fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { + fn set(args: &mut Args) -> TypResult { + let mut styles = StyleMap::new(); + if let Some(paper) = args.named_or_find::("paper")? { styles.set(Self::WIDTH, Smart::Custom(paper.width())); styles.set(Self::HEIGHT, Smart::Custom(paper.height())); @@ -59,7 +61,7 @@ impl PageNode { styles.set_opt(Self::HEADER, args.named("header")?); styles.set_opt(Self::FOOTER, args.named("footer")?); - Ok(()) + Ok(styles) } } @@ -153,7 +155,7 @@ impl Debug for PageNode { /// A page break. pub struct PagebreakNode; -#[class] +#[node] impl PagebreakNode { fn construct(_: &mut Context, _: &mut Args) -> TypResult { Ok(Content::Pagebreak) diff --git a/src/library/layout/place.rs b/src/library/layout/place.rs index 99ff52927..2d4ebc4d3 100644 --- a/src/library/layout/place.rs +++ b/src/library/layout/place.rs @@ -5,7 +5,7 @@ use crate::library::prelude::*; #[derive(Debug, Hash)] pub struct PlaceNode(pub LayoutNode); -#[class] +#[node] impl PlaceNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { let aligns = args.find()?.unwrap_or(Spec::with_x(Some(Align::Left))); diff --git a/src/library/layout/spacing.rs b/src/library/layout/spacing.rs index 9a27a8b2e..8aea780ca 100644 --- a/src/library/layout/spacing.rs +++ b/src/library/layout/spacing.rs @@ -3,7 +3,7 @@ use crate::library::prelude::*; /// Horizontal spacing. pub struct HNode; -#[class] +#[node] impl HNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::Horizontal(args.expect("spacing")?)) @@ -13,7 +13,7 @@ impl HNode { /// Vertical spacing. pub struct VNode; -#[class] +#[node] impl VNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::Vertical(args.expect("spacing")?)) @@ -22,28 +22,28 @@ impl VNode { /// Kinds of spacing. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum SpacingKind { +pub enum Spacing { /// A length stated in absolute values and/or relative to the parent's size. Linear(Linear), /// A length that is the fraction of the remaining free space in the parent. Fractional(Fractional), } -impl SpacingKind { +impl Spacing { /// Whether this is fractional spacing. pub fn is_fractional(self) -> bool { matches!(self, Self::Fractional(_)) } } -impl From for SpacingKind { +impl From for Spacing { fn from(length: Length) -> Self { Self::Linear(length.into()) } } castable! { - SpacingKind, + Spacing, Expected: "linear or fractional", Value::Length(v) => Self::Linear(v.into()), Value::Relative(v) => Self::Linear(v.into()), diff --git a/src/library/layout/stack.rs b/src/library/layout/stack.rs index 88e271168..11d45bb4d 100644 --- a/src/library/layout/stack.rs +++ b/src/library/layout/stack.rs @@ -1,4 +1,4 @@ -use super::{AlignNode, SpacingKind}; +use super::{AlignNode, Spacing}; use crate::library::prelude::*; /// Arrange nodes and spacing along an axis. @@ -7,12 +7,12 @@ pub struct StackNode { /// The stacking direction. pub dir: Dir, /// The spacing between non-spacing children. - pub spacing: Option, + pub spacing: Option, /// The children to be stacked. pub children: Vec, } -#[class] +#[node] impl StackNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::block(Self { @@ -60,7 +60,7 @@ impl Layout for StackNode { #[derive(Hash)] pub enum StackChild { /// Spacing between other nodes. - Spacing(SpacingKind), + Spacing(Spacing), /// An arbitrary node. Node(LayoutNode), } @@ -77,10 +77,10 @@ impl Debug for StackChild { castable! { StackChild, Expected: "linear, fractional or content", - Value::Length(v) => Self::Spacing(SpacingKind::Linear(v.into())), - Value::Relative(v) => Self::Spacing(SpacingKind::Linear(v.into())), - Value::Linear(v) => Self::Spacing(SpacingKind::Linear(v)), - Value::Fractional(v) => Self::Spacing(SpacingKind::Fractional(v)), + Value::Length(v) => Self::Spacing(Spacing::Linear(v.into())), + Value::Relative(v) => Self::Spacing(Spacing::Linear(v.into())), + Value::Linear(v) => Self::Spacing(Spacing::Linear(v)), + Value::Fractional(v) => Self::Spacing(Spacing::Fractional(v)), Value::Content(v) => Self::Node(v.pack()), } @@ -142,9 +142,9 @@ impl StackLayouter { } /// Add spacing along the spacing direction. - pub fn layout_spacing(&mut self, spacing: SpacingKind) { + pub fn layout_spacing(&mut self, spacing: Spacing) { match spacing { - SpacingKind::Linear(v) => { + Spacing::Linear(v) => { // Resolve the linear and limit it to the remaining space. let resolved = v.resolve(self.regions.base.get(self.axis)); let remaining = self.regions.first.get_mut(self.axis); @@ -153,7 +153,7 @@ impl StackLayouter { self.used.main += limited; self.items.push(StackItem::Absolute(resolved)); } - SpacingKind::Fractional(v) => { + Spacing::Fractional(v) => { self.fr += v; self.items.push(StackItem::Fractional(v)); } diff --git a/src/library/math/mod.rs b/src/library/math/mod.rs index b43db22e1..f20d65438 100644 --- a/src/library/math/mod.rs +++ b/src/library/math/mod.rs @@ -11,7 +11,7 @@ pub struct MathNode { pub display: bool, } -#[class] +#[node(showable)] impl MathNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::show(Self { diff --git a/src/library/mod.rs b/src/library/mod.rs index b2e4e4087..8f00e5fe0 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -18,77 +18,77 @@ pub fn new() -> Scope { let mut std = Scope::new(); // Text. - std.def_class::("text"); - std.def_class::("par"); - std.def_class::("linebreak"); - std.def_class::("parbreak"); - std.def_class::("strong"); - std.def_class::("emph"); - std.def_class::("raw"); - std.def_class::("underline"); - std.def_class::("strike"); - std.def_class::("overline"); - std.def_class::("link"); + std.def_node::("text"); + std.def_node::("par"); + std.def_node::("linebreak"); + std.def_node::("parbreak"); + std.def_node::("strong"); + std.def_node::("emph"); + std.def_node::("raw"); + std.def_node::("underline"); + std.def_node::("strike"); + std.def_node::("overline"); + std.def_node::("link"); // Structure. - std.def_class::("heading"); - std.def_class::("list"); - std.def_class::("enum"); - std.def_class::("table"); + std.def_node::("heading"); + std.def_node::("list"); + std.def_node::("enum"); + std.def_node::("table"); // Layout. - std.def_class::("page"); - std.def_class::("pagebreak"); - std.def_class::("h"); - std.def_class::("v"); - std.def_class::("box"); - std.def_class::("block"); - std.def_class::("align"); - std.def_class::("pad"); - std.def_class::("stack"); - std.def_class::("grid"); - std.def_class::("columns"); - std.def_class::("colbreak"); - std.def_class::("place"); + std.def_node::("page"); + std.def_node::("pagebreak"); + std.def_node::("h"); + std.def_node::("v"); + std.def_node::("box"); + std.def_node::("block"); + std.def_node::("align"); + std.def_node::("pad"); + std.def_node::("stack"); + std.def_node::("grid"); + std.def_node::("columns"); + std.def_node::("colbreak"); + std.def_node::("place"); // Graphics. - std.def_class::("image"); - std.def_class::("rect"); - std.def_class::("square"); - std.def_class::("ellipse"); - std.def_class::("circle"); - std.def_class::("move"); - std.def_class::("scale"); - std.def_class::("rotate"); - std.def_class::("hide"); + std.def_node::("image"); + std.def_node::("rect"); + std.def_node::("square"); + std.def_node::("ellipse"); + std.def_node::("circle"); + std.def_node::("move"); + std.def_node::("scale"); + std.def_node::("rotate"); + std.def_node::("hide"); // Math. - std.def_class::("math"); + std.def_node::("math"); // Utility functions. - std.def_func("assert", utility::assert); - std.def_func("type", utility::type_); - std.def_func("repr", utility::repr); - std.def_func("join", utility::join); - std.def_func("int", utility::int); - std.def_func("float", utility::float); - std.def_func("str", utility::str); - std.def_func("abs", utility::abs); - std.def_func("min", utility::min); - std.def_func("max", utility::max); - std.def_func("even", utility::even); - std.def_func("odd", utility::odd); - std.def_func("mod", utility::modulo); - std.def_func("range", utility::range); - std.def_func("rgb", utility::rgb); - std.def_func("cmyk", utility::cmyk); - std.def_func("lower", utility::lower); - std.def_func("upper", utility::upper); - std.def_func("letter", utility::letter); - std.def_func("roman", utility::roman); - std.def_func("symbol", utility::symbol); - std.def_func("len", utility::len); - std.def_func("sorted", utility::sorted); + std.def_fn("assert", utility::assert); + std.def_fn("type", utility::type_); + std.def_fn("repr", utility::repr); + std.def_fn("join", utility::join); + std.def_fn("int", utility::int); + std.def_fn("float", utility::float); + std.def_fn("str", utility::str); + std.def_fn("abs", utility::abs); + std.def_fn("min", utility::min); + std.def_fn("max", utility::max); + std.def_fn("even", utility::even); + std.def_fn("odd", utility::odd); + std.def_fn("mod", utility::modulo); + std.def_fn("range", utility::range); + std.def_fn("rgb", utility::rgb); + std.def_fn("cmyk", utility::cmyk); + std.def_fn("lower", utility::lower); + std.def_fn("upper", utility::upper); + std.def_fn("letter", utility::letter); + std.def_fn("roman", utility::roman); + std.def_fn("symbol", utility::symbol); + std.def_fn("len", utility::len); + std.def_fn("sorted", utility::sorted); // Predefined colors. std.def_const("black", Color::BLACK); diff --git a/src/library/prelude.rs b/src/library/prelude.rs index 0cca718f1..001798f37 100644 --- a/src/library/prelude.rs +++ b/src/library/prelude.rs @@ -5,13 +5,12 @@ pub use std::hash::Hash; pub use std::num::NonZeroUsize; pub use std::sync::Arc; -pub use typst_macros::class; +pub use typst_macros::node; pub use crate::diag::{with_alternative, At, StrResult, TypResult}; pub use crate::eval::{ - Arg, Args, Array, Cast, Construct, Content, Dict, Func, Layout, LayoutNode, Merge, - Property, Regions, Scope, Set, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec, - Value, + Arg, Args, Array, Cast, Content, Dict, Func, Key, Layout, LayoutNode, Merge, Node, + Regions, Scope, Show, ShowNode, Smart, StyleChain, StyleMap, StyleVec, Value, }; pub use crate::frame::*; pub use crate::geom::*; diff --git a/src/library/structure/heading.rs b/src/library/structure/heading.rs index f5565f3c9..f1bc795f6 100644 --- a/src/library/structure/heading.rs +++ b/src/library/structure/heading.rs @@ -11,7 +11,7 @@ pub struct HeadingNode { pub body: Content, } -#[class] +#[node(showable)] impl HeadingNode { /// The heading's font family. Just the normal text family if `auto`. pub const FAMILY: Leveled> = Leveled::Value(Smart::Auto); diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs index 2630d231b..414f601e9 100644 --- a/src/library/structure/list.rs +++ b/src/library/structure/list.rs @@ -28,7 +28,7 @@ pub struct ListItem { /// An ordered list. pub type EnumNode = ListNode; -#[class] +#[node(showable)] impl ListNode { /// How the list is labelled. pub const LABEL: Label = Label::Default; diff --git a/src/library/structure/table.rs b/src/library/structure/table.rs index 42b62eac7..0e455eadc 100644 --- a/src/library/structure/table.rs +++ b/src/library/structure/table.rs @@ -12,7 +12,7 @@ pub struct TableNode { pub children: Vec, } -#[class] +#[node(showable)] impl TableNode { /// The primary cell fill color. pub const PRIMARY: Option = None; @@ -41,14 +41,15 @@ impl TableNode { })) } - fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { + fn set(args: &mut Args) -> TypResult { + let mut styles = StyleMap::new(); let fill = args.named("fill")?; styles.set_opt(Self::PRIMARY, args.named("primary")?.or(fill)); styles.set_opt(Self::SECONDARY, args.named("secondary")?.or(fill)); styles.set_opt(Self::STROKE, args.named("stroke")?); styles.set_opt(Self::THICKNESS, args.named("thickness")?); styles.set_opt(Self::PADDING, args.named("padding")?); - Ok(()) + Ok(styles) } } diff --git a/src/library/text/deco.rs b/src/library/text/deco.rs index 608ebb185..29c04b2db 100644 --- a/src/library/text/deco.rs +++ b/src/library/text/deco.rs @@ -18,7 +18,7 @@ pub type StrikethroughNode = DecoNode; /// Typeset overlined text. pub type OverlineNode = DecoNode; -#[class] +#[node(showable)] impl DecoNode { /// Stroke color of the line, defaults to the text color if `None`. #[shorthand] diff --git a/src/library/text/link.rs b/src/library/text/link.rs index e0041df59..4c2b5e7b2 100644 --- a/src/library/text/link.rs +++ b/src/library/text/link.rs @@ -11,7 +11,7 @@ pub struct LinkNode { pub body: Option, } -#[class] +#[node(showable)] impl LinkNode { /// The fill color of text in the link. Just the surrounding text color /// if `auto`. diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs index dbc486fba..8939a8c13 100644 --- a/src/library/text/mod.rs +++ b/src/library/text/mod.rs @@ -25,7 +25,7 @@ use crate::util::EcoString; #[derive(Hash)] pub struct TextNode; -#[class] +#[node] impl TextNode { /// A prioritized sequence of font families. #[variadic] @@ -122,7 +122,7 @@ impl TextNode { #[derive(Debug, Hash)] pub struct StrongNode(pub Content); -#[class] +#[node(showable)] impl StrongNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::show(Self(args.expect("body")?))) @@ -141,7 +141,7 @@ impl Show for StrongNode { #[derive(Debug, Hash)] pub struct EmphNode(pub Content); -#[class] +#[node(showable)] impl EmphNode { fn construct(_: &mut Context, args: &mut Args) -> TypResult { Ok(Content::show(Self(args.expect("body")?))) diff --git a/src/library/text/par.rs b/src/library/text/par.rs index dc888637b..be4e8096b 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -6,7 +6,7 @@ use xi_unicode::LineBreakIterator; use super::{shape, ShapedText, TextNode}; use crate::font::FontStore; -use crate::library::layout::SpacingKind; +use crate::library::layout::Spacing; use crate::library::prelude::*; use crate::util::{ArcExt, EcoString, RangeExt, SliceExt}; @@ -20,12 +20,12 @@ pub enum ParChild { /// A chunk of text. Text(EcoString), /// Horizontal spacing between other children. - Spacing(SpacingKind), + Spacing(Spacing), /// An arbitrary inline-level node. Node(LayoutNode), } -#[class] +#[node] impl ParNode { /// An ISO 639-1 language code. pub const LANG: Option = None; @@ -53,9 +53,10 @@ impl ParNode { Ok(Content::Block(args.expect("body")?)) } - fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> { - let lang = args.named::>("lang")?; + fn set(args: &mut Args) -> TypResult { + let mut styles = StyleMap::new(); + let lang = args.named::>("lang")?; let mut dir = lang.clone().flatten().map(|iso| match iso.to_lowercase().as_str() { "ar" | "dv" | "fa" | "he" | "ks" | "pa" | "ps" | "sd" | "ug" | "ur" @@ -89,7 +90,7 @@ impl ParNode { styles.set_opt(Self::SPACING, args.named("spacing")?); styles.set_opt(Self::INDENT, args.named("indent")?); - Ok(()) + Ok(styles) } } @@ -183,7 +184,7 @@ impl Merge for ParChild { /// A paragraph break. pub struct ParbreakNode; -#[class] +#[node] impl ParbreakNode { fn construct(_: &mut Context, _: &mut Args) -> TypResult { Ok(Content::Parbreak) @@ -193,7 +194,7 @@ impl ParbreakNode { /// A line break. pub struct LinebreakNode; -#[class] +#[node] impl LinebreakNode { fn construct(_: &mut Context, _: &mut Args) -> TypResult { Ok(Content::Linebreak) @@ -256,13 +257,13 @@ impl<'a> ParLayout<'a> { ranges.push(subrange); } } - ParChild::Spacing(kind) => match *kind { - SpacingKind::Linear(v) => { + ParChild::Spacing(spacing) => match *spacing { + Spacing::Linear(v) => { let resolved = v.resolve(regions.first.x); items.push(ParItem::Absolute(resolved)); ranges.push(range); } - SpacingKind::Fractional(v) => { + Spacing::Fractional(v) => { items.push(ParItem::Fractional(v)); ranges.push(range); } diff --git a/src/library/text/raw.rs b/src/library/text/raw.rs index 988bd04e8..e225803f6 100644 --- a/src/library/text/raw.rs +++ b/src/library/text/raw.rs @@ -24,7 +24,7 @@ pub struct RawNode { pub block: bool, } -#[class] +#[node(showable)] impl RawNode { /// The language to syntax-highlight in. pub const LANG: Option = None; diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index f48b445c7..928680e57 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -900,16 +900,14 @@ node! { } impl SetExpr { - /// The class to set style properties for. - pub fn class(&self) -> Ident { - self.0.cast_first_child().expect("set expression is missing class") + /// The function to set style properties for. + pub fn target(&self) -> Ident { + self.0.cast_first_child().expect("set rule is missing target") } /// The style properties to set. pub fn args(&self) -> CallArgs { - self.0 - .cast_first_child() - .expect("set expression is missing argument list") + self.0.cast_last_child().expect("set rule is missing argument list") } } @@ -919,14 +917,14 @@ node! { } impl ShowExpr { - /// The class to set the show rule for. - pub fn class(&self) -> Ident { - self.0.cast_first_child().expect("show expression is missing class") + /// The function to customize with this show rule. + pub fn target(&self) -> Ident { + self.0.cast_first_child().expect("show rule is missing target") } /// The closure that defines the rule. - pub fn closure(&self) -> ClosureExpr { - self.0.cast_first_child().expect("show expression is missing closure") + pub fn recipe(&self) -> ClosureExpr { + self.0.cast_last_child().expect("show rule is missing closure") } } diff --git a/tests/ref/code/repr.png b/tests/ref/code/repr.png index be502800b0818b3db9074b623e4023bf92b98cb7..822b096d3aa993fc92c001d964786cb68c88a3ea 100644 GIT binary patch literal 28698 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�iE^a7)-KCdiYAW(j`fmMuV5~tNV)vea_Urg7Kts%`a z#WEt_WCi9!UK--+I;v$7``ciHlGz$xv8AUJ%02!$RB!2M=FN{AM4)q2MKUExT9|hC zRL@*TsS8R%6tucnAlM}Oe(7dXD9ukTj-kDFw3@2Jh&^)mZXwKpXcK>i zy=BFLmd?&4bX_!WbotI$B@roOwl6nIs))FsZ9Rrsi;cK?bv`=j^*eEzj}aH+X{k@U z5y=R+*ldhNlHwgf7&cbGoEqQON&Km_2*jXlf8ToHufN%;Ooyucw4}cJfLGo&K&CKf zqo9IyY$MFPW5jF-Sl)cIG&rPw%mU^PEcatG zI7LGKJ=$N?f;Rv?QNIry-&@qGIR=}c#chXoNB*P)KivFM+hLT?`S-W^BD|=jcE*@7 z+e@r}SZ=D~Xn0_~Wy;nx5=@5LJV-Yt zEt<-md-t9$EX<1WzMCIDq4XON4;?!hSr~tiFOc|K#oN8c#bS_90365p!=|Q@flyF8 z+Zy$jUDSwv9}=h`e~Vv_Te`cjwrjDXyLbM6Q@hVTW8z`&jj zBs4Pbx#=>sGWuYfuYHPx*CE97E9#^htzQ`uLcyJ0B|D;QSCk7xZpYr)Qt0Kbc^l z+YNQ}>GNkRv#*_V`!|0p-nU3$!-%Mf5>BkZi+jWIhuELnd`l0rM4;p}_8 zTwl=|A{A(-Ubo~Nr%IxeRGEykKF!VJ;Hd9BX9K!%Ne?_`3>~HABx13JAtcZj0>M zXrn*<6>wgN!e1_xu@J%$Wc76G-7G-Zeg>T zu^Q*v#R??i1BLMHPMSXb4+1~c4*(^$|7y9g64&5-UjKy2pV?JccQb) zYyqqerY*tK1jWQ^0UqddHw`LSWMt$@p0D-bL>}35F;{^s0*^K#48(VcWif29;7Uc%t4&fp#R^_te{!?l#`hWx z=D=-vQ{@WPM0)&YM>OAdZTeVa*K_tT9+DpQqtgx_ew0#ZZD|>uoUDDej9t^q!z1wB zb4!=U@VShVCUbwNY)vYuFTnnoZR2QXr*76oOeOj_R=1H;bePTu_KEH-XanT}YbPUt zGjI1(Q!{pQ3MNp2Vw0L%d9i=@+rj3-k?{=DsLeQ3_lumDMMQ~)w)T0yXJ%~rCMLa- xdb@f2{@)Gtf8svBGnBJE2>|(*UyI|rdY`L2*Y$G`D_!9)=bJ1v&NE!G?e8TtcBcRU diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ index e442e4d57..2e003b0a8 100644 --- a/tests/typ/style/show.typ +++ b/tests/typ/style/show.typ @@ -34,15 +34,19 @@ Another text. A [= Heading] C --- -// Error: 1-22 unexpected argument +// Error: 14-22 unexpected argument #show heading() as [] = Heading --- -// Error: 1-28 expected content, found string +// Error: 14-28 expected content, found string #show heading(_, _) as "hi" = Heading +--- +// Error: 7-12 this function cannot be customized with show +#show upper() as {} + --- // Ref: false // // Error: 1-29 show rule is recursive diff --git a/tests/typeset.rs b/tests/typeset.rs index 388d45168..dfc8bff9d 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -73,7 +73,7 @@ fn main() { 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.def_func("test", move |_, args| { + std.def_fn("test", move |_, args| { let lhs = args.expect::("left-hand side")?; let rhs = args.expect::("right-hand side")?; if lhs != rhs {