Express functions as trait objects 🚄

This commit is contained in:
Laurenz 2019-04-11 17:36:21 +02:00
parent 98c3788cf1
commit a51e7a0758
2 changed files with 116 additions and 17 deletions

View File

@ -1,5 +1,6 @@
//! Tokenization and parsing of source code into syntax trees.
use std::collections::HashMap;
use std::fmt;
use std::iter::Peekable;
use std::mem::swap;
@ -208,7 +209,7 @@ impl<'s> Tokens<'s> {
pub struct Parser<'s, T> where T: Iterator<Item=Token<'s>> {
tokens: Peekable<T>,
state: ParserState,
stack: Vec<Function<'s>>,
stack: Vec<FuncInvocation>,
tree: SyntaxTree<'s>,
}
@ -296,9 +297,13 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
},
PS::Function => {
let name = match token {
Token::Word(word) if word.is_identifier() => word,
_ => return self.err("expected identifier"),
let name = if let Token::Word(word) = token {
match Ident::new(word) {
Some(ident) => ident,
None => return self.err("invalid identifier"),
}
} else {
return self.err("expected identifier");
};
self.advance();
@ -306,9 +311,15 @@ impl<'s, T> Parser<'s, T> where T: Iterator<Item=Token<'s>> {
return self.err("expected closing bracket");
}
let mut func = Function {
let header = FuncHeader {
name,
body: None,
args: vec![],
kwargs: HashMap::new(),
};
let func = FuncInvocation {
header,
body: unimplemented!(),
};
// This function has a body.

View File

@ -1,5 +1,10 @@
//! Tokenized and syntax tree representations of source code.
use std::fmt::Debug;
use std::collections::HashMap;
use std::ops::Deref;
use crate::utility::StrExt;
/// A logical unit of the incoming text stream.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@ -32,7 +37,7 @@ pub enum Token<'s> {
}
/// A tree representation of the source.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct SyntaxTree<'s> {
/// The children.
pub nodes: Vec<Node<'s>>,
@ -47,7 +52,7 @@ impl<'s> SyntaxTree<'s> {
}
/// A node in the abstract syntax tree.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum Node<'s> {
/// Whitespace between other nodes.
Space,
@ -62,15 +67,98 @@ pub enum Node<'s> {
/// A literal word.
Word(&'s str),
/// A function invocation.
Func(Function<'s>),
Func(FuncInvocation),
}
/// A node representing a function invocation.
#[derive(Debug, Clone, PartialEq)]
pub struct Function<'s> {
/// The name of the function.
pub name: &'s str,
/// Some syntax tree if the function had a body (second set of brackets),
/// otherwise nothing.
pub body: Option<SyntaxTree<'s>>,
/// A complete function invocation consisting of header and body.
#[derive(Debug, PartialEq)]
pub struct FuncInvocation {
pub header: FuncHeader,
pub body: Box<dyn Function>,
}
/// Contains header information of a function invocation.
#[derive(Debug, Clone, PartialEq)]
pub struct FuncHeader {
pub name: Ident,
pub args: Vec<Expression>,
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.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Ident(String);
impl Ident {
/// Create a new identifier if the string is a valid one.
#[inline]
pub fn new<S: Into<String>>(ident: S) -> Option<Ident> {
let ident = ident.into();
if ident.is_identifier() {
Some(Ident(ident))
} else {
None
}
}
/// Consume self and return the underlying string.
#[inline]
pub fn into_inner(self) -> String {
self.0
}
}
impl Deref for Ident {
type Target = str;
#[inline]
fn deref(&self) -> &str {
&*self.0
}
}