From 9fb31defd037a90bf8f9e38fa33acae23a70b269 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 4 Dec 2019 19:34:29 +0100 Subject: =?UTF-8?q?Expand=20functionality=20of=20function!=20macro=20?= =?UTF-8?q?=F0=9F=9B=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/func/args.rs | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/func/helpers.rs | 203 ------------------------------------------------- src/func/macros.rs | 149 ++++++++++++++++++++++++++++++++++++ src/func/mod.rs | 121 +++++++++++++++++------------ 4 files changed, 437 insertions(+), 251 deletions(-) create mode 100644 src/func/args.rs delete mode 100644 src/func/helpers.rs create mode 100644 src/func/macros.rs (limited to 'src/func') diff --git a/src/func/args.rs b/src/func/args.rs new file mode 100644 index 00000000..2e9e80cc --- /dev/null +++ b/src/func/args.rs @@ -0,0 +1,215 @@ +//! Parsing, storing and deduplication of function arguments. + +use super::prelude::*; +use Expression::*; + +/// Provides a convenient interface to parse the arguments to a function. +pub struct ArgParser<'a> { + args: &'a FuncArgs, + positional_index: usize, +} + +impl<'a> ArgParser<'a> { + pub fn new(args: &'a FuncArgs) -> ArgParser<'a> { + ArgParser { + args, + positional_index: 0, + } + } + + /// Get the next positional argument of the given type. + /// + /// If there are no more arguments or the type is wrong, + /// this will return an error. + pub fn get_pos(&mut self) -> ParseResult> + where T: Argument<'a> { + Self::expected(self.get_pos_opt::()?) + } + + /// Get the next positional argument if there is any. + /// + /// If the argument is of the wrong type, this will return an error. + pub fn get_pos_opt(&mut self) -> ParseResult>> + where T: Argument<'a> { + let arg = self.args.positional + .get(self.positional_index) + .map(T::from_expr) + .transpose(); + + if let Ok(Some(_)) = arg { + self.positional_index += 1; + } + + arg + } + + /// Get a keyword argument with the given key and type. + pub fn get_key(&mut self, key: &str) -> ParseResult> + where T: Argument<'a> { + Self::expected(self.get_key_opt::(key)?) + } + + /// Get a keyword argument with the given key and type if it is present. + pub fn get_key_opt(&mut self, key: &str) -> ParseResult>> + where T: Argument<'a> { + self.args.keyword.iter() + .find(|entry| entry.val.0.val == key) + .map(|entry| T::from_expr(&entry.val.1)) + .transpose() + } + + /// Assert that there are no positional arguments left. Returns an error + /// otherwise. + pub fn done(&self) -> ParseResult<()> { + if self.positional_index == self.args.positional.len() { + Ok(()) + } else { + pr!("unexpected argument"); + } + } + + /// Covert an option to a result with an error on `None`. + fn expected(val: Option>) -> ParseResult> + where T: Argument<'a> { + val.ok_or_else(|| pr!(@"expected {}", T::ERROR_MESSAGE)) + } +} + +/// A kind of argument. +pub trait Argument<'a> { + type Output; + const ERROR_MESSAGE: &'static str; + + fn from_expr(expr: &'a Spanned) -> ParseResult>; +} + +macro_rules! arg { + ($type:ident, $err:expr, $doc:expr, $output:ty, $wanted:pat => $converted:expr) => ( + #[doc = $doc] + #[doc = " argument for use with the [`ArgParser`]."] + pub struct $type; + impl<'a> Argument<'a> for $type { + type Output = $output; + const ERROR_MESSAGE: &'static str = $err; + + fn from_expr(expr: &'a Spanned) -> ParseResult> { + #[allow(unreachable_patterns)] + match &expr.val { + $wanted => Ok(Spanned::new($converted, expr.span)), + _ => pr!("expected {}", $err), + } + } + } + ); +} + +arg!(ArgExpr, "expression", "A generic expression", &'a Expression, expr => &expr); +arg!(ArgIdent, "identifier", "An identifier (e.g. `horizontal`)", &'a str, Ident(s) => s.as_str()); +arg!(ArgStr, "string", "A string (e.g. `\"Hello\"`)", &'a str, Str(s) => s.as_str()); +arg!(ArgNum, "number", "A number (e.g. `5.4`)", f64, Num(n) => *n); +arg!(ArgSize, "size", "A size (e.g. `12pt`)", crate::size::Size, Size(s) => *s); +arg!(ArgBool, "bool", "A boolean (`true` or `false`)", bool, Bool(b) => *b); + +/// An argument key which identifies a layouting axis. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum AxisKey { + Primary, + Secondary, + Vertical, + Horizontal, +} + +impl AxisKey { + /// The generic version of this axis key in the given system of axes. + pub fn generic(&self, axes: LayoutAxes) -> GenericAxisKind { + match self { + Primary => GenericAxisKind::Primary, + Secondary => GenericAxisKind::Secondary, + Vertical => axes.vertical(), + Horizontal => axes.horizontal(), + } + } + + /// The specific version of this axis key in the given system of axes. + pub fn specific(&self, axes: LayoutAxes) -> SpecificAxisKind { + match self { + Primary => axes.primary(), + Secondary => axes.secondary(), + Vertical => SpecificAxisKind::Vertical, + Horizontal => SpecificAxisKind::Horizontal, + } + } +} + +/// An argument key which identifies a target alignment. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum AlignmentKey { + Left, + Top, + Right, + Bottom, + Origin, + Center, + End, +} + +impl AlignmentKey { + /// The generic axis this alignment key corresopnds to in the given system + /// of layouting axes. Falls back to `default` if the alignment is generic. + pub fn axis(&self, axes: LayoutAxes, default: GenericAxisKind) -> GenericAxisKind { + use AlignmentKey::*; + match self { + Origin | Center | End => default, + Left | Right => axes.horizontal(), + Top | Bottom => axes.vertical(), + } + } + + /// The generic version of this alignment in the given system of layouting + /// axes. Returns an error if the alignment is invalid for the given axis. + pub fn generic(&self, axes: LayoutAxes, axis: GenericAxisKind) -> LayoutResult { + use AlignmentKey::*; + + let horizontal = axis == axes.horizontal(); + Ok(match self { + Origin => Alignment::Origin, + Center => Alignment::Center, + End => Alignment::End, + Left if horizontal => axes.left(), + Right if horizontal => axes.right(), + Top if !horizontal => axes.top(), + Bottom if !horizontal => axes.bottom(), + _ => lr!( + "invalid alignment `{}` for {} axis", + format!("{:?}", self).to_lowercase(), + format!("{:?}", axis).to_lowercase() + ) + }) + } + + /// The specific version of this alignment in the given system of layouting + /// axes. + pub fn specific(&self, axes: LayoutAxes, axis: SpecificAxisKind) -> AlignmentKey { + use AlignmentKey::*; + match (self, axis) { + (Origin, SpecificAxisKind::Horizontal) => Left, + (End, SpecificAxisKind::Horizontal) => Right, + (Origin, SpecificAxisKind::Vertical) => Top, + (End, SpecificAxisKind::Vertical) => Bottom, + _ => *self, + } + } +} + +/// An argument key which identifies a margin or padding target. +/// +/// A is the axis type used. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum PaddingKey { + /// All four sides should have the specified padding. + All, + /// Both sides of the given axis should have the specified padding. + Axis(A), + /// Only the given side of the given axis should have the specified padding. + AxisAligned(A, AlignmentKey), +} diff --git a/src/func/helpers.rs b/src/func/helpers.rs deleted file mode 100644 index c82a90e1..00000000 --- a/src/func/helpers.rs +++ /dev/null @@ -1,203 +0,0 @@ -//! Helper types and macros for creating custom functions. - -use super::prelude::*; -use Expression::*; - -/// Lets you implement the function trait more concisely. -#[macro_export] -macro_rules! function { - (data: $ident:ident, $($tts:tt)*) => ( - #[allow(unused_imports)] - use $crate::func::prelude::*; - - impl Function for $ident { - function!(@parse $ident, $($tts)*); - } - ); - - (@parse $ident:ident, parse: plain, $($tts:tt)*) => ( - fn parse(header: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult - where Self: Sized { - ArgParser::new(&header.args).done()?; - if body.is_some() { - perr!("expected no body"); - } - Ok($ident) - } - - function!(@layout $($tts)*); - ); - - ( - @parse $ident:ident, - parse($args:ident, $body:ident, $ctx:ident) - $block:block - $($tts:tt)* - ) => ( - fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult - where Self: Sized { - #[allow(unused_mut)] - let mut $args = ArgParser::new(&header.args); - let $body = body; - let $ctx = ctx; - $block - } - - function!(@layout $($tts)*); - ); - - (@layout layout($this:pat, $ctx:pat) $block:block) => ( - fn layout(&self, ctx: LayoutContext) -> LayoutResult { - let $ctx = ctx; - let $this = self; - $block - } - ); -} - -/// Parse the body of a function. -/// - If the function does not expect a body, use `forbidden`. -/// - If the function can have a body, use `optional`. -/// - If the function must have a body, use `required`. -#[macro_export] -macro_rules! parse { - (forbidden: $body:expr) => { - if $body.is_some() { - perr!("unexpected body"); - } - }; - - (optional: $body:expr, $ctx:expr) => ( - if let Some(body) = $body { - Some($crate::syntax::parse(body, $ctx)?) - } else { - None - } - ); - - (required: $body:expr, $ctx:expr) => ( - if let Some(body) = $body { - $crate::syntax::parse(body, $ctx)? - } else { - perr!("expected body"); - } - ) -} - -/// Early-return with a formatted parsing error or yield -/// an error expression without returning when prefixed with `@`. -#[macro_export] -macro_rules! perr { - (@$($tts:tt)*) => ($crate::syntax::ParseError::new(format!($($tts)*))); - ($($tts:tt)*) => (return Err(perr!(@$($tts)*));); -} - -/// Early-return with a formatted layouting error or yield -/// an error expression without returning when prefixed with `@`. -#[macro_export] -macro_rules! lerr { - (@$($tts:tt)*) => ($crate::layout::LayoutError::new(format!($($tts)*))); - ($($tts:tt)*) => (return Err(lerr!(@$($tts)*));); -} - - -/// Easy parsing of function arguments. -pub struct ArgParser<'a> { - args: &'a FuncArgs, - positional_index: usize, -} - -impl<'a> ArgParser<'a> { - pub fn new(args: &'a FuncArgs) -> ArgParser<'a> { - ArgParser { - args, - positional_index: 0, - } - } - - /// Get the next positional argument of the given type. - /// - /// If there are no more arguments or the type is wrong, - /// this will return an error. - pub fn get_pos(&mut self) -> ParseResult> where T: Argument<'a> { - self.get_pos_opt::()? - .ok_or_else(|| perr!(@"expected {}", T::ERROR_MESSAGE)) - } - - /// Get the next positional argument if there is any. - /// - /// If the argument is of the wrong type, this will return an error. - pub fn get_pos_opt(&mut self) -> ParseResult>> - where T: Argument<'a> { - let arg = self.args.positional - .get(self.positional_index) - .map(T::from_expr) - .transpose(); - - if let Ok(Some(_)) = arg { - self.positional_index += 1; - } - - arg - } - - /// Get a keyword argument with the given key and type. - pub fn get_key(&mut self, key: &str) -> ParseResult> - where T: Argument<'a> { - self.get_key_opt::(key)? - .ok_or_else(|| perr!(@"expected {}", T::ERROR_MESSAGE)) - } - - /// Get a keyword argument with the given key and type if it is present. - pub fn get_key_opt(&mut self, key: &str) -> ParseResult>> - where T: Argument<'a> { - self.args.keyword.iter() - .find(|entry| entry.val.0.val == key) - .map(|entry| T::from_expr(&entry.val.1)) - .transpose() - } - - /// Assert that there are no positional arguments left. Returns an error, otherwise. - pub fn done(&self) -> ParseResult<()> { - if self.positional_index == self.args.positional.len() { - Ok(()) - } else { - perr!("unexpected argument"); - } - } -} - -/// A kind of argument. -pub trait Argument<'a> { - type Output; - const ERROR_MESSAGE: &'static str; - - fn from_expr(expr: &'a Spanned) -> ParseResult>; -} - -macro_rules! arg { - ($type:ident, $err:expr, $doc:expr, $output:ty, $wanted:pat => $converted:expr) => ( - #[doc = $doc] - #[doc = " argument for use with the [`ArgParser`]."] - pub struct $type; - impl<'a> Argument<'a> for $type { - type Output = $output; - const ERROR_MESSAGE: &'static str = $err; - - fn from_expr(expr: &'a Spanned) -> ParseResult> { - #[allow(unreachable_patterns)] - match &expr.val { - $wanted => Ok(Spanned::new($converted, expr.span)), - _ => perr!("expected {}", $err), - } - } - } - ); -} - -arg!(ArgExpr, "expression", "A generic expression", &'a Expression, expr => &expr); -arg!(ArgIdent, "identifier", "An identifier (e.g. `horizontal`)", &'a str, Ident(s) => s.as_str()); -arg!(ArgStr, "string", "A string (e.g. `\"Hello\"`)", &'a str, Str(s) => s.as_str()); -arg!(ArgNum, "number", "A number (e.g. `5.4`)", f64, Num(n) => *n); -arg!(ArgSize, "size", "A size (e.g. `12pt`)", crate::size::Size, Size(s) => *s); -arg!(ArgBool, "bool", "A boolean (`true` or `false`)", bool, Bool(b) => *b); diff --git a/src/func/macros.rs b/src/func/macros.rs new file mode 100644 index 00000000..78cf1f56 --- /dev/null +++ b/src/func/macros.rs @@ -0,0 +1,149 @@ +//! 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)*); + }; + + // 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 a metadata type definition. + (@meta $type:ident | type Meta = $meta:ty; $($rest:tt)*) => { + function!(@parse $type $meta | $($rest)*); + }; + + // Set the metadata to `()` if there is not type definition. + (@meta $type:ident | $($rest:tt)*) => { + function!(@parse $type () | $($rest)*); + }; + + // 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)*); + }; + + // (1-arg) Parse a parse-definition with only the first argument. + (@parse $type:ident $meta:ty | parse($args:ident) $code:block $($rest:tt)*) => { + function!(@parse $type $meta | parse($args, _body, _ctx, _meta) $code $($rest)*); + }; + + // (2-arg) Parse a parse-definition with only the first two arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat) $code:block $($rest:tt)* + ) => { + function!(@parse $type $meta | parse($args, $body, _ctx, _meta) $code $($rest)*); + }; + + // (3-arg) Parse a parse-definition with only the first three arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat, $ctx:pat) $code:block $($rest:tt)* + ) => { + function!(@parse $type $meta | parse($args, $body, $ctx, _meta) $code $($rest)*); + }; + + // (4-arg) Parse a parse-definition with all four arguments. + (@parse $type:ident $meta:ty | + parse($args:ident, $body:pat, $ctx:pat, $metadata:pat) $code:block + $($rest:tt)* + ) => { + impl $crate::func::ParseFunc for $type { + type Meta = $meta; + + fn parse( + header: &FuncHeader, + $body: Option<&str>, + $ctx: ParseContext, + $metadata: Self::Meta, + ) -> ParseResult where Self: Sized { + let mut $args = $crate::func::args::ArgParser::new(&header.args); + let val = $code; + $args.done()?; + Ok(val) + } + } + + function!(@layout $type | $($rest)*); + }; + + // (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(&$this, $ctx: LayoutContext) -> LayoutResult { + Ok($code) + } + } + }; +} + +/// Parse the body of a function. +/// - If the function does not expect a body, use `parse!(forbidden: body)`. +/// - 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() { + pr!("unexpected body"); + } + }; + + (optional: $body:expr, $ctx:expr) => ( + if let Some(body) = $body { + Some($crate::syntax::parse(body, $ctx)?) + } else { + None + } + ); + + (expected: $body:expr, $ctx:expr) => ( + if let Some(body) = $body { + $crate::syntax::parse(body, $ctx)? + } else { + pr!("expected body"); + } + ) +} + +/// Early-return with a formatted parsing error or yield +/// an error expression without returning when prefixed with `@`. +#[macro_export] +macro_rules! pr { + (@$($tts:tt)*) => ($crate::syntax::ParseError::new(format!($($tts)*))); + ($($tts:tt)*) => (return Err(pr!(@$($tts)*));); +} + +/// Early-return with a formatted layouting error or yield +/// an error expression without returning when prefixed with `@`. +#[macro_export] +macro_rules! lr { + (@$($tts:tt)*) => ($crate::layout::LayoutError::new(format!($($tts)*))); + ($($tts:tt)*) => (return Err(lr!(@$($tts)*));); +} diff --git a/src/func/mod.rs b/src/func/mod.rs index 33a6c756..b16eecb8 100644 --- a/src/func/mod.rs +++ b/src/func/mod.rs @@ -7,53 +7,64 @@ use std::fmt::{self, Debug, Formatter}; use self::prelude::*; #[macro_use] -pub mod helpers; +pub mod macros; +pub mod args; /// Useful imports for creating your own functions. pub mod prelude { - pub use crate::func::{Command, CommandList, Function}; - pub use crate::layout::{layout_tree, Layout, MultiLayout, LayoutContext}; - pub use crate::layout::{LayoutSpace, LayoutSpaces, SpacingKind}; - pub use crate::layout::{LayoutAxes, Axis, AxisKind, LayoutAlignment, Alignment}; - pub use crate::layout::{LayoutError, LayoutResult}; + pub use Command::*; + pub use super::args::*; + pub use super::{Scope, ParseFunc, LayoutFunc, Command, Commands}; pub use crate::syntax::{SyntaxTree, FuncHeader, FuncArgs, Expression, Spanned, Span}; pub use crate::syntax::{parse, ParseContext, ParseError, ParseResult}; pub use crate::size::{Size, Size2D, SizeBox}; pub use crate::style::{PageStyle, TextStyle}; - pub use super::helpers::*; - pub use Command::*; + pub use crate::layout::{ + layout_tree, Layout, MultiLayout, + LayoutContext, LayoutSpace, LayoutSpaces, + LayoutAxes, Axis, GenericAxisKind, SpecificAxisKind, + LayoutAlignment, Alignment, + SpacingKind, + LayoutError, LayoutResult, + }; } -/// Typesetting function types. -/// -/// These types have to be able to parse themselves from a string and build -/// a list of layouting commands corresponding to the parsed source. -/// -/// This trait is a supertrait of `FunctionBounds` for technical reasons. The -/// trait `FunctionBounds` is automatically implemented for types which can -/// be used as functions, that is, all types which fulfill the bounds `Debug + PartialEq + -/// 'static`. -pub trait Function: FunctionBounds { +/// Types representing functions that are parsed from source code. +pub trait ParseFunc { + type Meta; + /// Parse the header and body into this function given a context. - fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult - where Self: Sized; + fn parse( + header: &FuncHeader, + body: Option<&str>, + ctx: ParseContext, + metadata: Self::Meta, + ) -> ParseResult where Self: Sized; +} - /// Layout this function given a context. +/// Types representing functions 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`. +pub trait LayoutFunc: LayoutFuncBounds { + /// Layout this function in a given context. /// - /// Returns optionally the resulting layout and a new context if changes to - /// the context should be made. - fn layout(&self, ctx: LayoutContext) -> LayoutResult; + /// Returns a sequence of layouting commands which describe what the + /// function is doing. + fn layout(&self, ctx: LayoutContext) -> LayoutResult; } -impl dyn Function { - /// Downcast a dynamic function to a concrete function type. - pub fn downcast(&self) -> Option<&F> where F: Function + 'static { +impl dyn LayoutFunc { + /// Downcast a function trait object to a concrete function type. + pub fn downcast(&self) -> Option<&F> where F: LayoutFunc + 'static { self.help_cast_as_any().downcast_ref::() } } -impl PartialEq for dyn Function { - fn eq(&self, other: &dyn Function) -> bool { +impl PartialEq for dyn LayoutFunc { + fn eq(&self, other: &dyn LayoutFunc) -> bool { self.help_eq(other) } } @@ -63,22 +74,20 @@ impl PartialEq for dyn 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 FunctionBounds: Debug { +pub trait LayoutFuncBounds: Debug { /// Cast self into `Any`. fn help_cast_as_any(&self) -> &dyn Any; - /// Compare self with another function. - fn help_eq(&self, other: &dyn Function) -> bool; + /// Compare self with another function trait object. + fn help_eq(&self, other: &dyn LayoutFunc) -> bool; } -impl FunctionBounds for T -where T: Debug + PartialEq + 'static -{ +impl LayoutFuncBounds for T where T: Debug + PartialEq + 'static { fn help_cast_as_any(&self) -> &dyn Any { self } - fn help_eq(&self, other: &dyn Function) -> bool { + fn help_eq(&self, other: &dyn LayoutFunc) -> bool { if let Some(other) = other.help_cast_as_any().downcast_ref::() { self == other } else { @@ -87,17 +96,17 @@ where T: Debug + PartialEq + 'static } } -/// A sequence of commands requested for execution by a function. -pub type CommandList<'a> = Vec>; +/// A sequence of layouting commands. +pub type Commands<'a> = Vec>; -/// Commands requested for execution by functions. +/// Layouting commands from functions to the typesetting engine. #[derive(Debug)] pub enum Command<'a> { LayoutTree(&'a SyntaxTree), Add(Layout), AddMultiple(MultiLayout), - AddSpacing(Size, SpacingKind, AxisKind), + AddSpacing(Size, SpacingKind, GenericAxisKind), FinishLine, FinishRun, @@ -110,13 +119,18 @@ pub enum Command<'a> { SetAxes(LayoutAxes), } -/// A map from identifiers to functions. +/// A map from identifiers to function parsers. pub struct Scope { - parsers: HashMap>, + parsers: HashMap>, } -/// A function which parses a function invocation into a function type. -type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult>; +/// 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>; impl Scope { /// Create a new empty scope. @@ -131,16 +145,27 @@ impl Scope { crate::library::std() } - /// Add a function type to the scope giving it a name. - pub fn add(&mut self, name: &str) { + /// Associate the given name with a type that is parseable into a function. + pub fn add(&mut self, name: &str) + where F: ParseFunc + LayoutFunc + 'static { + self.add_with_metadata::(name, ()); + } + + /// Add a parseable type with additional metadata that is given to the + /// parser (other than the default of `()`). + pub fn add_with_metadata(&mut self, name: &str, metadata: T) + where F: ParseFunc + LayoutFunc + 'static, T: 'static { self.parsers.insert( name.to_owned(), - Box::new(|h, b, c| F::parse(h, b, c).map(|func| Box::new(func) as Box)), + Box::new(|h, b, c| { + F::parse(h, b, c, metadata) + .map(|f| Box::new(f) as Box) + }) ); } /// Return the parser with the given name if there is one. - pub(crate) fn get_parser(&self, name: &str) -> Option<&ParseFunc> { + pub(crate) fn get_parser(&self, name: &str) -> Option<&Parser> { self.parsers.get(name).map(|x| &**x) } } -- cgit v1.2.3