From f8819279f81c805db54635c2ecc3c634875ff2e0 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Thu, 3 Sep 2020 18:32:22 -0700 Subject: [PATCH] Fix parser for index and call exprs, remove old test that didn't work, add visitor pattern Signed-off-by: Alek Ratzloff --- src/syn/ast.rs | 306 +++++++++++++++++++++++++++++++++++++++------- src/syn/lexer.l | 1 + src/syn/macros.rs | 10 ++ src/syn/mod.rs | 2 + src/syn/parser.y | 47 +++---- src/syn/visit.rs | 24 ++++ 6 files changed, 325 insertions(+), 65 deletions(-) create mode 100644 src/syn/macros.rs create mode 100644 src/syn/visit.rs diff --git a/src/syn/ast.rs b/src/syn/ast.rs index 5646294..adbce92 100644 --- a/src/syn/ast.rs +++ b/src/syn/ast.rs @@ -1,17 +1,125 @@ +use crate::syn::visit::*; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum Stmt { Expr(Expr), - Assign, + Assign(AssignStmt), } +// +// impl Accept for Stmt +// +impl Accept for Stmt { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_stmt(self) + } +} + +// +// impl DefaultAccept for Stmt +// +impl DefaultAccept for Stmt { + fn default_accept(&self, visitor: &mut V) -> V::Out { + match self { + Stmt::Expr(e) => e.accept(visitor), + Stmt::Assign(a) => a.accept(visitor), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AssignStmt { + pub lhs: LhsExpr, + pub rhs: Expr, +} + +// +// impl Accept for AssignStmt +// +impl Accept for AssignStmt { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_assign_stmt(self) + } +} + +// +// impl DefaultAccept for AssignStmt +// +impl> DefaultAccept for AssignStmt { + fn default_accept(&self, visitor: &mut V) -> V::Out { + self.lhs.accept(visitor); + self.rhs.accept(visitor); + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum LhsExpr { + SetAttr(AccessExpr), + Local(String), +} + +// +// impl Accept for LhsExpr +// +impl Accept for LhsExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_lhs_expr(self) + } +} + +// +// impl DefaultAccept for LhsExpr +// +impl> DefaultAccept for LhsExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + match self { + LhsExpr::SetAttr(a) => a.accept(visitor), + LhsExpr::Local(_) => {}, + } + } +} + +// +// struct Expr +// #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expr { Atom(Atom), Bin(Box), Un(Box), Access(Box), + Call(Box), + Index(Box), } +// +// impl Accept for Expr +// +impl Accept for Expr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(self) + } +} + +// +// impl DefaultAccept for Expr +// +impl DefaultAccept for Expr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + match self { + Expr::Atom(a) => a.accept(visitor), + Expr::Bin(b) => b.accept(visitor), + Expr::Un(u) => u.accept(visitor), + Expr::Access(a) => a.accept(visitor), + Expr::Call(c) => c.accept(visitor), + Expr::Index(i) => i.accept(visitor), + } + } +} + +// +// impl From for Expr +// impl From for Expr { fn from(other: Atom) -> Self { Expr::Atom(other) @@ -36,6 +144,18 @@ impl From for Expr { } } +impl From for Expr { + fn from(other: CallExpr) -> Self { + Expr::Call(other.into()) + } +} + +impl From for Expr { + fn from(other: IndexExpr) -> Self { + Expr::Index(other.into()) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum BinOp { Plus, @@ -52,6 +172,9 @@ pub enum BinOp { Or, } +// +// struct BinExpr +// #[derive(Debug, Clone, PartialEq, Eq)] pub struct BinExpr { pub lhs: Expr, @@ -65,12 +188,34 @@ impl BinExpr { } } +// +// impl Accept for BinExpr +// +impl Accept for BinExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_bin_expr(self) + } +} + +// +// impl DefaultAccept for BinExpr +// +impl> DefaultAccept for BinExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(&self.lhs); + visitor.visit_expr(&self.rhs); + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum UnOp { Plus, Minus, } +// +// struct UnExpr +// #[derive(Debug, Clone, PartialEq, Eq)] pub struct UnExpr { pub op: UnOp, @@ -83,28 +228,124 @@ impl UnExpr { } } +// +// impl Accept for UnExpr +// +impl Accept for UnExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_un_expr(self) + } +} + +// +// impl DefaultAccept for UnExpr +// +impl DefaultAccept for UnExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(&self.expr) + } +} + +// +// struct CallExpr +// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CallExpr { + pub expr: Expr, + pub args: Vec, +} + +impl CallExpr { + pub fn new_expr(expr: Expr, args: Vec) -> Expr { + Self { expr, args }.into() + } +} + +// +// impl Accept for CallExpr +// +impl Accept for CallExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_call_expr(self) + } +} + +// +// impl DefaultAccept for CallExpr +// +impl> DefaultAccept for CallExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(&self.expr); + self.args.iter() + .for_each(|arg| visitor.visit_expr(arg)); + } +} + +// +// struct IndexExpr +// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IndexExpr { + pub expr: Expr, + pub index: Expr, +} + +impl IndexExpr { + pub fn new_expr(expr: Expr, index: Expr) -> Expr { + Self { expr, index, }.into() + } +} + +// +// impl Accept for IndexExpr +// +impl Accept for IndexExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_index_expr(self) + } +} + +// +// impl DefaultAccept for IndexExpr +// +impl> DefaultAccept for IndexExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(&self.expr); + visitor.visit_expr(&self.index); + } +} + +// +// struct AccessExpr +// #[derive(Debug, Clone, PartialEq, Eq)] pub struct AccessExpr { pub expr: Expr, - pub access: Vec, + pub access: String, } impl AccessExpr { - pub fn new_expr(expr: Expr, access: Vec) -> Expr { + pub fn new_expr(expr: Expr, access: String) -> Expr { Self { expr, access, }.into() } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Access { - pub access: String, - pub trailing: Vec, +// +// impl Accept for AccessExpr +// +impl Accept for AccessExpr { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_access_expr(self) + } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ExprTrail { - Call(Vec), - Index(Expr), +// +// impl DefaultAccept for AccessExpr +// +impl DefaultAccept for AccessExpr { + fn default_accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_expr(&self.expr) + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -115,6 +356,16 @@ pub enum Atom { String(String), } +impl Accept for Atom { + fn accept(&self, visitor: &mut V) -> V::Out { + visitor.visit_atom(self) + } +} + +impl> DefaultAccept for Atom { + fn default_accept(&self, _visitor: &mut V) -> V::Out { } +} + pub fn parse(text: &str) -> Result>, Vec> { use crate::syn::{lexer, parser}; let lexerdef = lexer::lexerdef(); @@ -129,36 +380,3 @@ pub fn parse(text: &str) -> Result>, Vec> { Ok(res.transpose().unwrap()) } - -#[cfg(test)] -mod test { - use super::*; - - fn parse_expr(text: &str) -> Expr { - let mut body = parse(text) - .unwrap() - .unwrap(); - assert!(body.len() == 1); - let stmt = body.pop().unwrap(); - - if let Stmt::Expr(expr) = stmt { - expr - } else { - panic!("{:?} parses to {:?} which is not a Stmt::Expr", text, stmt); - } - } - - #[test] - fn access() { - assert_eq!( - parse_expr("a.b.c()"), - AccessExpr::new_expr( - Atom::Ident("a".to_string()).into(), - vec![ - Access { access: "b".to_string(), trailing: vec![], }, - Access { access: "c".to_string(), trailing: vec![ExprTrail::Call(vec![])], }, - ] - ) - ); - } -} diff --git a/src/syn/lexer.l b/src/syn/lexer.l index 3ffc65e..e87d509 100644 --- a/src/syn/lexer.l +++ b/src/syn/lexer.l @@ -5,6 +5,7 @@ [0-9]+ "NUM" "([^"]|\\[rnt"'\\])+"|'([^"]|\\[rnt"'\\])+' "STRING" += "=" \|\| "||" && "&&" < "<" diff --git a/src/syn/macros.rs b/src/syn/macros.rs new file mode 100644 index 0000000..cf5a7bc --- /dev/null +++ b/src/syn/macros.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! accept_default { + ($visitor:ty, $acceptor:ty) => { + impl $crate::syn::visit::Accept for $acceptor<$visitor> { + fn accept(&self, visitor: &mut $visitor) -> $visitor::Out { + $crate::syn::visit::DefaultAccept::default_accept(self, visitor) + } + } + }; +} diff --git a/src/syn/mod.rs b/src/syn/mod.rs index ca9a41d..8116462 100644 --- a/src/syn/mod.rs +++ b/src/syn/mod.rs @@ -1,4 +1,6 @@ +#![macro_use] mod macros; pub mod ast; +pub mod visit; pub mod lexer { lrlex_mod!("syn/lexer.l"); diff --git a/src/syn/parser.y b/src/syn/parser.y index 853762f..692c48b 100644 --- a/src/syn/parser.y +++ b/src/syn/parser.y @@ -19,6 +19,7 @@ Body -> Result>: Stmt -> Result: Expr { Ok(Stmt::Expr($1?)) } + //| Assign { todo!() } ; Expr -> Result: BinExpr { $1 }; @@ -42,31 +43,24 @@ BinExpr -> Result: UnExpr -> Result: '+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) } | '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) } + | CallIndexExpr { $1 } + ; + +// TODO - add CallExpr and IndexExpr, allow them to nest +// - UnExpr will point to CallIndexExpr +// - Call/Index exprs are based on an AccessExpr +// - AccessExpr will retain ExprTrailing in between its items, but should end in a .ident + +CallIndexExpr -> Result: + IndexExpr { $1 } + | CallExpr { $1 } | AccessExpr { $1 } ; -AccessExpr -> Result: - AtomExpr AccessExprTail { - Ok(AccessExpr::new_expr($1?, $2?)) +CallExpr -> Result: + CallIndexExpr '(' FunArgs ')' { + Ok(CallExpr::new_expr($1?, $3?)) } - | AtomExpr { - Ok(AccessExpr::new_expr($1?, Default::default())) - } - ; - -AccessExprTail -> Result>: - AccessExprTail '.' Ident ExprTrailing { - flatten($1, Ok(Access { access: $3?, trailing: $4? })) - } - | '.' Ident ExprTrailing { - Ok(vec![Access { access: $2?, trailing: $3? }]) - } - ; - -ExprTrailing -> Result>: - ExprTrailing '(' FunArgs ')' { flatten($1, Ok(ExprTrail::Call($3?))) } - | ExprTrailing '[' Expr ']' { flatten($1, Ok(ExprTrail::Index($3?))) } - | { Ok(Vec::new()) } ; FunArgs -> Result>: @@ -79,6 +73,17 @@ FunArgsTail -> Result>: | Expr { Ok(vec![$1?]) } ; +IndexExpr -> Result: + CallIndexExpr '[' Expr ']' { + Ok(IndexExpr::new_expr($1?, $3?)) + } + ; + +AccessExpr -> Result: + AtomExpr { $1 } + | CallIndexExpr '.' Ident { Ok(AccessExpr::new_expr($1?, $3?)) } + ; + AtomExpr -> Result: Atom { $1.map(Expr::Atom) } | '(' Expr ')' { $2 } diff --git a/src/syn/visit.rs b/src/syn/visit.rs new file mode 100644 index 0000000..e32eafe --- /dev/null +++ b/src/syn/visit.rs @@ -0,0 +1,24 @@ +use crate::syn::ast::*; + +pub trait Accept { + fn accept(&self, visitor: &mut V) -> V::Out; +} + +pub trait DefaultAccept { + fn default_accept(&self, visitor: &mut V) -> V::Out; +} + +pub trait Visit { + type Out; + + fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out; + fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out; + fn visit_lhs_expr(&mut self, lhs_expr: &LhsExpr) -> Self::Out; + fn visit_expr(&mut self, expr: &Expr) -> Self::Out; + fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out; + fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out; + fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out; + fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out; + fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out; + fn visit_atom(&mut self, atom: &Atom) -> Self::Out; +}