summaryrefslogtreecommitdiff
path: root/src/func
diff options
context:
space:
mode:
Diffstat (limited to 'src/func')
-rw-r--r--src/func/args.rs215
-rw-r--r--src/func/helpers.rs203
-rw-r--r--src/func/macros.rs149
-rw-r--r--src/func/mod.rs121
4 files changed, 437 insertions, 251 deletions
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<T>(&mut self) -> ParseResult<Spanned<T::Output>>
+ where T: Argument<'a> {
+ Self::expected(self.get_pos_opt::<T>()?)
+ }
+
+ /// 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<T>(&mut self) -> ParseResult<Option<Spanned<T::Output>>>
+ 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<T>(&mut self, key: &str) -> ParseResult<Spanned<T::Output>>
+ where T: Argument<'a> {
+ Self::expected(self.get_key_opt::<T>(key)?)
+ }
+
+ /// Get a keyword argument with the given key and type if it is present.
+ pub fn get_key_opt<T>(&mut self, key: &str) -> ParseResult<Option<Spanned<T::Output>>>
+ 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<T>(val: Option<Spanned<T::Output>>) -> ParseResult<Spanned<T::Output>>
+ 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<Expression>) -> ParseResult<Spanned<Self::Output>>;
+}
+
+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<Expression>) -> ParseResult<Spanned<Self::Output>> {
+ #[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<Alignment> {
+ 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<A> {
+ /// 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<Self>
- 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<Self>
- 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<CommandList> {
- 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<T>(&mut self) -> ParseResult<Spanned<T::Output>> where T: Argument<'a> {
- self.get_pos_opt::<T>()?
- .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<T>(&mut self) -> ParseResult<Option<Spanned<T::Output>>>
- 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<T>(&mut self, key: &str) -> ParseResult<Spanned<T::Output>>
- where T: Argument<'a> {
- self.get_key_opt::<T>(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<T>(&mut self, key: &str) -> ParseResult<Option<Spanned<T::Output>>>
- 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<Expression>) -> ParseResult<Spanned<Self::Output>>;
-}
-
-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<Expression>) -> ParseResult<Spanned<Self::Output>> {
- #[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<Self> 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<Commands> {
+ 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<Self>
- where Self: Sized;
+ fn parse(
+ header: &FuncHeader,
+ body: Option<&str>,
+ ctx: ParseContext,
+ metadata: Self::Meta,
+ ) -> ParseResult<Self> 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<CommandList>;
+ /// Returns a sequence of layouting commands which describe what the
+ /// function is doing.
+ fn layout(&self, ctx: LayoutContext) -> LayoutResult<Commands>;
}
-impl dyn Function {
- /// Downcast a dynamic function to a concrete function type.
- pub fn downcast<F>(&self) -> Option<&F> where F: Function + 'static {
+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 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<T> FunctionBounds for T
-where T: Debug + PartialEq + 'static
-{
+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 Function) -> bool {
+ fn help_eq(&self, other: &dyn LayoutFunc) -> bool {
if let Some(other) = other.help_cast_as_any().downcast_ref::<Self>() {
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<Command<'a>>;
+/// A sequence of layouting commands.
+pub type Commands<'a> = Vec<Command<'a>>;
-/// 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<String, Box<ParseFunc>>,
+ parsers: HashMap<String, Box<Parser>>,
}
-/// A function which parses a function invocation into a function type.
-type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult<Box<dyn Function>>;
+/// 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>>;
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<F: Function + 'static>(&mut self, name: &str) {
+ /// 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 {
+ 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, T>(&mut self, name: &str, metadata: T)
+ where F: ParseFunc<Meta=T> + 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<dyn Function>)),
+ Box::new(|h, b, c| {
+ F::parse(h, b, c, metadata)
+ .map(|f| Box::new(f) as Box<dyn LayoutFunc>)
+ })
);
}
/// 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)
}
}