diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-10-23 00:14:43 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-10-23 00:21:40 +0200 |
| commit | ecf0ff4d054f11c79ec0ddbbdf45f3dfcf9fc8d7 (patch) | |
| tree | efdc76915e5c7f43629aa3aa5d5aa3998d7f5367 /src/func/mod.rs | |
| parent | cff325b520727ac0eec46a3757831afaa0916cc1 (diff) | |
Introduce a set of macros for writing functions more concisely 🎁
Diffstat (limited to 'src/func/mod.rs')
| -rw-r--r-- | src/func/mod.rs | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/func/mod.rs b/src/func/mod.rs new file mode 100644 index 00000000..9a6fcbd1 --- /dev/null +++ b/src/func/mod.rs @@ -0,0 +1,187 @@ +//! Dynamic typesetting functions. + +use std::any::Any; +use std::collections::HashMap; +use std::fmt::{self, Debug, Formatter}; + +use crate::layout::{Layout, LayoutContext, Alignment, LayoutResult, MultiLayout}; +use crate::parsing::{ParseContext, ParseResult}; +use crate::style::TextStyle; +use crate::syntax::{FuncHeader, SyntaxTree}; + +#[macro_use] +mod helpers; +pub use helpers::Arguments; + +/// Useful imports for creating your own functions. +pub mod prelude { + pub use crate::func::{Command, CommandList, Function}; + pub use crate::layout::{layout_tree, Layout, LayoutContext, MultiLayout}; + pub use crate::layout::{Flow, Alignment, LayoutError, LayoutResult}; + pub use crate::parsing::{parse, ParseContext, ParseError, ParseResult}; + pub use crate::syntax::{Expression, FuncHeader, SyntaxTree}; + pub use crate::size::{Size, Size2D, SizeBox}; + pub use super::helpers::*; +} + +/// Typesetting function types. +/// +/// These types have to be able to parse tokens into themselves and store the +/// relevant information from the parsing to do their role in typesetting later. +/// +/// The trait `FunctionBounds` is automatically implemented for types which can +/// be used as functions, that is they fulfill the bounds `Debug + PartialEq + +/// 'static`. +pub trait Function: FunctionBounds { + /// 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; + + /// Layout this function given a 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>; +} + +impl PartialEq for dyn Function { + fn eq(&self, other: &dyn Function) -> 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 FunctionBounds: 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; +} + +impl<T> FunctionBounds for T +where T: Debug + PartialEq + 'static +{ + fn help_cast_as_any(&self) -> &dyn Any { + self + } + + fn help_eq(&self, other: &dyn Function) -> bool { + if let Some(other) = other.help_cast_as_any().downcast_ref::<Self>() { + self == other + } else { + false + } + } +} + +/// A sequence of commands requested for execution by a function. +#[derive(Debug)] +pub struct CommandList<'a> { + pub commands: Vec<Command<'a>>, +} + +impl<'a> CommandList<'a> { + /// Create an empty command list. + pub fn new() -> CommandList<'a> { + CommandList { commands: vec![] } + } + + /// Create a command list with commands from a vector. + pub fn from_vec(commands: Vec<Command<'a>>) -> CommandList<'a> { + CommandList { commands } + } + + /// Add a command to the sequence. + pub fn add(&mut self, command: Command<'a>) { + self.commands.push(command); + } + + /// Whether there are any commands in this sequence. + pub fn is_empty(&self) -> bool { + self.commands.is_empty() + } +} + +impl<'a> IntoIterator for CommandList<'a> { + type Item = Command<'a>; + type IntoIter = std::vec::IntoIter<Command<'a>>; + + fn into_iter(self) -> Self::IntoIter { + self.commands.into_iter() + } +} + +impl<'a> IntoIterator for &'a CommandList<'a> { + type Item = &'a Command<'a>; + type IntoIter = std::slice::Iter<'a, Command<'a>>; + + fn into_iter(self) -> Self::IntoIter { + self.commands.iter() + } +} + +/// Commands requested for execution by functions. +#[derive(Debug)] +pub enum Command<'a> { + Layout(&'a SyntaxTree), + Add(Layout), + AddMany(MultiLayout), + AddFlex(Layout), + SetAlignment(Alignment), + SetStyle(TextStyle), + FinishLayout, + FinishFlexRun, +} + +macro_rules! commands { + ($($x:expr),*$(,)*) => ({ + $crate::func::CommandList::from_vec(vec![$($x,)*]) + }); +} + +/// A map from identifiers to functions. +pub struct Scope { + parsers: HashMap<String, Box<ParseFunc>>, +} + +/// A function which parses a function invocation into a function type. +type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult<Box<dyn Function>>; + +impl Scope { + /// Create a new empty scope. + pub fn new() -> Scope { + Scope { + parsers: HashMap::new(), + } + } + + /// Create a new scope with the standard functions contained. + pub fn with_std() -> 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) { + 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>)), + ); + } + + /// Return the parser with the given name if there is one. + pub(crate) fn get_parser(&self, name: &str) -> Option<&ParseFunc> { + self.parsers.get(name).map(|x| &**x) + } +} + +impl Debug for Scope { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "Scope ")?; + write!(f, "{:?}", self.parsers.keys()) + } +} |
