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 <alekratz@gmail.com>
This commit is contained in:
@@ -142,7 +142,7 @@ impl<'t> Parser<'t> {
|
|||||||
// Expression parsing
|
// Expression parsing
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
macro_rules! bin_expr {
|
macro_rules! bin_expr_right {
|
||||||
($name:ident, $op_tokens:expr, $next:ident) => {
|
($name:ident, $op_tokens:expr, $next:ident) => {
|
||||||
fn $name(&mut self) -> Result<Expr> {
|
fn $name(&mut self) -> Result<Expr> {
|
||||||
let lhs = self.$next()?;
|
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<Expr> {
|
||||||
|
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> {
|
impl<'t> Parser<'t> {
|
||||||
pub fn next_expr(&mut self) -> Result<Expr> {
|
pub fn next_expr(&mut self) -> Result<Expr> {
|
||||||
self.next_bin_cmp_expr()
|
self.next_bin_cmp_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// == < > <= >=
|
// == < > <= >=
|
||||||
bin_expr!(
|
bin_expr_right!(
|
||||||
next_bin_cmp_expr,
|
next_bin_cmp_expr,
|
||||||
&[
|
&[
|
||||||
TokenKind::EqEq,
|
TokenKind::EqEq,
|
||||||
@@ -176,19 +194,37 @@ impl<'t> Parser<'t> {
|
|||||||
next_bin_add_expr
|
next_bin_add_expr
|
||||||
);
|
);
|
||||||
// + -
|
// + -
|
||||||
bin_expr!(
|
bin_expr_right!(
|
||||||
next_bin_add_expr,
|
next_bin_add_expr,
|
||||||
&[TokenKind::Plus, TokenKind::Minus],
|
&[TokenKind::Plus, TokenKind::Minus],
|
||||||
next_bin_mul_expr
|
next_bin_mul_expr
|
||||||
);
|
);
|
||||||
// * /
|
// * /
|
||||||
bin_expr!(
|
bin_expr_right!(
|
||||||
next_bin_mul_expr,
|
next_bin_mul_expr,
|
||||||
&[TokenKind::FSlash, TokenKind::Splat],
|
&[TokenKind::FSlash, TokenKind::Splat],
|
||||||
next_dot_expr
|
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<Expr> {
|
||||||
|
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<Expr> {
|
fn next_un_expr(&mut self) -> Result<Expr> {
|
||||||
if let Some(un_op) = self.match_any_token_kind(&UN_EXPR_START)? {
|
if let Some(un_op) = self.match_any_token_kind(&UN_EXPR_START)? {
|
||||||
@@ -304,7 +340,8 @@ impl<'t> Parser<'t> {
|
|||||||
_ => unreachable!(),
|
_ => 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
|
/// Takes an expression and attempts to match a suffix for it - either a function call or
|
||||||
@@ -974,17 +1011,20 @@ mod test {
|
|||||||
test_parser!(
|
test_parser!(
|
||||||
Parser::try_from("foo.bar().baz(qux)").unwrap(),
|
Parser::try_from("foo.bar().baz(qux)").unwrap(),
|
||||||
next_expr,
|
next_expr,
|
||||||
bin_expr(
|
fun_call_expr(
|
||||||
base_expr(BaseExprKind::Ident),
|
|
||||||
BinOp::Dot,
|
|
||||||
bin_expr(
|
bin_expr(
|
||||||
fun_call_expr(base_expr(BaseExprKind::Ident), vec![]),
|
|
||||||
BinOp::Dot,
|
|
||||||
fun_call_expr(
|
fun_call_expr(
|
||||||
base_expr(BaseExprKind::Ident),
|
bin_expr(
|
||||||
vec![base_expr(BaseExprKind::Ident)]
|
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
|
// Stmt testing
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user