From f279c52b503e9dacb558d8a46a75b42bc5222ee4 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 29 Apr 2019 00:12:36 +0200 Subject: =?UTF-8?q?Simple=20dynamic,=20scoped=20function=20parsing=20?= =?UTF-8?q?=F0=9F=93=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/func.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/func.rs (limited to 'src/func.rs') diff --git a/src/func.rs b/src/func.rs new file mode 100644 index 00000000..cbc2f58b --- /dev/null +++ b/src/func.rs @@ -0,0 +1,93 @@ +//! Dynamic typesetting functions. + +use std::any::Any; +use std::collections::HashMap; +use std::fmt::Debug; +use crate::syntax::{Token, FuncHeader, Expression}; +use crate::parsing::ParseError; + + +/// An optional iterator over the tokens of a function body. +pub type BodyTokens<'a> = Option> + 'a>>; + + +/// Types that act as functions. +/// +/// 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. +/// +/// `FunctionRequirements` is automatically implemented for types which +/// can be used as functions, that is they fulfill the bounds +/// `Debug + PartialEq + 'static`. +pub trait Function: FunctionRequirements + 'static { + /// Parse the function. + fn parse(header: &FuncHeader, tokens: BodyTokens<'_>, scope: &Scope) + -> Result where Self: Sized; + + /// Execute the function and optionally yield a return value. + fn typeset(&self, header: &FuncHeader) -> Option; +} + +/// A helper trait that describes requirements for types 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 FunctionRequirements: 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 FunctionRequirements 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 == other + } else { + false + } + } +} + +impl PartialEq for dyn Function { + fn eq(&self, other: &dyn Function) -> bool { + self.help_eq(other) + } +} + +/// A map from identifiers to functions. +pub struct Scope { + parsers: HashMap, &Scope) + -> Result, ParseError>>>, +} + +impl Scope { + /// Create a new empty scope. + pub fn new() -> Scope { + Scope { parsers: HashMap::new() } + } + + /// Add a function type to the scope with a given name. + pub fn add(&mut self, name: &str) { + self.parsers.insert( + name.to_owned(), + Box::new(|header, tokens, scope| match F::parse(header, tokens, scope) { + Ok(func) => Ok(Box::new(func)), + Err(err) => Err(err), + }) + ); + } + + /// Return the parser with the given name if there is one. + pub fn get_parser(&self, name: &str) + -> Option<&dyn Fn(&FuncHeader, BodyTokens<'_>, &Scope) + -> Result, ParseError>> { + self.parsers.get(name).map(|x| &**x) + } +} -- cgit v1.2.3