about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-07-28 21:18:09 +0000
committerMel <einebeere@gmail.com>2022-07-28 21:18:09 +0000
commit895120776a0ae4dd121029f1730d29ca886e201e (patch)
tree98e4771fd83ec88d559de95f058f724747405f1a
parentd79973cb9df8660fe89810507557f5ba86256f30 (diff)
downloadjinx-895120776a0ae4dd121029f1730d29ca886e201e.tar.zst
jinx-895120776a0ae4dd121029f1730d29ca886e201e.zip
Fix nested stacked exprs and parse members
-rw-r--r--pkg/lang/ast/expr.go6
-rw-r--r--pkg/lang/ast/op.go8
-rw-r--r--pkg/lang/parser/exprs.go113
-rw-r--r--pkg/lang/parser/parser_test.go49
-rw-r--r--pkg/lang/parser/stmts.go2
5 files changed, 128 insertions, 50 deletions
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