about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--pkg/lang/parser/bin_order.go34
-rw-r--r--pkg/lang/parser/exprs.go35
-rw-r--r--pkg/lang/parser/parser_test.go51
3 files changed, 90 insertions, 30 deletions
diff --git a/pkg/lang/parser/bin_order.go b/pkg/lang/parser/bin_order.go
new file mode 100644
index 0000000..666d1fc
--- /dev/null
+++ b/pkg/lang/parser/bin_order.go
@@ -0,0 +1,34 @@
+package parser
+
+import (
+	"jinx/pkg/lang/ast"
+)
+
+func (p *Parser) mergeIntoBinary(left ast.Expr, op ast.BinOp, right ast.Expr) ast.ExprBinary {
+	if right.Kind == ast.ExprKindBinary {
+		rightBin := right.Value.(ast.ExprBinary)
+
+		needsSwitch := (op.Precedence() > rightBin.Op.Precedence()) ||
+			(op.Precedence() == rightBin.Op.Precedence() && op.Associativity() == ast.AssociativityLeft)
+
+		if needsSwitch {
+			leftBin := p.mergeIntoBinary(left, op, rightBin.Left)
+
+			left = ast.Expr{
+				At:    left.At,
+				Kind:  ast.ExprKindBinary,
+				Value: leftBin,
+			}
+
+			right = rightBin.Right
+
+			op = rightBin.Op
+		}
+	}
+
+	return ast.ExprBinary{
+		Left:  left,
+		Op:    op,
+		Right: right,
+	}
+}
diff --git a/pkg/lang/parser/exprs.go b/pkg/lang/parser/exprs.go
index b08864a..ceda018 100644
--- a/pkg/lang/parser/exprs.go
+++ b/pkg/lang/parser/exprs.go
@@ -26,38 +26,13 @@ func (p *Parser) parseBinaryExpr() (ast.Expr, error) {
 			return ast.Expr{}, err
 		}
 
-		// Check precedence and associativity.
-		if right.Kind == ast.ExprKindBinary {
-			rightBin := right.Value.(ast.ExprBinary)
-
-			needsSwitch := (op.Precedence() > rightBin.Op.Precedence()) ||
-				(op.Precedence() == rightBin.Op.Precedence() && op.Associativity() == ast.AssociativityLeft)
-
-			if needsSwitch {
-				left = ast.Expr{
-					At:   left.At,
-					Kind: ast.ExprKindBinary,
-					Value: ast.ExprBinary{
-						Left:  left,
-						Op:    op,
-						Right: rightBin.Left,
-					},
-				}
-
-				right = rightBin.Right
-
-				op = rightBin.Op
-			}
-		}
+		// Adjust for precedence and associativity.
+		binary := p.mergeIntoBinary(left, op, right)
 
 		left = ast.Expr{
-			At:   left.At,
-			Kind: ast.ExprKindBinary,
-			Value: ast.ExprBinary{
-				Left:  left,
-				Op:    op,
-				Right: right,
-			},
+			At:    left.At,
+			Kind:  ast.ExprKindBinary,
+			Value: binary,
 		}
 	}
 
diff --git a/pkg/lang/parser/parser_test.go b/pkg/lang/parser/parser_test.go
index c3794e9..64dd67a 100644
--- a/pkg/lang/parser/parser_test.go
+++ b/pkg/lang/parser/parser_test.go
@@ -98,6 +98,57 @@ func TestLeftAssocBinaryExpr(t *testing.T) {
 	}, program.Stmts[0])
 }
 
+func TestLeftAssocBinary4Expr(t *testing.T) {
+	src := `1 + 2 - 3 + 4`
+	p := cheatWithScanner(t, src)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: 11,
+		Value: ast.StmtExpr{
+			Value: ast.Expr{
+				Kind: ast.ExprKindBinary,
+				Value: ast.ExprBinary{
+					Left: ast.Expr{
+						Kind: ast.ExprKindBinary,
+						Value: ast.ExprBinary{
+							Left: ast.Expr{
+								Kind: ast.ExprKindBinary,
+								Value: ast.ExprBinary{
+									Left: ast.Expr{
+										Kind:  ast.ExprKindIntLit,
+										Value: ast.ExprIntLit{Value: 1},
+									},
+									Op: ast.BinOpPlus,
+									Right: ast.Expr{
+										At:    source.NewLoc(0, 4),
+										Kind:  ast.ExprKindIntLit,
+										Value: ast.ExprIntLit{Value: 2},
+									},
+								},
+							},
+							Op: ast.BinOpMinus,
+							Right: ast.Expr{
+								At:    source.NewLoc(0, 8),
+								Kind:  ast.ExprKindIntLit,
+								Value: ast.ExprIntLit{Value: 3},
+							},
+						},
+					},
+					Op: ast.BinOpPlus,
+					Right: ast.Expr{
+						At:    source.NewLoc(0, 12),
+						Kind:  ast.ExprKindIntLit,
+						Value: ast.ExprIntLit{Value: 4},
+					},
+				},
+			},
+		},
+	}, program.Stmts[0])
+}
+
 func TestRightAssocBinaryExpr(t *testing.T) {
 	src := `x = y = z`
 	p := cheatWithScanner(t, src)