diff options
Diffstat (limited to 'pkg/lang/parser/parser_test.go')
| -rw-r--r-- | pkg/lang/parser/parser_test.go | 321 |
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) +} |
