diff --git a/src/parsing.rs b/src/parsing.rs index 5333cbd3c..9d921fe18 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -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> { tokens: Peekable, state: ParserState, - stack: Vec>, + stack: Vec, tree: SyntaxTree<'s>, } @@ -296,9 +297,13 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { }, 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> { 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. diff --git a/src/syntax.rs b/src/syntax.rs index 46b848323..fdb71d145 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -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>, @@ -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>, +/// A complete function invocation consisting of header and body. +#[derive(Debug, PartialEq)] +pub struct FuncInvocation { + pub header: FuncHeader, + pub body: Box, +} + +/// Contains header information of a function invocation. +#[derive(Debug, Clone, PartialEq)] +pub struct FuncHeader { + pub name: Ident, + pub args: Vec, + 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. +#[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>(ident: S) -> Option { + 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 + } }