From 65b8d3af583f527072b220524b6cddf417fce4d8 Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Wed, 27 May 2020 15:07:14 -0400 Subject: [PATCH] Update associativity of dot binary expressions and suffix expressions The problem being encountered was, given something like this: foo.bar() we would be getting an AST that looks like this: - binexpr: lhs: foo op: dot rhs: funcall: expr: bar params: [] which is the same as parsing like this: foo . (bar()) Which isn't what we want - it's like saying "get variable foo, and then call function bar(), and whatever is returned by bar() should be used to get an attribute from foo" and so forth. Instead, we want "get the attribute bar from foo, and then call it as a function," which parses like this: (foo . bar) () Dot expressions are now left-associative, and they also slurp function call and index expression suffixes so that a dot expression before a function call will be resolved before being called as a function. Signed-off-by: Alek Ratzloff --- src/syn/parser.rs | 95 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/src/syn/parser.rs b/src/syn/parser.rs index 0f9d915..6ae8cd0 100644 --- a/src/syn/parser.rs +++ b/src/syn/parser.rs @@ -142,7 +142,7 @@ impl<'t> Parser<'t> { // Expression parsing //////////////////////////////////////////////////////////////////////////////// -macro_rules! bin_expr { +macro_rules! bin_expr_right { ($name:ident, $op_tokens:expr, $next:ident) => { fn $name(&mut self) -> Result { let lhs = self.$next()?; @@ -158,13 +158,31 @@ macro_rules! bin_expr { }; } +/* + // left-associative rule - not currently used, but could be useful in the future +macro_rules! bin_expr_left { + ($name:ident, $op_tokens:expr, $next:ident) => { + fn $name(&mut self) -> Result { + let mut lhs = self.$next()?; + while let Some(token) = self.match_any_token_kind($op_tokens)? { + let op = BinOp::from(token); + let rhs = self.$next()?; + let span = lhs.span().union(rhs.span()); + lhs = Expr::Bin(BinExpr { lhs, op, rhs, span }.into()); + } + Ok(lhs) + } + }; +} +*/ + impl<'t> Parser<'t> { pub fn next_expr(&mut self) -> Result { self.next_bin_cmp_expr() } // == < > <= >= - bin_expr!( + bin_expr_right!( next_bin_cmp_expr, &[ TokenKind::EqEq, @@ -176,19 +194,37 @@ impl<'t> Parser<'t> { next_bin_add_expr ); // + - - bin_expr!( + bin_expr_right!( next_bin_add_expr, &[TokenKind::Plus, TokenKind::Minus], next_bin_mul_expr ); // * / - bin_expr!( + bin_expr_right!( next_bin_mul_expr, &[TokenKind::FSlash, TokenKind::Splat], next_dot_expr ); - // . - bin_expr!(next_dot_expr, &[TokenKind::Dot], next_un_expr); + + // Dot expressions also slurp function calls. This is so that something like: + // + // foo.bar().baz() + // + // will parse as + // + // ((foo.bar)().baz)() + // + fn next_dot_expr(&mut self) -> Result { + let mut lhs = self.next_un_expr()?; + while let Some(token) = self.match_any_token_kind(&[TokenKind::Dot])? { + let op = BinOp::from(token); + let rhs = self.next_un_expr()?; + let span = lhs.span().union(rhs.span()); + lhs = Expr::Bin(BinExpr { lhs, op, rhs, span }.into()); + lhs = self.next_suffix_expr(lhs)?; + } + self.next_suffix_expr(lhs) + } fn next_un_expr(&mut self) -> Result { if let Some(un_op) = self.match_any_token_kind(&UN_EXPR_START)? { @@ -304,7 +340,8 @@ impl<'t> Parser<'t> { _ => unreachable!(), }; - self.next_suffix_expr(expr) + //self.next_suffix_expr(expr) + Ok(expr) } /// Takes an expression and attempts to match a suffix for it - either a function call or @@ -974,17 +1011,20 @@ mod test { test_parser!( Parser::try_from("foo.bar().baz(qux)").unwrap(), next_expr, - bin_expr( - base_expr(BaseExprKind::Ident), - BinOp::Dot, + fun_call_expr( bin_expr( - fun_call_expr(base_expr(BaseExprKind::Ident), vec![]), - BinOp::Dot, fun_call_expr( - base_expr(BaseExprKind::Ident), - vec![base_expr(BaseExprKind::Ident)] + bin_expr( + base_expr(BaseExprKind::Ident), + BinOp::Dot, + base_expr(BaseExprKind::Ident) + ), + vec![] ), - ) + BinOp::Dot, + base_expr(BaseExprKind::Ident) + ), + vec![base_expr(BaseExprKind::Ident)] ) ); } @@ -1161,6 +1201,31 @@ mod test { ); } + #[test] + fn test_expr_left_assoc() { + test_parser! { + Parser::try_from(r"a.b.c.d").unwrap(), + next_expr, + bin_expr( + bin_expr( + bin_expr( + // a + base_expr(BaseExprKind::Ident), + BinOp::Dot, + // b + base_expr(BaseExprKind::Ident), + ), + BinOp::Dot, + // c + base_expr(BaseExprKind::Ident), + ), + BinOp::Dot, + // d + base_expr(BaseExprKind::Ident), + ) + } + } + //////////////////////////////////////////////////////////////////////////////// // Stmt testing ////////////////////////////////////////////////////////////////////////////////