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 ////////////////////////////////////////////////////////////////////////////////