From 14c9ff571d41e01fe46fb842aa5ea1b93497c4ab Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 29 Apr 2019 11:03:17 +0200 Subject: [PATCH] =?UTF-8?q?Remove=20stack=20from=20parser=20=E2=99=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/func.rs | 87 +++++++++++++++++------------------ src/parsing.rs | 122 +++++++++++++++++++++++-------------------------- src/syntax.rs | 9 ++-- 3 files changed, 105 insertions(+), 113 deletions(-) diff --git a/src/func.rs b/src/func.rs index cbc2f58b7..d85d7a18d 100644 --- a/src/func.rs +++ b/src/func.rs @@ -3,68 +3,37 @@ use std::any::Any; use std::collections::HashMap; use std::fmt::Debug; + use crate::syntax::{Token, FuncHeader, Expression}; -use crate::parsing::ParseError; +use crate::parsing::ParseResult; /// An optional iterator over the tokens of a function body. pub type BodyTokens<'a> = Option> + 'a>>; +/// Parser functions. +pub type ParseFunc = dyn Fn(&FuncHeader, BodyTokens<'_>, &Scope) + -> ParseResult>; /// 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 { +/// The trait `FunctionBounds` is automatically implemented for types which can be +/// used as functions, that is they fulfill the bounds `Debug + PartialEq + 'static`. +pub trait Function: FunctionBounds { /// Parse the function. fn parse(header: &FuncHeader, tokens: BodyTokens<'_>, scope: &Scope) - -> Result where Self: Sized; + -> ParseResult 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>>>, + parsers: HashMap>, } impl Scope { @@ -85,9 +54,39 @@ impl Scope { } /// 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>> { + pub fn get_parser(&self, name: &str) -> Option<&ParseFunc> { self.parsers.get(name).map(|x| &**x) } } + +/// A helper trait that describes requirements for types that can 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 FunctionBounds: 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 FunctionBounds 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) + } +} diff --git a/src/parsing.rs b/src/parsing.rs index 7c4d60414..ebcdeef42 100644 --- a/src/parsing.rs +++ b/src/parsing.rs @@ -5,11 +5,13 @@ 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}; +use unicode_segmentation::{UnicodeSegmentation, UWordBounds}; + /// An iterator over the tokens of source code. #[derive(Clone)] @@ -211,7 +213,6 @@ pub struct Parser<'s, T> where T: Iterator> { tokens: Peekable, scope: ParserScope<'s>, state: ParserState, - stack: Vec, tree: SyntaxTree, } @@ -228,23 +229,6 @@ 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 fn new(tokens: T) -> Parser<'s, T> { @@ -262,7 +246,6 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { tokens: tokens.peekable(), scope, state: ParserState::Body, - stack: vec![], tree: SyntaxTree::new(), } } @@ -310,11 +293,7 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { // Functions Token::LeftBracket => self.switch_consumed(PS::Function), Token::RightBracket => { - self.advance(); - match self.stack.pop() { - Some(func) => self.append(Node::Func(func)), - None => return Err(ParseError::new("unexpected closing bracket")), - } + return Err(ParseError::new("unexpected closing bracket")); }, // Modifiers @@ -366,20 +345,18 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { let body = if let Some(parser) = self.scope.get_parser(&name) { parser(&header, borrow_tokens, &self.scope)? } else { - let message = format!("unknown function: '{}'", &name); - return Err(ParseError::new(message)); + return Err(ParseError::new(format!("unknown function: '{}'", &name))); }; // 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 { + self.append(Node::Func(FuncCall { header, body, })); @@ -390,10 +367,6 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } } - // if !self.stack.is_empty() { - // return Err(ParseError::new("expected closing bracket")); - // } - Ok(self.tree) } @@ -402,13 +375,9 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { self.tokens.next(); } - /// Append a node to the top-of-stack function or the main tree itself. + /// Append a node to the tree. 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. @@ -422,7 +391,10 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } /// Advance and append a space if there is not one already. - fn append_space_consumed(&mut self) { self.advance(); self.append_space(); } + fn append_space_consumed(&mut self) { + self.advance(); + self.append_space(); + } /// Switch the state. fn switch(&mut self, state: ParserState) { @@ -430,15 +402,14 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } /// Advance and switch the state. - fn switch_consumed(&mut self, state: ParserState) { self.advance(); self.state = state; } + 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. + /// The last appended node of the tree. 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. @@ -452,6 +423,23 @@ impl<'s, T> Parser<'s, T> where T: Iterator> { } } +/// 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, + } + } +} + /// A token iterator that that stops after the first unbalanced right paren. pub struct FuncTokens<'s, T> where T: Iterator> { tokens: T, @@ -505,9 +493,11 @@ impl ParseError { } } +/// The result type for parsing. +pub type ParseResult = Result; + error_type! { err: ParseError, - res: ParseResult, show: f => f.write_str(&err.message), } @@ -623,6 +613,7 @@ mod token_tests { } } + #[cfg(test)] mod parse_tests { use super::*; @@ -653,7 +644,8 @@ mod parse_tests { struct BodylessFn; impl Function for BodylessFn { - fn parse(_: &FuncHeader, tokens: BodyTokens<'_>, _: &Scope) -> ParseResult where Self: Sized { + fn parse(_: &FuncHeader, tokens: BodyTokens<'_>, _: &Scope) + -> ParseResult where Self: Sized { if tokens.is_none() { Ok(BodylessFn) } else { @@ -672,7 +664,7 @@ mod parse_tests { func!(@$name, Box::new(TreeFn($tree))) }; (@$name:expr, $body:expr) => { - FuncInvocation { + FuncCall { header: FuncHeader { name: Ident::new($name).unwrap(), args: vec![], @@ -707,7 +699,7 @@ mod parse_tests { assert_eq!(Parser::new(Tokens::new(src)).parse().unwrap_err().message, err); } - /// Test if the source parses into the error. + /// Test with a scope 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); } @@ -735,10 +727,10 @@ mod parse_tests { #[test] fn parse_functions() { 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); } + scope.add::("test"); + scope.add::("end"); + scope.add::("modifier"); + 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! [ @@ -746,13 +738,13 @@ mod parse_tests { F(func! { name => "modifier", body => tree! [ W("example") ] }), S, W("of"), S, W("a"), S, W("function"), S, W("invocation"), W(".") ]); - test_scoped(&scope, "[func][Hello][links][Here][end]", tree! [ + test_scoped(&scope, "[func][Hello][modifier][Here][end]", tree! [ F(func! { name => "func", body => tree! [ W("Hello") ], }), F(func! { - name => "links", + name => "modifier", body => tree! [ W("Here") ], }), F(func! { @@ -760,15 +752,15 @@ mod parse_tests { body => None, }), ]); - test_scoped(&scope, "[bodyempty][]", tree! [ + test_scoped(&scope, "[func][]", tree! [ F(func! { - name => "bodyempty", + name => "func", body => tree! [], }) ]); - test_scoped(&scope, "[nested][[func][call]] outside", tree! [ + test_scoped(&scope, "[modifier][[func][call]] outside", tree! [ F(func! { - name => "nested", + name => "modifier", body => tree! [ F(func! { name => "func", @@ -784,19 +776,19 @@ mod parse_tests { #[test] fn parse_unicode() { let mut scope = Scope::new(); - scope.add::("lib_parse"); - scope.add::("func123"); + scope.add::("func"); + scope.add::("bold"); - test_scoped(&scope, "[lib_parse] ⺐.", tree! [ + test_scoped(&scope, "[func] ⺐.", tree! [ F(func! { - name => "lib_parse", + name => "func", body => None, }), S, W("⺐"), W(".") ]); - test_scoped(&scope, "[func123][Hello 🌍!]", tree! [ + test_scoped(&scope, "[bold][Hello 🌍!]", tree! [ F(func! { - name => "func123", + name => "bold", body => tree! [ W("Hello"), S, W("🌍"), W("!") ], }) ]); diff --git a/src/syntax.rs b/src/syntax.rs index dd83b4a39..8d248ab7c 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::fmt::{self, Display, Formatter}; use std::ops::Deref; + use crate::func::Function; use crate::utility::StrExt; @@ -68,18 +69,18 @@ pub enum Node { /// A literal word. Word(String), /// A function invocation. - Func(FuncInvocation), + Func(FuncCall), } /// A complete function invocation consisting of header and body. #[derive(Debug)] -pub struct FuncInvocation { +pub struct FuncCall { pub header: FuncHeader, pub body: Box, } -impl PartialEq for FuncInvocation { - fn eq(&self, other: &FuncInvocation) -> bool { +impl PartialEq for FuncCall { + fn eq(&self, other: &FuncCall) -> bool { (self.header == other.header) && (&self.body == &other.body) } }