about summary refs log tree commit diff
path: root/pkg/lang/parser/parser_test.go
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-04-21 03:19:06 +0200
committerMel <einebeere@gmail.com>2022-04-21 03:19:06 +0200
commitb5a9660b6ac42bce27c746e76013c3ce5992743a (patch)
tree0e9bd21a30aae25fe21a7a3994a48ac2cea8c955 /pkg/lang/parser/parser_test.go
parent5267c0e8653b431cfd2c06212cdba4f22225bd02 (diff)
downloadjinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.tar.zst
jinx-b5a9660b6ac42bce27c746e76013c3ce5992743a.zip
Lang parser prototype
Diffstat (limited to 'pkg/lang/parser/parser_test.go')
-rw-r--r--pkg/lang/parser/parser_test.go321
1 files changed, 321 insertions, 0 deletions
diff --git a/pkg/lang/parser/parser_test.go b/pkg/lang/parser/parser_test.go
new file mode 100644
index 0000000..969e878
--- /dev/null
+++ b/pkg/lang/parser/parser_test.go
@@ -0,0 +1,321 @@
+package parser_test
+
+import (
+	"jinx/pkg/lang/ast"
+	"jinx/pkg/lang/parser"
+	"jinx/pkg/lang/scanner"
+	"jinx/pkg/lang/scanner/token"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestIntLit(t *testing.T) {
+	source := `123`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindExpr,
+		Value: ast.StmtExpr{
+			Value: ast.Expr{
+				Kind:  ast.ExprKindIntLit,
+				Value: ast.ExprIntLit{Value: 123},
+			},
+		},
+	}, program.Stmts[0])
+}
+
+func TestSimpleBinaryExpr(t *testing.T) {
+	source := `1 + 2`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindExpr,
+		Value: ast.StmtExpr{
+			Value: 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:    token.NewLoc(0, 4),
+						Kind:  ast.ExprKindIntLit,
+						Value: ast.ExprIntLit{Value: 2},
+					},
+				},
+			},
+		},
+	}, program.Stmts[0])
+}
+
+func TestLeftAssocBinaryExpr(t *testing.T) {
+	source := `1 + 2 + 3`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindExpr,
+		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.ExprKindIntLit,
+								Value: ast.ExprIntLit{Value: 1},
+							},
+							Op: ast.BinOpPlus,
+							Right: ast.Expr{
+								At:    token.NewLoc(0, 4),
+								Kind:  ast.ExprKindIntLit,
+								Value: ast.ExprIntLit{Value: 2},
+							},
+						},
+					},
+					Op: ast.BinOpPlus,
+					Right: ast.Expr{
+						At:    token.NewLoc(0, 8),
+						Kind:  ast.ExprKindIntLit,
+						Value: ast.ExprIntLit{Value: 3},
+					},
+				},
+			},
+		},
+	}, program.Stmts[0])
+}
+
+func TestRightAssocBinaryExpr(t *testing.T) {
+	source := `x = y = z`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindExpr,
+		Value: ast.StmtExpr{
+			Value: ast.Expr{
+				Kind: ast.ExprKindBinary,
+				Value: ast.ExprBinary{
+					Left: ast.Expr{
+						Kind:  ast.ExprKindIdent,
+						Value: ast.ExprIdent{Value: ast.IdentNode{Value: "x"}},
+					},
+					Op: ast.BinOpAssign,
+					Right: ast.Expr{
+						At:   token.NewLoc(0, 4),
+						Kind: ast.ExprKindBinary,
+						Value: ast.ExprBinary{
+							Left: ast.Expr{
+								At:    token.NewLoc(0, 4),
+								Kind:  ast.ExprKindIdent,
+								Value: ast.ExprIdent{Value: ast.IdentNode{At: token.NewLoc(0, 4), Value: "y"}},
+							},
+							Op: ast.BinOpAssign,
+							Right: ast.Expr{
+								At:    token.NewLoc(0, 8),
+								Kind:  ast.ExprKindIdent,
+								Value: ast.ExprIdent{Value: ast.IdentNode{At: token.NewLoc(0, 8), Value: "z"}},
+							},
+						},
+					},
+				},
+			},
+		},
+	}, program.Stmts[0])
+}
+
+func TestVarDecl(t *testing.T) {
+	source := `var x = 123`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindVarDecl,
+		Value: ast.StmtVarDecl{
+			Name: ast.IdentNode{At: token.NewLoc(0, 4), Value: "x"},
+			Value: ast.Expr{
+				At:    token.NewLoc(0, 8),
+				Kind:  ast.ExprKindIntLit,
+				Value: ast.ExprIntLit{Value: 123},
+			},
+		},
+	}, program.Stmts[0])
+}
+
+func TestEmptyStmt(t *testing.T) {
+	source := `
+	
+	
+	;
+
+
+	`
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 4, len(program.Stmts))
+
+	expected := []ast.Stmt{
+		{
+			Kind:  ast.StmtKindEmpty,
+			Value: ast.StmtEmpty{},
+		},
+		{
+			At:    token.NewLoc(3, 1),
+			Kind:  ast.StmtKindEmpty,
+			Value: ast.StmtEmpty{},
+		},
+		{
+			At:    token.NewLoc(3, 2),
+			Kind:  ast.StmtKindEmpty,
+			Value: ast.StmtEmpty{},
+		},
+		{
+			At:    token.NewLoc(6, 1),
+			Kind:  ast.StmtKindEmpty,
+			Value: ast.StmtEmpty{},
+		},
+	}
+
+	require.Equal(t, expected, program.Stmts)
+}
+
+func TestMultipleStmts(t *testing.T) {
+	source := sourceify(
+		`var x = 1`,
+		`var y = 2`,
+		`z = 3`,
+	)
+
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 3, len(program.Stmts))
+
+	expected := []ast.Stmt{
+		{
+			Kind: ast.StmtKindVarDecl,
+			Value: ast.StmtVarDecl{
+				Name: ast.IdentNode{At: token.NewLoc(0, 4), Value: "x"},
+				Value: ast.Expr{
+					At:    token.NewLoc(0, 8),
+					Kind:  ast.ExprKindIntLit,
+					Value: ast.ExprIntLit{Value: 1},
+				},
+			},
+		},
+		{
+			At:   token.NewLoc(1, 0),
+			Kind: ast.StmtKindVarDecl,
+			Value: ast.StmtVarDecl{
+				Name: ast.IdentNode{At: token.NewLoc(1, 4), Value: "y"},
+				Value: ast.Expr{
+					At:    token.NewLoc(1, 8),
+					Kind:  ast.ExprKindIntLit,
+					Value: ast.ExprIntLit{Value: 2},
+				},
+			},
+		},
+		{
+			At:   token.NewLoc(2, 0),
+			Kind: ast.StmtKindExpr,
+			Value: ast.StmtExpr{
+				Value: ast.Expr{
+					At:   token.NewLoc(2, 0),
+					Kind: ast.ExprKindBinary,
+					Value: ast.ExprBinary{
+						Left: ast.Expr{
+							At:    token.NewLoc(2, 0),
+							Kind:  ast.ExprKindIdent,
+							Value: ast.ExprIdent{Value: ast.IdentNode{At: token.NewLoc(2, 0), Value: "z"}},
+						},
+						Op: ast.BinOpAssign,
+						Right: ast.Expr{
+							At:    token.NewLoc(2, 4),
+							Kind:  ast.ExprKindIntLit,
+							Value: ast.ExprIntLit{Value: 3},
+						},
+					},
+				},
+			},
+		},
+	}
+	require.Equal(t, expected, program.Stmts)
+}
+
+func TestIfStmt(t *testing.T) {
+	source := sourceify(
+		`if false {`,
+		`	var x = 2`,
+		`}`,
+	)
+	p := cheatWithScanner(t, source)
+	program, err := p.Parse()
+	require.NoError(t, err)
+
+	require.Equal(t, 1, len(program.Stmts))
+	require.Equal(t, ast.Stmt{
+		Kind: ast.StmtKindIf,
+		Value: ast.StmtIf{
+			Cond: ast.Expr{
+				At:    token.NewLoc(0, 3),
+				Kind:  ast.ExprKindBoolLit,
+				Value: ast.ExprBoolLit{Value: false},
+			},
+			Then: ast.BlockNode{
+				At: token.NewLoc(0, 9),
+				Stmts: []ast.Stmt{
+					{
+						At:    token.NewLoc(0, 10),
+						Kind:  ast.StmtKindEmpty,
+						Value: ast.StmtEmpty{},
+					},
+					{
+						At:   token.NewLoc(1, 1),
+						Kind: ast.StmtKindVarDecl,
+						Value: ast.StmtVarDecl{
+							Name: ast.IdentNode{At: token.NewLoc(1, 5), Value: "x"},
+							Value: ast.Expr{
+								At:    token.NewLoc(1, 9),
+								Kind:  ast.ExprKindIntLit,
+								Value: ast.ExprIntLit{Value: 2},
+							},
+						},
+					},
+				},
+			},
+			Elifs: []ast.CondNode{},
+		},
+	}, program.Stmts[0])
+}
+
+func sourceify(lines ...string) string {
+	return strings.Join(lines, "\n")
+}
+
+func cheatWithScanner(t *testing.T, source string) *parser.Parser {
+	s := scanner.New(strings.NewReader(source))
+	tokens, err := s.Scan()
+	require.NoError(t, err, "scanner error occurred while testing parser :(")
+
+	return parser.New(tokens)
+}