summaryrefslogtreecommitdiff
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
parent5ac7eb3860ebd3247f6486c227e816894cb8fd91 (diff)
Remove classes and improve naming
-rw-r--r--macros/src/lib.rs36
-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
-rw-r--r--src/library/graphics/hide.rs2
-rw-r--r--src/library/graphics/image.rs2
-rw-r--r--src/library/graphics/shape.rs2
-rw-r--r--src/library/graphics/transform.rs2
-rw-r--r--src/library/layout/align.rs2
-rw-r--r--src/library/layout/columns.rs4
-rw-r--r--src/library/layout/container.rs4
-rw-r--r--src/library/layout/flow.rs10
-rw-r--r--src/library/layout/grid.rs2
-rw-r--r--src/library/layout/pad.rs2
-rw-r--r--src/library/layout/page.rs10
-rw-r--r--src/library/layout/place.rs2
-rw-r--r--src/library/layout/spacing.rs12
-rw-r--r--src/library/layout/stack.rs22
-rw-r--r--src/library/math/mod.rs2
-rw-r--r--src/library/mod.rs122
-rw-r--r--src/library/prelude.rs7
-rw-r--r--src/library/structure/heading.rs2
-rw-r--r--src/library/structure/list.rs2
-rw-r--r--src/library/structure/table.rs7
-rw-r--r--src/library/text/deco.rs2
-rw-r--r--src/library/text/link.rs2
-rw-r--r--src/library/text/mod.rs6
-rw-r--r--src/library/text/par.rs23
-rw-r--r--src/library/text/raw.rs2
-rw-r--r--src/syntax/ast.rs20
-rw-r--r--tests/ref/code/repr.pngbin29315 -> 28698 bytes
-rw-r--r--tests/typ/style/show.typ8
-rw-r--r--tests/typeset.rs2
38 files changed, 509 insertions, 572 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 774770a3..7478c8cb 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<TokenStream2> {
+fn expand(stream: TokenStream2, mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
// 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<TokenStream2> {
});
parse_quote! {
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
+ fn set(args: &mut Args) -> TypResult<StyleMap> {
+ 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<TokenStream2> {
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 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>",
);
diff --git a/src/library/graphics/hide.rs b/src/library/graphics/hide.rs
index 21fc58c2..85971c36 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<Content> {
Ok(Content::inline(Self(args.expect("body")?)))
diff --git a/src/library/graphics/image.rs b/src/library/graphics/image.rs
index faf89850..54793754 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 fbe21347..9f9ff889 100644
--- a/src/library/graphics/shape.rs
+++ b/src/library/graphics/shape.rs
@@ -19,7 +19,7 @@ pub type CircleNode = ShapeNode<CIRCLE>;
/// Place a node into an ellipse.
pub type EllipseNode = ShapeNode<ELLIPSE>;
-#[class]
+#[node]
impl<const S: ShapeKind> ShapeNode<S> {
/// How to fill the shape.
pub const FILL: Option<Paint> = None;
diff --git a/src/library/graphics/transform.rs b/src/library/graphics/transform.rs
index fcd7528d..0dc50166 100644
--- a/src/library/graphics/transform.rs
+++ b/src/library/graphics/transform.rs
@@ -19,7 +19,7 @@ pub type RotateNode = TransformNode<ROTATE>;
/// Transform a node by scaling it without affecting layout.
pub type ScaleNode = TransformNode<SCALE>;
-#[class]
+#[node]
impl<const T: TransformKind> TransformNode<T> {
/// The origin of the transformation.
pub const ORIGIN: Spec<Option<Align>> = Spec::default();
diff --git a/src/library/layout/align.rs b/src/library/layout/align.rs
index 2a969524..b08e5fce 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<Content> {
let aligns: Spec<_> = args.find()?.unwrap_or_default();
diff --git a/src/library/layout/columns.rs b/src/library/layout/columns.rs
index 9e461108..b5ec7de9 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<Content> {
Ok(Content::Colbreak)
diff --git a/src/library/layout/container.rs b/src/library/layout/container.rs
index f7f4017c..6689dd48 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<Content> {
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<Content> {
Ok(Content::Block(args.find()?.unwrap_or_default()))
diff --git a/src/library/layout/flow.rs b/src/library/layout/flow.rs
index f4b885b1..3602bea6 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 90cf6da3..716ac853 100644
--- a/src/library/layout/grid.rs
+++ b/src/library/layout/grid.rs
@@ -11,7 +11,7 @@ pub struct GridNode {
pub children: Vec<LayoutNode>,
}
-#[class]
+#[node]
impl GridNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let columns = args.named("columns")?.unwrap_or_default();
diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs
index 835beef9..664c63ac 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<Content> {
let all = args.find()?;
diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs
index def2940e..b1008feb 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<Length> = 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<StyleMap> {
+ let mut styles = StyleMap::new();
+
if let Some(paper) = args.named_or_find::<Paper>("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<Content> {
Ok(Content::Pagebreak)
diff --git a/src/library/layout/place.rs b/src/library/layout/place.rs
index 99ff5292..2d4ebc4d 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<Content> {
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 9a27a8b2..8aea780c 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<Content> {
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<Content> {
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<Length> for SpacingKind {
+impl From<Length> 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 88e27116..11d45bb4 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<SpacingKind>,
+ pub spacing: Option<Spacing>,
/// The children to be stacked.
pub children: Vec<StackChild>,
}
-#[class]
+#[node]
impl StackNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
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 b43db22e..f20d6543 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<Content> {
Ok(Content::show(Self {
diff --git a/src/library/mod.rs b/src/library/mod.rs
index b2e4e408..8f00e5fe 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::TextNode>("text");
- std.def_class::<text::ParNode>("par");
- std.def_class::<text::LinebreakNode>("linebreak");
- std.def_class::<text::ParbreakNode>("parbreak");
- std.def_class::<text::StrongNode>("strong");
- std.def_class::<text::EmphNode>("emph");
- std.def_class::<text::RawNode>("raw");
- std.def_class::<text::UnderlineNode>("underline");
- std.def_class::<text::StrikethroughNode>("strike");
- std.def_class::<text::OverlineNode>("overline");
- std.def_class::<text::LinkNode>("link");
+ std.def_node::<text::TextNode>("text");
+ std.def_node::<text::ParNode>("par");
+ std.def_node::<text::LinebreakNode>("linebreak");
+ std.def_node::<text::ParbreakNode>("parbreak");
+ std.def_node::<text::StrongNode>("strong");
+ std.def_node::<text::EmphNode>("emph");
+ std.def_node::<text::RawNode>("raw");
+ std.def_node::<text::UnderlineNode>("underline");
+ std.def_node::<text::StrikethroughNode>("strike");
+ std.def_node::<text::OverlineNode>("overline");
+ std.def_node::<text::LinkNode>("link");
// Structure.
- std.def_class::<structure::HeadingNode>("heading");
- std.def_class::<structure::ListNode>("list");
- std.def_class::<structure::EnumNode>("enum");
- std.def_class::<structure::TableNode>("table");
+ std.def_node::<structure::HeadingNode>("heading");
+ std.def_node::<structure::ListNode>("list");
+ std.def_node::<structure::EnumNode>("enum");
+ std.def_node::<structure::TableNode>("table");
// Layout.
- std.def_class::<layout::PageNode>("page");
- std.def_class::<layout::PagebreakNode>("pagebreak");
- std.def_class::<layout::HNode>("h");
- std.def_class::<layout::VNode>("v");
- std.def_class::<layout::BoxNode>("box");
- std.def_class::<layout::BlockNode>("block");
- std.def_class::<layout::AlignNode>("align");
- std.def_class::<layout::PadNode>("pad");
- std.def_class::<layout::StackNode>("stack");
- std.def_class::<layout::GridNode>("grid");
- std.def_class::<layout::ColumnsNode>("columns");
- std.def_class::<layout::ColbreakNode>("colbreak");
- std.def_class::<layout::PlaceNode>("place");
+ std.def_node::<layout::PageNode>("page");
+ std.def_node::<layout::PagebreakNode>("pagebreak");
+ std.def_node::<layout::HNode>("h");
+ std.def_node::<layout::VNode>("v");
+ std.def_node::<layout::BoxNode>("box");
+ std.def_node::<layout::BlockNode>("block");
+ std.def_node::<layout::AlignNode>("align");
+ std.def_node::<layout::PadNode>("pad");
+ std.def_node::<layout::StackNode>("stack");
+ std.def_node::<layout::GridNode>("grid");
+ std.def_node::<layout::ColumnsNode>("columns");
+ std.def_node::<layout::ColbreakNode>("colbreak");
+ std.def_node::<layout::PlaceNode>("place");
// Graphics.
- std.def_class::<graphics::ImageNode>("image");
- std.def_class::<graphics::RectNode>("rect");
- std.def_class::<graphics::SquareNode>("square");
- std.def_class::<graphics::EllipseNode>("ellipse");
- std.def_class::<graphics::CircleNode>("circle");
- std.def_class::<graphics::MoveNode>("move");
- std.def_class::<graphics::ScaleNode>("scale");
- std.def_class::<graphics::RotateNode>("rotate");
- std.def_class::<graphics::HideNode>("hide");
+ std.def_node::<graphics::ImageNode>("image");
+ std.def_node::<graphics::RectNode>("rect");
+ std.def_node::<graphics::SquareNode>("square");
+ std.def_node::<graphics::EllipseNode>("ellipse");
+ std.def_node::<graphics::CircleNode>("circle");
+ std.def_node::<graphics::MoveNode>("move");
+ std.def_node::<graphics::ScaleNode>("scale");
+ std.def_node::<graphics::RotateNode>("rotate");
+ std.def_node::<graphics::HideNode>("hide");
// Math.
- std.def_class::<math::MathNode>("math");
+ std.def_node::<math::MathNode>("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 0cca718f..001798f3 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 f5565f3c..f1bc795f 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<Smart<FontFamily>> = Leveled::Value(Smart::Auto);
diff --git a/src/library/structure/list.rs b/src/library/structure/list.rs
index 2630d231..414f601e 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<ORDERED>;
-#[class]
+#[node(showable)]
impl<const L: ListKind> ListNode<L> {
/// 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 42b62eac..0e455ead 100644
--- a/src/library/structure/table.rs
+++ b/src/library/structure/table.rs
@@ -12,7 +12,7 @@ pub struct TableNode {
pub children: Vec<Content>,
}
-#[class]
+#[node(showable)]
impl TableNode {
/// The primary cell fill color.
pub const PRIMARY: Option<Paint> = None;
@@ -41,14 +41,15 @@ impl TableNode {
}))
}
- fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
+ fn set(args: &mut Args) -> TypResult<StyleMap> {
+ 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 608ebb18..29c04b2d 100644
--- a/src/library/text/deco.rs
+++ b/src/library/text/deco.rs
@@ -18,7 +18,7 @@ pub type StrikethroughNode = DecoNode<STRIKETHROUGH>;
/// Typeset overlined text.
pub type OverlineNode = DecoNode<OVERLINE>;
-#[class]
+#[node(showable)]
impl<const L: DecoLine> DecoNode<L> {
/// 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 e0041df5..4c2b5e7b 100644
--- a/src/library/text/link.rs
+++ b/src/library/text/link.rs
@@ -11,7 +11,7 @@ pub struct LinkNode {
pub body: Option<Content>,
}
-#[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 dbc486fb..8939a8c1 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<Content> {
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<Content> {
Ok(Content::show(Self(args.expect("body")?)))
diff --git a/src/library/text/par.rs b/src/library/text/par.rs
index dc888637..be4e8096 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<EcoString> = 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::<Option<EcoString>>("lang")?;
+ fn set(args: &mut Args) -> TypResult<StyleMap> {
+ let mut styles = StyleMap::new();
+ let lang = args.named::<Option<EcoString>>("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<Content> {
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<Content> {
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 988bd04e..e225803f 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<EcoString> = None;
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index f48b445c..928680e5 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 be502800..822b096d 100644
--- a/tests/ref/code/repr.png
+++ b/tests/ref/code/repr.png
Binary files differ
diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ
index e442e4d5..2e003b0a 100644
--- a/tests/typ/style/show.typ
+++ b/tests/typ/style/show.typ
@@ -34,16 +34,20 @@ 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
// #show strong(x) as strong(x)
diff --git a/tests/typeset.rs b/tests/typeset.rs
index 388d4516..dfc8bff9 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::<Value>("left-hand side")?;
let rhs = args.expect::<Value>("right-hand side")?;
if lhs != rhs {