summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-04-29 11:03:17 +0200
committerLaurenz <laurmaedje@gmail.com>2019-04-29 11:03:17 +0200
commit14c9ff571d41e01fe46fb842aa5ea1b93497c4ab (patch)
tree0a09cec0552dcd990d12df8ab361271112d4bb83
parentf279c52b503e9dacb558d8a46a75b42bc5222ee4 (diff)
Remove stack from parser β™»
-rw-r--r--src/func.rs83
-rw-r--r--src/parsing.rs122
-rw-r--r--src/syntax.rs9
3 files changed, 103 insertions, 111 deletions
diff --git a/src/func.rs b/src/func.rs
index cbc2f58b..d85d7a18 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -3,37 +3,67 @@
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<Box<dyn Iterator<Item=Token<'a>> + 'a>>;
+/// Parser functions.
+pub type ParseFunc = dyn Fn(&FuncHeader, BodyTokens<'_>, &Scope)
+ -> ParseResult<Box<dyn Function>>;
/// 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<Self, ParseError> where Self: Sized;
+ -> ParseResult<Self> 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`].
+/// A map from identifiers to functions.
+pub struct Scope {
+ parsers: HashMap<String, Box<ParseFunc>>,
+}
+
+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<&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 FunctionRequirements: Debug {
+/// `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;
@@ -41,7 +71,7 @@ pub trait FunctionRequirements: Debug {
fn help_eq(&self, other: &dyn Function) -> bool;
}
-impl<T> FunctionRequirements for T where T: Debug + PartialEq + 'static {
+impl<T> FunctionBounds for T where T: Debug + PartialEq + 'static {
fn help_cast_as_any(&self) -> &dyn Any {
self
}
@@ -60,34 +90,3 @@ impl PartialEq for dyn Function {
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)
- }
-}
diff --git a/src/parsing.rs b/src/parsing.rs
index 7c4d6041..ebcdeef4 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<Item=Token<'s>> {
tokens: Peekable<T>,
scope: ParserScope<'s>,
state: ParserState,
- stack: Vec<FuncInvocation>,
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<Item=Token<'s>> {
/// 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<Item=Token<'s>> {
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<Item=Token<'s>> {
// 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<Item=Token<'s>> {
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<Item=Token<'s>> {
}
}
- // 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<Item=Token<'s>> {
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<Item=Token<'s>> {
}
/// 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<Item=Token<'s>> {
}
/// 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<Item=Token<'s>> {
}
}
+/// 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<Item=Token<'s>> {
tokens: T,
@@ -505,9 +493,11 @@ impl ParseError {
}
}
+/// The result type for parsing.
+pub type ParseResult<T> = Result<T, ParseError>;
+
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<Self> where Self: Sized {
+ fn parse(_: &FuncHeader, tokens: BodyTokens<'_>, _: &Scope)
+ -> ParseResult<Self> 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::<BodylessFn>(func); }
- for func in &tree_fns { scope.add::<TreeFn>(func); }
+ scope.add::<BodylessFn>("test");
+ scope.add::<BodylessFn>("end");
+ scope.add::<TreeFn>("modifier");
+ 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! [
@@ -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::<BodylessFn>("lib_parse");
- scope.add::<TreeFn>("func123");
+ scope.add::<BodylessFn>("func");
+ scope.add::<TreeFn>("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 dd83b4a3..8d248ab7 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<dyn Function>,
}
-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)
}
}