diff options
Diffstat (limited to 'src/func')
| -rw-r--r-- | src/func/macros.rs | 202 | ||||
| -rw-r--r-- | src/func/mod.rs | 132 |
2 files changed, 105 insertions, 229 deletions
diff --git a/src/func/macros.rs b/src/func/macros.rs index 90c3b11e..1e92d735 100644 --- a/src/func/macros.rs +++ b/src/func/macros.rs @@ -1,131 +1,76 @@ //! Helper types and macros for creating custom functions. -/// Defines function types concisely. #[macro_export] macro_rules! function { - // Parse a unit struct. - ($(#[$outer:meta])* pub struct $type:ident; $($rest:tt)*) => { - $(#[$outer])* pub struct $type; - function!(@meta $type | $($rest)*); + // Entry point. + ($(#[$outer:meta])* $v:vis $storage:ident $name:ident $($r:tt)*) => { + function!(@def($name) $(#[$outer])* $v $storage $name $($r)*); }; - - // Parse a tuple struct. - ($(#[$outer:meta])* pub struct $type:ident($($fields:tt)*); $($rest:tt)*) => { - $(#[$outer])* pub struct $type($($fields)*); - function!(@meta $type | $($rest)*); - }; - - // Parse a struct with fields. - ($(#[$outer:meta])* pub struct $type:ident { $($fields:tt)* } $($rest:tt)*) => { - $(#[$outer])* pub struct $type { $($fields)* } - function!(@meta $type | $($rest)*); - }; - - // Parse an enum. - ($(#[$outer:meta])* pub enum $type:ident { $($fields:tt)* } $($rest:tt)*) => { - $(#[$outer])* pub enum $type { $($fields)* } - function!(@meta $type | $($rest)*); - }; - - // Parse a metadata type definition. - (@meta $type:ident | type Meta = $meta:ty; $($rest:tt)*) => { - function!(@parse $type $meta | $($rest)*); + (@def($name:ident) $definition:item $($r:tt)*) => { + $definition + function!(@meta($name) $($r)*); }; - // Set the metadata to `()` if there is no type definition. - (@meta $type:ident | $($rest:tt)*) => { - function!(@parse $type () | $($rest)*); + // Metadata. + (@meta($name:ident) type Meta = $meta:ty; $($r:tt)*) => { + function!(@parse($name, $meta) $($r)*); }; - - // Parse a `parse(default)`. - (@parse $type:ident $meta:ty | parse(default) $($rest:tt)*) => { - function!(@parse $type $meta | - parse(_args, _body, _ctx, _meta) { Default::default() } - $($rest)* - ); - }; - - // (0-arg) Parse a parse-definition without arguments. - (@parse $type:ident $meta:ty | parse() $code:block $($rest:tt)*) => { - function!(@parse $type $meta | parse(_args, _body, _ctx, _meta) $code $($rest)*); + (@meta($name:ident) $($r:tt)*) => { + function!(@parse($name, ()) $($r)*); }; - // (1-arg) Parse a parse-definition with only the first argument. - (@parse $type:ident $meta:ty | parse($header:ident) $code:block $($rest:tt)*) => { - function!(@parse $type $meta | parse($header, _body, _ctx, _meta) $code $($rest)*); + // Parse trait. + (@parse($($a:tt)*) parse(default) $($r:tt)*) => { + function!(@parse($($a)*) parse() { Default::default() } $($r)*); }; - - // (2-arg) Parse a parse-definition with only the first two arguments. - (@parse $type:ident $meta:ty | - parse($header:ident, $body:pat) $code:block $($rest:tt)* - ) => { - function!(@parse $type $meta | parse($header, $body, _ctx, _meta) $code $($rest)*); + (@parse($($a:tt)*) parse($h:ident, $b:ident, $c:ident, $e:ident, $d:ident) $($r:tt)* ) => { + function!(@parse($($a)*) parse($h, $b, $c, $e, $d, _metadata) $($r)*); }; - - // (3-arg) Parse a parse-definition with only the first three arguments. - (@parse $type:ident $meta:ty | - parse($header:ident, $body:pat, $ctx:pat) $code:block $($rest:tt)* - ) => { - function!(@parse $type $meta | parse($header, $body, $ctx, _meta) $code $($rest)*); - }; - - // (4-arg) Parse a parse-definition with all four arguments. - (@parse $type:ident $meta:ty | - parse($header:ident, $body:pat, $ctx:pat, $metadata:pat) $code:block - $($rest:tt)* - ) => { - impl $crate::func::ParseFunc for $type { + (@parse($name:ident, $meta:ty) parse( + $header:ident, + $body:ident, + $ctx:ident, + $errors:ident, + $decos:ident, + $metadata:ident + ) $code:block $($r:tt)*) => { + impl $crate::func::Parse for $name { type Meta = $meta; fn parse( - header: $crate::syntax::FuncHeader, - $body: Option<&str>, - $ctx: $crate::syntax::ParseContext, - $metadata: Self::Meta, - ) -> $crate::syntax::ParseResult<Self> where Self: Sized { - #[allow(unused_mut)] - let mut $header = header; - let val = $code; - if !$header.args.is_empty() { - return Err($crate::TypesetError::with_message("unexpected arguments")); - } - Ok(val) + #[allow(unused)] mut $header: FuncHeader, + #[allow(unused)] $body: Option<Spanned<&str>>, + #[allow(unused)] $ctx: ParseContext, + #[allow(unused)] $metadata: Self::Meta, + ) -> Parsed<Self> where Self: Sized { + #[allow(unused)] let mut $errors = vec![]; + #[allow(unused)] let mut $decos = vec![]; + let output = $code; + $crate::syntax::Parsed { output, errors: $errors, decorations: $decos } } } - function!(@layout $type | $($rest)*); + function!(@layout($name) $($r)*); }; - // (0-arg) Parse a layout-definition without arguments. - (@layout $type:ident | layout() $code:block) => { - function!(@layout $type | layout(self, _ctx) $code); - }; - - // (1-arg) Parse a layout-definition with only the first argument. - (@layout $type:ident | layout($this:ident) $code:block) => { - function!(@layout $type | layout($this, _ctx) $code); - }; - - // (2-arg) Parse a layout-definition with all arguments. - (@layout $type:ident | layout($this:ident, $ctx:pat) $code:block) => { - impl $crate::func::LayoutFunc for $type { - fn layout<'a, 'life0, 'life1, 'async_trait>( - &'a $this, - $ctx: $crate::layout::LayoutContext<'life0, 'life1> - ) -> std::pin::Pin<Box<dyn std::future::Future< - Output = $crate::layout::LayoutResult< - $crate::func::Commands<'a>> - > + 'async_trait - >> + (@layout($name:ident) layout($this:ident, $ctx:ident, $errors:ident) $code:block) => { + impl $crate::syntax::Model for $name { + fn layout<'a, 'b, 'c, 't>( + #[allow(unused)] &'a $this, + #[allow(unused)] $ctx: $crate::layout::LayoutContext<'b, 'c>, + ) -> $crate::syntax::DynFuture<'t, $crate::layout::Layouted<$crate::func::Commands<'a>>> where - 'a: 'async_trait, - 'life0: 'async_trait, - 'life1: 'async_trait, - Self: 'async_trait, + 'a: 't, + 'b: 't, + 'c: 't, + Self: 't, { - #[allow(unreachable_code)] - Box::pin(async move { Ok($code) }) + Box::pin(async move { + #[allow(unused)] let mut $errors = vec![]; + let output = $code; + $crate::layout::Layouted { output, errors: $errors } + }) } } }; @@ -137,35 +82,30 @@ macro_rules! function { /// - If the function can have a body, use `parse!(optional: body, ctx)`. /// - If the function must have a body, use `parse!(expected: body, ctx)`. #[macro_export] -macro_rules! parse { - (forbidden: $body:expr) => { - if $body.is_some() { - return Err($crate::TypesetError::with_message("unexpected body")); - } - }; - - (optional: $body:expr, $ctx:expr) => ( +macro_rules! body { + (opt: $body:expr, $ctx:expr, $errors:expr, $decos:expr) => ({ + $body.map(|body| { + let parsed = $crate::syntax::parse(body.span.start, body.v, $ctx); + $errors.extend(parsed.errors); + $decos.extend(parsed.decorations); + parsed.output + }) + }); + + (nope: $body:expr, $errors:expr) => { if let Some(body) = $body { - Some($crate::syntax::parse(body, $ctx).0) - } else { - None + $errors.push($crate::err!(body.span, "unexpected body")); } - ); - - (expected: $body:expr, $ctx:expr) => ( - if let Some(body) = $body { - $crate::syntax::parse(body, $ctx).0 - } else { - Err($crate::TypesetError::with_message("unexpected body")) - } - ) + }; } -/// Early-return with a formatted typesetting error or construct an error -/// expression. +/// Construct a spanned error. #[macro_export] -macro_rules! error { - (@unexpected_argument) => (error!(@"unexpected argument")); - (@$($tts:tt)*) => ($crate::TypesetError::with_message(format!($($tts)*))); - ($($tts:tt)*) => (return Err(error!(@$($tts)*));); +macro_rules! err { + ($span:expr, $($args:tt)*) => { + $crate::syntax::Spanned { + v: $crate::error::Error::new(format!($($args)*)), + span: $span, + } + }; } diff --git a/src/func/mod.rs b/src/func/mod.rs index bfc2774c..1ca226c3 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -3,7 +3,6 @@ use std::any::Any; use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; -use async_trait::async_trait; use self::prelude::*; @@ -12,81 +11,34 @@ mod macros; /// Useful imports for creating your own functions. pub mod prelude { - pub use crate::func::{Scope, ParseFunc, LayoutFunc, Command, Commands}; + pub use super::{Scope, Parse, Command, Commands}; pub use crate::layout::prelude::*; - pub use crate::syntax::*; + pub use crate::syntax::prelude::*; pub use crate::size::{Size, Size2D, SizeBox, ValueBox, ScaleSize, FSize, PSize}; pub use crate::style::{LayoutStyle, PageStyle, TextStyle}; pub use Command::*; } -/// Types representing functions that are parsed from source code. -pub trait ParseFunc { +/// Parse a function from source code. +pub trait Parse { type Meta: Clone; /// Parse the header and body into this function given a context. fn parse( header: FuncHeader, - body: Option<&str>, + body: Option<Spanned<&str>>, ctx: ParseContext, metadata: Self::Meta, - ) -> ParseResult<Self> where Self: Sized; + ) -> Parsed<Self> where Self: Sized; } -/// Function types which can be laid out in a layout context. -/// -/// This trait is a supertrait of `[LayoutFuncBounds]` for technical reasons. -/// The trait `[LayoutFuncBounds]` is automatically implemented for types which -/// can be used as functions, that is, all types which fulfill the bounds `Debug -/// + PartialEq + 'static`. -#[async_trait(?Send)] -pub trait LayoutFunc: LayoutFuncBounds { - /// Layout this function in a given context. - /// - /// Returns a sequence of layouting commands which describe what the - /// function is doing. - async fn layout<'a>(&'a self, ctx: LayoutContext<'_, '_>) -> LayoutResult<Commands<'a>>; -} - -impl dyn LayoutFunc { - /// Downcast a function trait object to a concrete function type. - pub fn downcast<F>(&self) -> Option<&F> where F: LayoutFunc + 'static { - self.help_cast_as_any().downcast_ref::<F>() - } -} - -impl PartialEq for dyn LayoutFunc { - fn eq(&self, other: &dyn LayoutFunc) -> bool { - self.help_eq(other) - } -} - -/// A helper trait that describes requirements for types that can implement -/// [`Function`]. -/// -/// Automatically implemented for all types which fulfill to the bounds `Debug + -/// PartialEq + 'static`. There should be no need to implement this manually. -pub trait LayoutFuncBounds: Debug { - /// Cast self into `Any`. - fn help_cast_as_any(&self) -> &dyn Any; - - /// Compare self with another function trait object. - fn help_eq(&self, other: &dyn LayoutFunc) -> bool; -} - -impl<T> LayoutFuncBounds for T where T: Debug + PartialEq + 'static { - fn help_cast_as_any(&self) -> &dyn Any { - self - } - - fn help_eq(&self, other: &dyn LayoutFunc) -> bool { - if let Some(other) = other.help_cast_as_any().downcast_ref::<Self>() { - self == other - } else { - false - } - } -} +/// A function which parses the source of a function into a model type which +/// implements [`Model`]. +type Parser = dyn Fn( + FuncHeader, + Option<Spanned<&str>>, + ParseContext, +) -> Parsed<Box<dyn Model>>; /// A sequence of layouting commands. pub type Commands<'a> = Vec<Command<'a>>; @@ -94,7 +46,7 @@ pub type Commands<'a> = Vec<Command<'a>>; /// Layouting commands from functions to the typesetting engine. #[derive(Debug)] pub enum Command<'a> { - LayoutTree(&'a SyntaxTree), + LayoutSyntaxModel(&'a SyntaxModel), Add(Layout), AddMultiple(MultiLayout), @@ -114,41 +66,17 @@ pub enum Command<'a> { /// A map from identifiers to function parsers. pub struct Scope { parsers: HashMap<String, Box<Parser>>, - debug: Option<Box<Parser>> -} - -/// A function which parses the source of a function into a function type which -/// implements [`LayoutFunc`]. -type Parser = dyn Fn( - FuncHeader, - Option<&str>, - ParseContext -) -> ParseResult<Box<dyn LayoutFunc>>; - -fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<Parser> -where F: ParseFunc + LayoutFunc + 'static { - Box::new(move |a, b, c| { - F::parse(a, b, c, metadata.clone()) - .map(|f| Box::new(f) as Box<dyn LayoutFunc>) - }) + fallback: Box<Parser> } impl Scope { - /// Create a new empty scope. - pub fn new() -> Scope { - Scope { - parsers: HashMap::new(), - debug: None, - } - } - - /// Create a new scope with a debug parser that is invoked if not other + /// Create a new empty scope with a fallback parser that is invoked when no /// match is found. - pub fn with_debug<F>() -> Scope - where F: ParseFunc<Meta=()> + LayoutFunc + 'static { + pub fn new<F>() -> Scope + where F: Parse<Meta=()> + Model + 'static { Scope { parsers: HashMap::new(), - debug: Some(make_parser::<F>(())), + fallback: parser::<F>(()), } } @@ -159,24 +87,25 @@ impl Scope { /// Associate the given name with a type that is parseable into a function. pub fn add<F>(&mut self, name: &str) - where F: ParseFunc<Meta=()> + LayoutFunc + 'static { + where F: Parse<Meta=()> + Model + 'static { self.add_with_metadata::<F>(name, ()); } /// Add a parseable type with additional metadata that is given to the /// parser (other than the default of `()`). - pub fn add_with_metadata<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta) - where F: ParseFunc + LayoutFunc + 'static { + pub fn add_with_metadata<F>(&mut self, name: &str, metadata: <F as Parse>::Meta) + where F: Parse + Model + 'static { self.parsers.insert( name.to_owned(), - make_parser::<F>(metadata), + parser::<F>(metadata), ); } /// Return the parser with the given name if there is one. - pub(crate) fn get_parser(&self, name: &str) -> Option<&Parser> { - self.parsers.get(name).map(|x| &**x) - .or(self.debug.as_ref().map(|x| &**x)) + pub(crate) fn get_parser(&self, name: &str) -> Result<&Parser, &Parser> { + self.parsers.get(name) + .map(|x| &**x) + .ok_or_else(|| &*self.fallback) } } @@ -186,3 +115,10 @@ impl Debug for Scope { write!(f, "{:?}", self.parsers.keys()) } } + +fn parser<F>(metadata: <F as Parse>::Meta) -> Box<Parser> where F: Parse + Model + 'static { + Box::new(move |h, b, c| { + F::parse(h, b, c, metadata.clone()) + .map(|model| Box::new(model) as Box<dyn Model>) + }) +} |
