Fix parser for index and call exprs, remove old test that didn't work, add visitor pattern
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
306
src/syn/ast.rs
306
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<V: Visit> Accept<V> for Stmt {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_stmt(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for Stmt
|
||||
//
|
||||
impl<V: Visit> DefaultAccept<V> 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<V: Visit> Accept<V> for AssignStmt {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_assign_stmt(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for AssignStmt
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> 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<V: Visit> Accept<V> for LhsExpr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_lhs_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for LhsExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> 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<BinExpr>),
|
||||
Un(Box<UnExpr>),
|
||||
Access(Box<AccessExpr>),
|
||||
Call(Box<CallExpr>),
|
||||
Index(Box<IndexExpr>),
|
||||
}
|
||||
|
||||
//
|
||||
// impl Accept for Expr
|
||||
//
|
||||
impl<V: Visit> Accept<V> for Expr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for Expr
|
||||
//
|
||||
impl<V: Visit> DefaultAccept<V> 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<T> for Expr
|
||||
//
|
||||
impl From<Atom> for Expr {
|
||||
fn from(other: Atom) -> Self {
|
||||
Expr::Atom(other)
|
||||
@@ -36,6 +144,18 @@ impl From<AccessExpr> for Expr {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CallExpr> for Expr {
|
||||
fn from(other: CallExpr) -> Self {
|
||||
Expr::Call(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IndexExpr> 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<V: Visit> Accept<V> for BinExpr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_bin_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for BinExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> 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<V: Visit> Accept<V> for UnExpr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_un_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for UnExpr
|
||||
//
|
||||
impl<V: Visit> DefaultAccept<V> 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<Expr>,
|
||||
}
|
||||
|
||||
impl CallExpr {
|
||||
pub fn new_expr(expr: Expr, args: Vec<Expr>) -> Expr {
|
||||
Self { expr, args }.into()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl Accept for CallExpr
|
||||
//
|
||||
impl<V: Visit> Accept<V> for CallExpr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_call_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for CallExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> 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<V: Visit> Accept<V> for IndexExpr {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_index_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// impl DefaultAccept for IndexExpr
|
||||
//
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> 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<Access>,
|
||||
pub access: String,
|
||||
}
|
||||
|
||||
impl AccessExpr {
|
||||
pub fn new_expr(expr: Expr, access: Vec<Access>) -> 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<ExprTrail>,
|
||||
//
|
||||
// impl Accept for AccessExpr
|
||||
//
|
||||
impl<V: Visit> Accept<V> 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<Expr>),
|
||||
Index(Expr),
|
||||
//
|
||||
// impl DefaultAccept for AccessExpr
|
||||
//
|
||||
impl<V: Visit> DefaultAccept<V> 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<V: Visit> Accept<V> for Atom {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out {
|
||||
visitor.visit_atom(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Visit<Out=()>> DefaultAccept<V> for Atom {
|
||||
fn default_accept(&self, _visitor: &mut V) -> V::Out { }
|
||||
}
|
||||
|
||||
pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<String>> {
|
||||
use crate::syn::{lexer, parser};
|
||||
let lexerdef = lexer::lexerdef();
|
||||
@@ -129,36 +380,3 @@ pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<String>> {
|
||||
|
||||
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![])], },
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[0-9]+ "NUM"
|
||||
"([^"]|\\[rnt"'\\])+"|'([^"]|\\[rnt"'\\])+' "STRING"
|
||||
|
||||
= "="
|
||||
\|\| "||"
|
||||
&& "&&"
|
||||
< "<"
|
||||
|
||||
10
src/syn/macros.rs
Normal file
10
src/syn/macros.rs
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#![macro_use] mod macros;
|
||||
pub mod ast;
|
||||
pub mod visit;
|
||||
|
||||
pub mod lexer {
|
||||
lrlex_mod!("syn/lexer.l");
|
||||
|
||||
@@ -19,6 +19,7 @@ Body -> Result<Vec<Stmt>>:
|
||||
|
||||
Stmt -> Result<Stmt>:
|
||||
Expr { Ok(Stmt::Expr($1?)) }
|
||||
//| Assign { todo!() }
|
||||
;
|
||||
|
||||
Expr -> Result<Expr>: BinExpr { $1 };
|
||||
@@ -42,31 +43,24 @@ BinExpr -> Result<Expr>:
|
||||
UnExpr -> Result<Expr>:
|
||||
'+' 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<Expr>:
|
||||
IndexExpr { $1 }
|
||||
| CallExpr { $1 }
|
||||
| AccessExpr { $1 }
|
||||
;
|
||||
|
||||
AccessExpr -> Result<Expr>:
|
||||
AtomExpr AccessExprTail {
|
||||
Ok(AccessExpr::new_expr($1?, $2?))
|
||||
CallExpr -> Result<Expr>:
|
||||
CallIndexExpr '(' FunArgs ')' {
|
||||
Ok(CallExpr::new_expr($1?, $3?))
|
||||
}
|
||||
| AtomExpr {
|
||||
Ok(AccessExpr::new_expr($1?, Default::default()))
|
||||
}
|
||||
;
|
||||
|
||||
AccessExprTail -> Result<Vec<Access>>:
|
||||
AccessExprTail '.' Ident ExprTrailing {
|
||||
flatten($1, Ok(Access { access: $3?, trailing: $4? }))
|
||||
}
|
||||
| '.' Ident ExprTrailing {
|
||||
Ok(vec![Access { access: $2?, trailing: $3? }])
|
||||
}
|
||||
;
|
||||
|
||||
ExprTrailing -> Result<Vec<ExprTrail>>:
|
||||
ExprTrailing '(' FunArgs ')' { flatten($1, Ok(ExprTrail::Call($3?))) }
|
||||
| ExprTrailing '[' Expr ']' { flatten($1, Ok(ExprTrail::Index($3?))) }
|
||||
| { Ok(Vec::new()) }
|
||||
;
|
||||
|
||||
FunArgs -> Result<Vec<Expr>>:
|
||||
@@ -79,6 +73,17 @@ FunArgsTail -> Result<Vec<Expr>>:
|
||||
| Expr { Ok(vec![$1?]) }
|
||||
;
|
||||
|
||||
IndexExpr -> Result<Expr>:
|
||||
CallIndexExpr '[' Expr ']' {
|
||||
Ok(IndexExpr::new_expr($1?, $3?))
|
||||
}
|
||||
;
|
||||
|
||||
AccessExpr -> Result<Expr>:
|
||||
AtomExpr { $1 }
|
||||
| CallIndexExpr '.' Ident { Ok(AccessExpr::new_expr($1?, $3?)) }
|
||||
;
|
||||
|
||||
AtomExpr -> Result<Expr>:
|
||||
Atom { $1.map(Expr::Atom) }
|
||||
| '(' Expr ')' { $2 }
|
||||
|
||||
24
src/syn/visit.rs
Normal file
24
src/syn/visit.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::syn::ast::*;
|
||||
|
||||
pub trait Accept<V: Visit + ?Sized> {
|
||||
fn accept(&self, visitor: &mut V) -> V::Out;
|
||||
}
|
||||
|
||||
pub trait DefaultAccept<V: Visit + ?Sized> {
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user