summaryrefslogtreecommitdiff
path: root/src/func.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-04-29 00:12:36 +0200
committerLaurenz <laurmaedje@gmail.com>2019-04-29 00:12:36 +0200
commitf279c52b503e9dacb558d8a46a75b42bc5222ee4 (patch)
treeb70dd229877d18b5db45e88966d6114993281dad /src/func.rs
parenta51e7a0758758d92ce3655896db6c56d5c1013cf (diff)
Simple dynamic, scoped function parsing 📥
Diffstat (limited to 'src/func.rs')
-rw-r--r--src/func.rs93
1 files changed, 93 insertions, 0 deletions
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<Box<dyn Iterator<Item=Token<'a>> + '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<Self, ParseError> where Self: Sized;
+
+ /// Execute the function and optionally yield a return value.
+ fn typeset(&self, header: &FuncHeader) -> Option<Expression>;
+}
+
+/// 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<T> 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>() {
+ 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<String, Box<dyn Fn(&FuncHeader, BodyTokens<'_>, &Scope)
+ -> Result<Box<dyn Function>, 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<F: Function + 'static>(&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<Box<dyn Function>, ParseError>> {
+ self.parsers.get(name).map(|x| &**x)
+ }
+}