From 895120776a0ae4dd121029f1730d29ca886e201e Mon Sep 17 00:00:00 2001 From: Mel Date: Thu, 28 Jul 2022 21:18:09 +0000 Subject: Fix nested stacked exprs and parse members --- pkg/lang/ast/expr.go | 6 +++ pkg/lang/ast/op.go | 8 --- pkg/lang/parser/exprs.go | 113 ++++++++++++++++++++++++++--------------- pkg/lang/parser/parser_test.go | 49 ++++++++++++++++++ pkg/lang/parser/stmts.go | 2 +- 5 files changed, 128 insertions(+), 50 deletions(-) (limited to 'pkg') diff --git a/pkg/lang/ast/expr.go b/pkg/lang/ast/expr.go index b0ed599..aceed18 100644 --- a/pkg/lang/ast/expr.go +++ b/pkg/lang/ast/expr.go @@ -11,6 +11,7 @@ const ( ExprKindUnary ExprKindCall ExprKindSubscription + ExprKindMember ExprKindGroup ExprKindFnLit @@ -57,6 +58,11 @@ type ExprSubscription struct { Key Expr } +type ExprMember struct { + Obj Expr + Key IdentNode +} + type ExprGroup struct { Value Expr } diff --git a/pkg/lang/ast/op.go b/pkg/lang/ast/op.go index 063a309..6f48e0c 100644 --- a/pkg/lang/ast/op.go +++ b/pkg/lang/ast/op.go @@ -21,8 +21,6 @@ const ( BinOpLte BinOpGt BinOpGte - - BinOpDot ) func BinOpFromToken(tok token.Token) (BinOp, bool) { @@ -52,8 +50,6 @@ func BinOpFromToken(tok token.Token) (BinOp, bool) { op = BinOpGt case token.Gte: op = BinOpGte - case token.Dot: - op = BinOpDot default: return 0, false } @@ -71,8 +67,6 @@ func (op BinOp) Precedence() int { return 3 case BinOpStar, BinOpSlash, BinOpPercent: return 4 - case BinOpDot: - return 5 default: panic(fmt.Sprintf("unknown binary operator: %d", op)) @@ -94,8 +88,6 @@ func (op BinOp) Associativity() Associativity { return AssociativityRight case BinOpEq, BinOpNeq, BinOpLt, BinOpLte, BinOpGt, BinOpGte: return AssociativityLeft - case BinOpDot: - return AssociativityLeft default: panic(fmt.Sprintf("unknown binary operator: %d", op)) } diff --git a/pkg/lang/parser/exprs.go b/pkg/lang/parser/exprs.go index 80b0fd6..48d1a8c 100644 --- a/pkg/lang/parser/exprs.go +++ b/pkg/lang/parser/exprs.go @@ -65,7 +65,7 @@ func (p *Parser) parseUnaryExpr() (ast.Expr, error) { func (p *Parser) parsePostfixExpr() (ast.Expr, error) { // PostfixExpr = SubscriptionExpr | CallExpr - obj, err := p.parseUnitExpr() + obj, err := p.parseMemberExpr() if err != nil { return ast.Expr{}, err } @@ -81,42 +81,41 @@ func (p *Parser) parsePostfixExpr() (ast.Expr, error) { } func (p *Parser) parseArrayIndexExpr(obj ast.Expr) (ast.Expr, error) { - // SubscriptionExpr = UnitExpr ( "[" Expr "]" )* + // SubscriptionExpr = MemberExpr ( "[" Expr "]" )* - if _, err := p.expect(token.LBracket); err != nil { - return ast.Expr{}, err - } + for p.peek().Kind == token.LBracket { + p.next() - index, err := p.parseExpr() - if err != nil { - return ast.Expr{}, err - } + index, err := p.parseExpr() + if err != nil { + return ast.Expr{}, err + } - if _, err := p.expect(token.RBracket); err != nil { - return ast.Expr{}, err - } + if _, err := p.expect(token.RBracket); err != nil { + return ast.Expr{}, err + } - return ast.Expr{ - At: obj.At, - Kind: ast.ExprKindSubscription, - Value: ast.ExprSubscription{ - Obj: obj, - Key: index, - }, - }, nil + obj = ast.Expr{ + At: obj.At, + Kind: ast.ExprKindSubscription, + Value: ast.ExprSubscription{ + Obj: obj, + Key: index, + }, + } + } + return obj, nil } func (p *Parser) parseCallExpr(callee ast.Expr) (ast.Expr, error) { - // CallExpr = UnitExpr ( "(" ( Expr ( "," Expr )* )? ")" )* + // CallExpr = MemberExpr ( "(" ( Expr ( "," Expr )* )? ")" )* - if _, err := p.expect(token.LParen); err != nil { - return ast.Expr{}, err - } + for p.peek().Kind == token.LParen { + p.next() - args := []ast.Expr{} - if p.peek().Kind != token.RParen { - for { + args := []ast.Expr{} + for p.peek().Kind != token.RParen { arg, err := p.parseExpr() if err != nil { return ast.Expr{}, err @@ -124,29 +123,61 @@ func (p *Parser) parseCallExpr(callee ast.Expr) (ast.Expr, error) { args = append(args, arg) - if p.peek().Kind == token.RParen { + if p.peek().Kind == token.Comma { + p.next() + } else { break } + } - if _, err := p.expect(token.Comma); err != nil { - return ast.Expr{}, err - } + if _, err := p.expect(token.RParen); err != nil { + return ast.Expr{}, err + } + + callee = ast.Expr{ + At: callee.At, + Kind: ast.ExprKindCall, + Value: ast.ExprCall{ + Callee: callee, + Args: args, + }, } } - if _, err := p.expect(token.RParen); err != nil { + return callee, nil +} + +func (p *Parser) parseMemberExpr() (ast.Expr, error) { + // MemberExpr = UnitExpr ( "." Ident )* + + left, err := p.parseUnitExpr() + if err != nil { return ast.Expr{}, err } - return ast.Expr{ - At: callee.At, - Kind: ast.ExprKindCall, - Value: ast.ExprCall{ - Callee: callee, - Args: args, - }, - }, nil + for { + if p.peek().Kind != token.Dot { + break + } + + p.next() + + node, err := p.parseIdent() + if err != nil { + return ast.Expr{}, err + } + left = ast.Expr{ + At: left.At, + Kind: ast.ExprKindMember, + Value: ast.ExprMember{ + Obj: left, + Key: node, + }, + } + } + + return left, nil } func (p *Parser) parseUnitExpr() (ast.Expr, error) { @@ -176,7 +207,7 @@ func (p *Parser) parseUnitExpr() (ast.Expr, error) { case token.KwTrue, token.KwFalse, token.KwNull, token.KwThis: return p.parseValueLitExpr() default: - panic(fmt.Errorf("unexpected token '%v', wanted unit expression start", p.peek())) + panic(fmt.Errorf("unexpected token '%v', wanted unit expression start", p.peek().Kind)) } } diff --git a/pkg/lang/parser/parser_test.go b/pkg/lang/parser/parser_test.go index de97928..24f3044 100644 --- a/pkg/lang/parser/parser_test.go +++ b/pkg/lang/parser/parser_test.go @@ -370,6 +370,55 @@ func TestMultipleStmts(t *testing.T) { require.Equal(t, expected, program.Stmts) } +func TestRepeatingNestedExpr(t *testing.T) { + src := "x.y.z()()" + + p := cheatWithScanner(t, src) + program, err := p.Parse() + require.NoError(t, err) + + require.Equal(t, 1, len(program.Stmts)) + require.Equal(t, ast.Stmt{ + At: source.NewLoc(0, 0), + Kind: ast.StmtKindExpr, + Value: ast.StmtExpr{ + Value: ast.Expr{ + At: source.NewLoc(0, 0), + Kind: ast.ExprKindCall, + Value: ast.ExprCall{ + Callee: ast.Expr{ + At: source.NewLoc(0, 0), + Kind: ast.ExprKindCall, + Value: ast.ExprCall{ + Callee: ast.Expr{ + At: source.NewLoc(0, 0), + Kind: ast.ExprKindMember, + Value: ast.ExprMember{ + Obj: ast.Expr{ + At: source.NewLoc(0, 0), + Kind: ast.ExprKindMember, + Value: ast.ExprMember{ + Obj: ast.Expr{ + At: source.NewLoc(0, 0), + Kind: ast.ExprKindIdent, + Value: ast.ExprIdent{Value: ast.IdentNode{At: source.NewLoc(0, 0), Value: "x"}}, + }, + Key: ast.IdentNode{At: source.NewLoc(0, 2), Value: "y"}, + }, + }, + Key: ast.IdentNode{At: source.NewLoc(0, 4), Value: "z"}, + }, + }, + Args: []ast.Expr{}, + }, + }, + Args: []ast.Expr{}, + }, + }, + }, + }, program.Stmts[0]) +} + func TestIfStmt(t *testing.T) { src := sourceify( `if false {`, diff --git a/pkg/lang/parser/stmts.go b/pkg/lang/parser/stmts.go index a0bba28..33719ce 100644 --- a/pkg/lang/parser/stmts.go +++ b/pkg/lang/parser/stmts.go @@ -546,7 +546,7 @@ func (p *Parser) parseEmptyStmt() (ast.Stmt, error) { func (p *Parser) parseStmtEnd() error { tok := p.peek() if !tok.CanEndStmt() { - panic(fmt.Errorf("wanted statement end, received: '%v'", tok)) + return fmt.Errorf("wanted statement end, received: '%v'", tok.Kind) } p.next() return nil -- cgit 1.4.1