summaryrefslogtreecommitdiff
path: root/src/func
diff options
context:
space:
mode:
Diffstat (limited to 'src/func')
-rw-r--r--src/func/macros.rs202
-rw-r--r--src/func/mod.rs132
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>)
+ })
+}