aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-06-14 00:07:40 +0000
committerGitHub <noreply@github.com>2022-06-14 00:07:40 +0000
commit3498876f06104515002191468fd99019d40051c2 (patch)
tree7f1c3a34fe442382c82519a94ae089f22e252f71
parent14bdb59c24aef85f2a7c69f03ddacb7f56445e9e (diff)
downloadjinx-3498876f06104515002191468fd99019d40051c2.tar.zst
jinx-3498876f06104515002191468fd99019d40051c2.zip
Fix BinExpr precendence not being drilled down
-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)