Simple dynamic, scoped function parsing 📥

This commit is contained in:
Laurenz 2019-04-29 00:12:36 +02:00
parent a51e7a0758
commit f279c52b50
5 changed files with 404 additions and 161 deletions

View File

@ -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<TextCommand>,
@ -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<FontLoaderState<'t>>,
state: RefCell<FontLoaderState<'a>>,
}
/// 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<usize>, Font)>,
/// Allows to retrieve cached results for queries.
query_cache: HashMap<FontQuery<'t>, usize>,
query_cache: HashMap<FontQuery<'a>, 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<usize>,
}
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<Font>)> {
pub fn get(&self, query: FontQuery<'a>) -> Option<(usize, Ref<Font>)> {
// Check if we had the exact same query before.
let state = self.state.borrow();
if let Some(&index) = state.query_cache.get(&query) {

93
src/func.rs Normal file
View File

@ -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)
}
}

View File

@ -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<SyntaxTree<'s>, ParseError> {
pub fn parse(&self, src: &str) -> Result<SyntaxTree, ParseError> {
Parser::new(Tokens::new(src)).parse()
}

View File

@ -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<Item=Token<'s>> {
tokens: Peekable<T>,
scope: ParserScope<'s>,
state: ParserState,
stack: Vec<FuncInvocation>,
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<Item=Token<'s>> {
/// 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<Item=Token<'s>> {
}
/// Parse into an abstract syntax tree.
pub(crate) fn parse(mut self) -> ParseResult<SyntaxTree<'s>> {
pub(crate) fn parse(mut self) -> ParseResult<SyntaxTree> {
use ParserState as PS;
while let Some(token) = self.tokens.peek() {
@ -275,7 +305,7 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
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<Item=Token<'s>> {
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<Item=Token<'s>> {
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<dyn Iterator<Item=Token<'_>>>
});
// 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<Item=Token<'s>> {
}
/// 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<Item=Token<'s>> {
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<Item=Token<'s>> {
self.advance();
}
}
}
/// Gives a parsing error with a message.
fn err<R, S: Into<String>>(&self, message: S) -> ParseResult<R> {
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<Item=Token<'s>> {
tokens: T,
parens: u32,
unexpected_end: bool,
}
impl<'s, T> FuncTokens<'s, T> where T: Iterator<Item=Token<'s>> {
/// 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<Item=Token<'s>> {
type Item = Token<'s>;
fn next(&mut self) -> Option<Token<'s>> {
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<S: Into<String>>(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<Self> 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<Expression> { 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<Self> where Self: Sized {
if tokens.is_none() {
Ok(BodylessFn)
} else {
Err(ParseError::new("unexpected body for bodyless fn"))
}
}
fn typeset(&self, _header: &FuncHeader) -> Option<Expression> { 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::<BodylessFn>(func); }
for func in &tree_fns { scope.add::<TreeFn>(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::<BodylessFn>("lib_parse");
scope.add::<TreeFn>("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::<TreeFn>("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");
}

View File

@ -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<Node<'s>>,
pub nodes: Vec<Node>,
}
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<dyn Function>,
}
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<Ident, Expression>
}
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<Expression>;
}
trait FunctionHelper {
fn help_as_any(&self) -> &dyn Any;
fn help_eq(&self, other: &dyn Function) -> bool;
}
impl<T> 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>() {
self == other
} else {
false
}
}
}
impl PartialEq<dyn Function> for &dyn Function {
fn eq(&self, other: &dyn Function) -> bool {
self.help_eq(other)
}
}
impl PartialEq<Box<dyn Function>> for Box<dyn Function> {
fn eq(&self, other: &Box<dyn Function>) -> 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;