From f279c52b503e9dacb558d8a46a75b42bc5222ee4 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 29 Apr 2019 00:12:36 +0200 Subject: [PATCH] =?UTF-8?q?Simple=20dynamic,=20scoped=20function=20parsing?= =?UTF-8?q?=20=F0=9F=93=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/mod.rs | 32 ++-- src/func.rs | 93 ++++++++++++ src/lib.rs | 3 +- src/parsing.rs | 364 ++++++++++++++++++++++++++++++++++------------ src/syntax.rs | 73 +++------- 5 files changed, 404 insertions(+), 161 deletions(-) create mode 100644 src/func.rs diff --git a/src/engine/mod.rs b/src/engine/mod.rs index ea81053b2..37fce8dc4 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -14,13 +14,13 @@ pub use size::Size; /// The core typesetting engine, transforming an abstract syntax tree into a document. -pub struct Engine<'t> { +pub struct Engine<'a> { // Input - tree: &'t SyntaxTree<'t>, - ctx: &'t Context<'t>, + tree: &'a SyntaxTree, + ctx: &'a Context<'a>, // Internal - font_loader: FontLoader<'t>, + font_loader: FontLoader<'a>, // Output text_commands: Vec, @@ -34,9 +34,9 @@ pub struct Engine<'t> { italic: bool, } -impl<'t> Engine<'t> { +impl<'a> Engine<'a> { /// Create a new generator from a syntax tree. - pub(crate) fn new(tree: &'t SyntaxTree<'t>, context: &'t Context<'t>) -> Engine<'t> { + pub(crate) fn new(tree: &'a SyntaxTree, context: &'a Context<'a>) -> Engine<'a> { Engine { tree, ctx: context, @@ -211,30 +211,30 @@ impl<'t> Engine<'t> { } /// Serves matching fonts given a query. -struct FontLoader<'t> { +struct FontLoader<'a> { /// The context containing the used font providers. - context: &'t Context<'t>, + context: &'a Context<'a>, /// All available fonts indexed by provider. - provider_fonts: Vec<&'t [FontInfo]>, + provider_fonts: Vec<&'a [FontInfo]>, /// The internal state. - state: RefCell>, + state: RefCell>, } /// Internal state of the font loader (wrapped in a RefCell). -struct FontLoaderState<'t> { +struct FontLoaderState<'a> { /// The loaded fonts along with their external indices. fonts: Vec<(Option, Font)>, /// Allows to retrieve cached results for queries. - query_cache: HashMap, usize>, + query_cache: HashMap, usize>, /// Allows to lookup fonts by their infos. - info_cache: HashMap<&'t FontInfo, usize>, + info_cache: HashMap<&'a FontInfo, usize>, /// Indexed by outside and indices maps to internal indices. inner_index: Vec, } -impl<'t> FontLoader<'t> { +impl<'a> FontLoader<'a> { /// Create a new font loader. - pub fn new(context: &'t Context<'t>) -> FontLoader { + pub fn new(context: &'a Context<'a>) -> FontLoader { let provider_fonts = context.font_providers.iter() .map(|prov| prov.available()).collect(); @@ -251,7 +251,7 @@ impl<'t> FontLoader<'t> { } /// Return the best matching font and it's index (if there is any) given the query. - pub fn get(&self, query: FontQuery<'t>) -> Option<(usize, Ref)> { + pub fn get(&self, query: FontQuery<'a>) -> Option<(usize, Ref)> { // Check if we had the exact same query before. let state = self.state.borrow(); if let Some(&index) = state.query_cache.get(&query) { diff --git a/src/func.rs b/src/func.rs new file mode 100644 index 000000000..cbc2f58b7 --- /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) + } +} diff --git a/src/lib.rs b/src/lib.rs index f9883a871..423aeb8de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,7 @@ pub mod engine; pub mod export; #[macro_use] pub mod font; +pub mod func; pub mod parsing; pub mod syntax; @@ -105,7 +106,7 @@ impl<'p> Compiler<'p> { impl<'p> Compiler<'p> { /// Parse source code into a syntax tree. #[inline] - pub fn parse<'s>(&self, src: &'s str) -> Result, ParseError> { + pub fn parse(&self, src: &str) -> Result { Parser::new(Tokens::new(src)).parse() } diff --git a/src/parsing.rs b/src/parsing.rs index 9d921fe18..7c4d60414 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -4,8 +4,10 @@ use std::collections::HashMap; use std::fmt; use std::iter::Peekable; use std::mem::swap; +use std::ops::Deref; use unicode_segmentation::{UnicodeSegmentation, UWordBounds}; use crate::syntax::*; +use crate::func::{Scope, BodyTokens}; use crate::utility::{Splinor, Spline, Splined, StrExt}; @@ -205,12 +207,12 @@ impl<'s> Tokens<'s> { } /// Transforms token streams to syntax trees. -#[derive(Debug)] pub struct Parser<'s, T> where T: Iterator> { tokens: Peekable, + scope: ParserScope<'s>, state: ParserState, stack: Vec, - tree: SyntaxTree<'s>, + tree: SyntaxTree, } /// The state the parser is in. @@ -226,11 +228,39 @@ enum ParserState { Function, } +/// An owned or shared scope. +enum ParserScope<'s> { + Owned(Scope), + Shared(&'s Scope) +} + +impl Deref for ParserScope<'_> { + type Target = Scope; + + fn deref(&self) -> &Scope { + match self { + ParserScope::Owned(scope) => &scope, + ParserScope::Shared(scope) => scope, + } + } +} + impl<'s, T> Parser<'s, T> where T: Iterator> { /// Create a new parser from a type that emits results of tokens. - pub(crate) fn new(tokens: T) -> Parser<'s, T> { + pub fn new(tokens: T) -> Parser<'s, T> { + Parser::new_internal(ParserScope::Owned(Scope::new()), tokens) + } + + /// Create a new parser with a scope containing function definitions. + pub fn with_scope(scope: &'s Scope, tokens: T) -> Parser<'s, T> { + Parser::new_internal(ParserScope::Shared(scope), tokens) + } + + /// Internal helper for construction. + fn new_internal(scope: ParserScope<'s>, tokens: T) -> Parser<'s, T> { Parser { tokens: tokens.peekable(), + scope, state: ParserState::Body, stack: vec![], tree: SyntaxTree::new(), @@ -238,7 +268,7 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } /// Parse into an abstract syntax tree. - pub(crate) fn parse(mut self) -> ParseResult> { + pub(crate) fn parse(mut self) -> ParseResult { use ParserState as PS; while let Some(token) = self.tokens.peek() { @@ -275,7 +305,7 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { Token::Newline => self.switch_consumed(PS::FirstNewline), // Words - Token::Word(word) => self.append_consumed(Node::Word(word)), + Token::Word(word) => self.append_consumed(Node::Word(word.to_owned())), // Functions Token::LeftBracket => self.switch_consumed(PS::Function), @@ -283,7 +313,7 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { self.advance(); match self.stack.pop() { Some(func) => self.append(Node::Func(func)), - None => return self.err("unexpected closing bracket"), + None => return Err(ParseError::new("unexpected closing bracket")), } }, @@ -300,46 +330,69 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { let name = if let Token::Word(word) = token { match Ident::new(word) { Some(ident) => ident, - None => return self.err("invalid identifier"), + None => return Err(ParseError::new("invalid identifier")), } } else { - return self.err("expected identifier"); + return Err(ParseError::new("expected identifier")); }; - self.advance(); + + // Expect the header closing bracket. if self.tokens.next() != Some(Token::RightBracket) { - return self.err("expected closing bracket"); + return Err(ParseError::new("expected closing bracket")); } + // Store the header information of the function invocation. let header = FuncHeader { - name, + name: name.clone(), args: vec![], kwargs: HashMap::new(), }; - let func = FuncInvocation { - header, - body: unimplemented!(), + // This function has a body. + let mut tokens = if let Some(Token::LeftBracket) = self.tokens.peek() { + self.advance(); + Some(FuncTokens::new(&mut self.tokens)) + } else { + None }; - // This function has a body. - if let Some(Token::LeftBracket) = self.tokens.peek() { - self.advance(); - func.body = Some(SyntaxTree::new()); - self.stack.push(func); + // A mutably borrowed view over the tokens. + let borrow_tokens: BodyTokens<'_> = tokens.as_mut().map(|toks| { + Box::new(toks) as Box>> + }); + + // Run the parser over the tokens. + let body = if let Some(parser) = self.scope.get_parser(&name) { + parser(&header, borrow_tokens, &self.scope)? } else { - self.append(Node::Func(func)); + let message = format!("unknown function: '{}'", &name); + return Err(ParseError::new(message)); + }; + + // Expect the closing bracket if it had a body. + if let Some(tokens) = tokens { + println!("lulz"); + if tokens.unexpected_end { + return Err(ParseError::new("expected closing bracket")); + } } + // Finally this function is parsed to the end. + self.append(Node::Func(FuncInvocation { + header, + body, + })); + self.switch(PS::Body); }, } } - if !self.stack.is_empty() { - return self.err("expected closing bracket"); - } + // if !self.stack.is_empty() { + // return Err(ParseError::new("expected closing bracket")); + // } Ok(self.tree) } @@ -350,15 +403,16 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } /// Append a node to the top-of-stack function or the main tree itself. - fn append(&mut self, node: Node<'s>) { - match self.stack.last_mut() { - Some(func) => func.body.as_mut().unwrap(), - None => &mut self.tree, - }.nodes.push(node); + fn append(&mut self, node: Node) { + self.tree.nodes.push(node); + // match self.stack.last_mut() { + // Some(func) => func.body.as_mut().unwrap(), + // None => &mut self.tree, + // }.nodes.push(node); } /// Advance and return the given node. - fn append_consumed(&mut self, node: Node<'s>) { self.advance(); self.append(node); } + fn append_consumed(&mut self, node: Node) { self.advance(); self.append(node); } /// Append a space if there is not one already. fn append_space(&mut self) { @@ -379,11 +433,12 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { fn switch_consumed(&mut self, state: ParserState) { self.advance(); self.state = state; } /// The last appended node of the top-of-stack function or of the main tree. - fn last(&self) -> Option<&Node<'s>> { - match self.stack.last() { - Some(func) => func.body.as_ref().unwrap(), - None => &self.tree, - }.nodes.last() + fn last(&self) -> Option<&Node> { + self.tree.nodes.last() + // match self.stack.last() { + // Some(func) => func.body.as_ref().unwrap(), + // None => &self.tree, + // }.nodes.last() } /// Skip tokens until the condition is met. @@ -395,10 +450,47 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { self.advance(); } } +} - /// Gives a parsing error with a message. - fn err>(&self, message: S) -> ParseResult { - Err(ParseError { message: message.into() }) +/// A token iterator that that stops after the first unbalanced right paren. +pub struct FuncTokens<'s, T> where T: Iterator> { + tokens: T, + parens: u32, + unexpected_end: bool, +} + +impl<'s, T> FuncTokens<'s, T> where T: Iterator> { + /// Create a new iterator operating over an existing one. + pub fn new(tokens: T) -> FuncTokens<'s, T> { + FuncTokens { + tokens, + parens: 0, + unexpected_end: false, + } + } +} + +impl<'s, T> Iterator for FuncTokens<'s, T> where T: Iterator> { + type Item = Token<'s>; + + fn next(&mut self) -> Option> { + let token = self.tokens.next(); + match token { + Some(Token::RightBracket) if self.parens == 0 => None, + Some(Token::RightBracket) => { + self.parens -= 1; + token + }, + Some(Token::LeftBracket) => { + self.parens += 1; + token + } + None => { + self.unexpected_end = true; + None + } + token => token, + } } } @@ -407,6 +499,12 @@ pub struct ParseError { message: String, } +impl ParseError { + fn new>(message: S) -> ParseError { + ParseError { message: message.into() } + } +} + error_type! { err: ParseError, res: ParseResult, @@ -528,19 +626,64 @@ mod token_tests { #[cfg(test)] mod parse_tests { use super::*; - use Node::{Space as S, Word as W, Newline as N, Func as F}; + use crate::func::{Function, Scope, BodyTokens}; + use Node::{Space as S, Newline as N, Func as F}; - /// Test if the source code parses into the syntax tree. - fn test(src: &str, tree: SyntaxTree) { - assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap(), tree); + #[allow(non_snake_case)] + fn W(s: &str) -> Node { Node::Word(s.to_owned()) } + + /// A testing function which just parses it's body into a syntax tree. + #[derive(Debug, PartialEq)] + struct TreeFn(SyntaxTree); + + impl Function for TreeFn { + fn parse(_: &FuncHeader, tokens: BodyTokens<'_>, scope: &Scope) + -> ParseResult where Self: Sized { + if let Some(tokens) = tokens { + Parser::with_scope(scope, tokens).parse().map(|tree| TreeFn(tree)) + } else { + Err(ParseError::new("expected body for tree fn")) + } + } + fn typeset(&self, _header: &FuncHeader) -> Option { None } } - /// Test if the source parses into the error. - fn test_err(src: &str, err: &str) { - assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap_err().message, err); + /// A testing function without a body. + #[derive(Debug, PartialEq)] + struct BodylessFn; + + impl Function for BodylessFn { + fn parse(_: &FuncHeader, tokens: BodyTokens<'_>, _: &Scope) -> ParseResult where Self: Sized { + if tokens.is_none() { + Ok(BodylessFn) + } else { + Err(ParseError::new("unexpected body for bodyless fn")) + } + } + fn typeset(&self, _header: &FuncHeader) -> Option { None } } - /// Short cut macro to create a syntax tree. + /// Shortcut macro to create a function. + macro_rules! func { + (name => $name:expr, body => None $(,)*) => { + func!(@$name, Box::new(BodylessFn)) + }; + (name => $name:expr, body => $tree:expr $(,)*) => { + func!(@$name, Box::new(TreeFn($tree))) + }; + (@$name:expr, $body:expr) => { + FuncInvocation { + header: FuncHeader { + name: Ident::new($name).unwrap(), + args: vec![], + kwargs: HashMap::new(), + }, + body: $body, + } + } + } + + /// Shortcut macro to create a syntax tree. /// Is `vec`-like and the elements are the nodes. macro_rules! tree { ($($x:expr),*) => ( @@ -549,89 +692,124 @@ mod parse_tests { ($($x:expr,)*) => (tree![$($x),*]) } + /// Test if the source code parses into the syntax tree. + fn test(src: &str, tree: SyntaxTree) { + assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap(), tree); + } + + /// Test with a scope containing function definitions. + fn test_scoped(scope: &Scope, src: &str, tree: SyntaxTree) { + assert_eq!(Parser::with_scope(scope, Tokens::new(src)).parse().unwrap(), tree); + } + + /// Test if the source parses into the error. + fn test_err(src: &str, err: &str) { + assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap_err().message, err); + } + + /// Test if the source parses into the error. + fn test_err_scoped(scope: &Scope, src: &str, err: &str) { + assert_eq!(Parser::with_scope(scope, Tokens::new(src)).parse().unwrap_err().message, err); + } + /// Parse the basic cases. #[test] fn parse_base() { - test("", tree! {}); - test("Hello World!", tree! { W("Hello"), S, W("World"), W("!")}); + test("", tree! []); + test("Hello World!", tree! [ W("Hello"), S, W("World"), W("!") ]); } /// Test whether newlines generate the correct whitespace. #[test] fn parse_newlines_whitespace() { - test("Hello\nWorld", tree! { W("Hello"), S, W("World") }); - test("Hello \n World", tree! { W("Hello"), S, W("World") }); - test("Hello\n\nWorld", tree! { W("Hello"), N, W("World") }); - test("Hello \n\nWorld", tree! { W("Hello"), S, N, W("World") }); - test("Hello\n\n World", tree! { W("Hello"), N, S, W("World") }); - test("Hello \n \n \n World", tree! { W("Hello"), S, N, S, W("World") }); - test("Hello\n \n\n World", tree! { W("Hello"), S, N, S, W("World") }); + test("Hello\nWorld", tree! [ W("Hello"), S, W("World") ]); + test("Hello \n World", tree! [ W("Hello"), S, W("World") ]); + test("Hello\n\nWorld", tree! [ W("Hello"), N, W("World") ]); + test("Hello \n\nWorld", tree! [ W("Hello"), S, N, W("World") ]); + test("Hello\n\n World", tree! [ W("Hello"), N, S, W("World") ]); + test("Hello \n \n \n World", tree! [ W("Hello"), S, N, S, W("World") ]); + test("Hello\n \n\n World", tree! [ W("Hello"), S, N, S, W("World") ]); } /// Parse things dealing with functions. #[test] fn parse_functions() { - test("[test]", tree! { F(Function { name: "test", body: None }) }); - test("This is an [modifier][example] of a function invocation.", tree! { + let mut scope = Scope::new(); + let tree_fns = ["modifier", "func", "links", "bodyempty", "nested"]; + let bodyless_fns = ["test", "end"]; + for func in &bodyless_fns { scope.add::(func); } + for func in &tree_fns { scope.add::(func); } + + test_scoped(&scope,"[test]", tree! [ F(func! { name => "test", body => None }) ]); + test_scoped(&scope, "This is an [modifier][example] of a function invocation.", tree! [ W("This"), S, W("is"), S, W("an"), S, - F(Function { name: "modifier", body: Some(tree! { W("example") }) }), S, + F(func! { name => "modifier", body => tree! [ W("example") ] }), S, W("of"), S, W("a"), S, W("function"), S, W("invocation"), W(".") - }); - test("[func][Hello][links][Here][end]", tree! { - F(Function { - name: "func", - body: Some(tree! { W("Hello") }), + ]); + test_scoped(&scope, "[func][Hello][links][Here][end]", tree! [ + F(func! { + name => "func", + body => tree! [ W("Hello") ], }), - F(Function { - name: "links", - body: Some(tree! { W("Here") }), + F(func! { + name => "links", + body => tree! [ W("Here") ], }), - F(Function { - name: "end", - body: None, + F(func! { + name => "end", + body => None, }), - }); - test("[bodyempty][]", tree! { - F(Function { - name: "bodyempty", - body: Some(tree! {}) + ]); + test_scoped(&scope, "[bodyempty][]", tree! [ + F(func! { + name => "bodyempty", + body => tree! [], }) - }); - test("[nested][[func][call]] outside", tree! { - F(Function { - name: "nested", - body: Some(tree! { F(Function { - name: "func", - body: Some(tree! { W("call") }), - }), }), + ]); + test_scoped(&scope, "[nested][[func][call]] outside", tree! [ + F(func! { + name => "nested", + body => tree! [ + F(func! { + name => "func", + body => tree! [ W("call") ], + }), + ], }), S, W("outside") - }); + ]); } /// Tests if the parser handles non-ASCII stuff correctly. #[test] fn parse_unicode() { - test("[lib_parse] ⺐.", tree! { - F(Function { - name: "lib_parse", - body: None + let mut scope = Scope::new(); + scope.add::("lib_parse"); + scope.add::("func123"); + + test_scoped(&scope, "[lib_parse] ⺐.", tree! [ + F(func! { + name => "lib_parse", + body => None, }), S, W("⺐"), W(".") - }); - test("[func123][Hello 🌍!]", tree! { - F(Function { - name: "func123", - body: Some(tree! { W("Hello"), S, W("🌍"), W("!") }), + ]); + test_scoped(&scope, "[func123][Hello 🌍!]", tree! [ + F(func! { + name => "func123", + body => tree! [ W("Hello"), S, W("🌍"), W("!") ], }) - }); + ]); } /// Tests whether errors get reported correctly. #[test] fn parse_errors() { + let mut scope = Scope::new(); + scope.add::("hello"); + test_err("No functions here]", "unexpected closing bracket"); - test_err("[hello][world", "expected closing bracket"); + test_err_scoped(&scope, "[hello][world", "expected closing bracket"); test_err("[hello world", "expected closing bracket"); test_err("[ no-name][Why?]", "expected identifier"); } diff --git a/src/syntax.rs b/src/syntax.rs index fdb71d145..dd83b4a39 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -1,8 +1,9 @@ //! Tokenized and syntax tree representations of source code. -use std::fmt::Debug; use std::collections::HashMap; +use std::fmt::{self, Display, Formatter}; use std::ops::Deref; +use crate::func::Function; use crate::utility::StrExt; @@ -38,22 +39,22 @@ pub enum Token<'s> { /// A tree representation of the source. #[derive(Debug, PartialEq)] -pub struct SyntaxTree<'s> { +pub struct SyntaxTree { /// The children. - pub nodes: Vec>, + pub nodes: Vec, } -impl<'s> SyntaxTree<'s> { +impl SyntaxTree { /// Create an empty syntax tree. #[inline] - pub fn new() -> SyntaxTree<'s> { + pub fn new() -> SyntaxTree { SyntaxTree { nodes: vec![] } } } /// A node in the abstract syntax tree. #[derive(Debug, PartialEq)] -pub enum Node<'s> { +pub enum Node { /// Whitespace between other nodes. Space, /// A line feed. @@ -65,18 +66,24 @@ pub enum Node<'s> { /// Indicates that math mode was enabled/disabled. ToggleMath, /// A literal word. - Word(&'s str), + Word(String), /// A function invocation. Func(FuncInvocation), } /// A complete function invocation consisting of header and body. -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct FuncInvocation { pub header: FuncHeader, pub body: Box, } +impl PartialEq for FuncInvocation { + fn eq(&self, other: &FuncInvocation) -> bool { + (self.header == other.header) && (&self.body == &other.body) + } +} + /// Contains header information of a function invocation. #[derive(Debug, Clone, PartialEq)] pub struct FuncHeader { @@ -85,53 +92,11 @@ pub struct FuncHeader { pub kwargs: HashMap } -use std::any::Any; - -/// Types that act as functions. -pub trait Function: Debug + FunctionHelper { - /// Parse the function. - fn parse() -> Self where Self: Sized; - - /// Execute the function and optionally yield a return value. - fn typeset(&self, header: &FuncHeader) -> Option; -} - -trait FunctionHelper { - fn help_as_any(&self) -> &dyn Any; - fn help_eq(&self, other: &dyn Function) -> bool; -} - -impl FunctionHelper for T where T: Clone + PartialEq + 'static { - fn help_as_any(&self) -> &dyn Any { - self - } - - fn help_eq(&self, other: &dyn Function) -> bool { - if let Some(other) = other.help_as_any().downcast_ref::() { - self == other - } else { - false - } - } -} - -impl PartialEq for &dyn Function { - fn eq(&self, other: &dyn Function) -> bool { - self.help_eq(other) - } -} - -impl PartialEq> for Box { - fn eq(&self, other: &Box) -> bool { - &*self == &*other - } -} - /// A potentially unevaluated expression. #[derive(Debug, Clone, PartialEq)] pub enum Expression {} -/// A valid identifier. +/// An owned valid identifier. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct Ident(String); @@ -154,6 +119,12 @@ impl Ident { } } +impl Display for Ident { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + impl Deref for Ident { type Target = str;