diff options
| -rw-r--r-- | pkg/lang/compiler/compiler.go | 188 | ||||
| -rw-r--r-- | pkg/lang/compiler/compiler_test.go | 83 |
2 files changed, 260 insertions, 11 deletions
diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go index bf34487..e299fdc 100644 --- a/pkg/lang/compiler/compiler.go +++ b/pkg/lang/compiler/compiler.go @@ -6,36 +6,202 @@ import ( ) type Compiler struct { - ast ast.Program + ast ast.Program + builder code.Builder } func New(ast ast.Program) *Compiler { return &Compiler{ - ast: ast, + ast: ast, + builder: code.NewBuilder(), } } func (comp *Compiler) Compile() (code.Code, error) { - bc := make([]byte, 0, 1024) - for _, stmt := range comp.ast.Stmts { + if stmt.Kind == ast.StmtKindEmpty { + continue + } + if stmt.Kind != ast.StmtKindExpr { - panic("statements other than expressions not implemented") + panic("statements other than expressions and empties not implemented") } expr := stmt.Value.(ast.StmtExpr).Value - res, err := comp.compileExpr(expr) - if err != nil { + if err := comp.compileExpr(expr); err != nil { return code.Code{}, err } + } + + return comp.builder.Build(), nil +} + +func (comp *Compiler) compileExpr(expr ast.Expr) error { + switch expr.Kind { + case ast.ExprKindBinary: + return comp.compileBinaryExpr(expr.Value.(ast.ExprBinary)) + case ast.ExprKindUnary: + return comp.compileUnaryExpr(expr.Value.(ast.ExprUnary)) + case ast.ExprKindCall: + return comp.compileCallExpr(expr.Value.(ast.ExprCall)) + case ast.ExprKindSubscription: + return comp.compileSubscriptionExpr(expr.Value.(ast.ExprSubscription)) + + case ast.ExprKindGroup: + return comp.compileGroupExpr(expr.Value.(ast.ExprGroup)) + case ast.ExprKindFnLit: + panic("not implemented") + case ast.ExprKindArrayLit: + panic("not implemented") + case ast.ExprKindIdent: + panic("not implemented") + case ast.ExprKindIntLit: + return comp.compileIntLitExpr(expr.Value.(ast.ExprIntLit)) + case ast.ExprKindFloatLit: + return comp.compileFloatLitExpr(expr.Value.(ast.ExprFloatLit)) + case ast.ExprKindStringLit: + return comp.compileStringLitExpr(expr.Value.(ast.ExprStringLit)) + case ast.ExprKindBoolLit: + return comp.compileBoolLitExpr(expr.Value.(ast.ExprBoolLit)) + case ast.ExprKindNullLit: + return comp.compileNullLitExpr(expr.Value.(ast.ExprNullLit)) + case ast.ExprKindThis: + panic("not implemented") + default: + panic("unknown expression kind") + } +} + +func (comp *Compiler) compileBinaryExpr(expr ast.ExprBinary) error { + if err := comp.compileExpr(expr.Right); err != nil { + return err + } + + if err := comp.compileExpr(expr.Left); err != nil { + return err + } + + switch expr.Op { + case ast.BinOpPlus: + comp.builder.AppendOp(code.OpAdd) + case ast.BinOpMinus: + comp.builder.AppendOp(code.OpSub) + case ast.BinOpStar: + // comp.builder.AppendOp(code.OpMul) + panic("not implemented") + case ast.BinOpSlash: + // comp.builder.AppendOp(code.OpDiv) + panic("not implemented") + case ast.BinOpPercent: + // comp.builder.AppendOp(code.OpMod) + panic("not implemented") + + case ast.BinOpAssign: + panic("not implemented") + case ast.BinOpEq: + // comp.builder.AppendOp(code.OpEq) + panic("not implemented") + case ast.BinOpNeq: + // comp.builder.AppendOp(code.OpNeq) + panic("not implemented") + case ast.BinOpLt: + // comp.builder.AppendOp(code.OpLt) + panic("not implemented") + case ast.BinOpLte: + comp.builder.AppendOp(code.OpLte) + case ast.BinOpGt: + // comp.builder.AppendOp(code.OpGt) + panic("not implemented") + case ast.BinOpGte: + // comp.builder.AppendOp(code.OpGte) + panic("not implemented") + default: + panic("unknown binary operator") + } + + return nil +} + +func (comp *Compiler) compileUnaryExpr(expr ast.ExprUnary) error { + if err := comp.compileExpr(expr.Value); err != nil { + return err + } + + switch expr.Op { + case ast.UnOpBang: + panic("not implemented") + case ast.UnOpMinus: + panic("not implemented") + default: + panic("unknown unary operator") + } + + return nil +} + +func (comp *Compiler) compileCallExpr(expr ast.ExprCall) error { + for i := len(expr.Args) - 1; i >= 0; i-- { + if err := comp.compileExpr(expr.Args[i]); err != nil { + return err + } + } + + if err := comp.compileExpr(expr.Callee); err != nil { + return err + } + + comp.builder.AppendOp(code.OpCall) + comp.builder.AppendInt(int64(len(expr.Args))) + + return nil +} + +func (comp *Compiler) compileSubscriptionExpr(expr ast.ExprSubscription) error { + if err := comp.compileExpr(expr.Key); err != nil { + return err + } - bc = append(bc, res...) + if err := comp.compileExpr(expr.Obj); err != nil { + return err } - return code.New(bc, code.NewDebugInfo("unknown file")), nil + comp.builder.AppendOp(code.OpIndex) + return nil +} + +func (comp *Compiler) compileGroupExpr(expr ast.ExprGroup) error { + return comp.compileExpr(expr.Value) +} + +func (comp *Compiler) compileIntLitExpr(expr ast.ExprIntLit) error { + comp.builder.AppendOp(code.OpPushInt) + comp.builder.AppendInt(int64(expr.Value)) + return nil +} + +func (comp *Compiler) compileFloatLitExpr(expr ast.ExprFloatLit) error { + comp.builder.AppendOp(code.OpPushFloat) + comp.builder.AppendFloat(expr.Value) + return nil +} + +func (comp *Compiler) compileStringLitExpr(expr ast.ExprStringLit) error { + comp.builder.AppendOp(code.OpPushString) + comp.builder.AppendString(expr.Value) + return nil +} + +func (comp *Compiler) compileBoolLitExpr(expr ast.ExprBoolLit) error { + if expr.Value { + comp.builder.AppendOp(code.OpPushTrue) + } else { + comp.builder.AppendOp(code.OpPushFalse) + } + return nil } -func (comp *Compiler) compileExpr(expr ast.Expr) ([]byte, error) { - panic("not implemented") +func (comp *Compiler) compileNullLitExpr(expr ast.ExprNullLit) error { + comp.builder.AppendOp(code.OpPushNull) + return nil } diff --git a/pkg/lang/compiler/compiler_test.go b/pkg/lang/compiler/compiler_test.go new file mode 100644 index 0000000..2477b21 --- /dev/null +++ b/pkg/lang/compiler/compiler_test.go @@ -0,0 +1,83 @@ +package compiler_test + +import ( + "jinx/pkg/lang/compiler" + "jinx/pkg/lang/parser" + "jinx/pkg/lang/scanner" + "jinx/pkg/lang/vm/text" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSimpleAddExpr(t *testing.T) { + src := ` + 1 + 2 + ` + + expected := ` + push_int 2 + push_int 1 + add + ` + + mustCompileTo(t, src, expected) +} + +func TestNestedExpr(t *testing.T) { + // Yeah, we don't compile identifiers yet, so here we call and index directly on literals. + src := ` + 1 + 2 - 3 - (123("a", "b", "c") + 456[321]) + ` + + expected := ` + push_int 321 + push_int 456 + index + + push_string "c" + push_string "b" + push_string "a" + push_int 123 + call + + add + + push_int 3 + sub + + push_int 2 + sub + + push_int 1 + add + ` + + mustCompileTo(t, src, expected) +} + +func mustCompileTo(t *testing.T, src, expected string) { + scanner := scanner.New(strings.NewReader(src)) + tokens, err := scanner.Scan() + require.NoError(t, err) + + parser := parser.New(tokens) + program, err := parser.Parse() + require.NoError(t, err) + + langCompiler := compiler.New(program) + testResult, err := langCompiler.Compile() + require.NoError(t, err) + + textCompiler := text.NewCompiler(strings.NewReader(expected)) + expectedResult, err := textCompiler.Compile() + require.NoError(t, err) + + if !assert.Equal(t, expectedResult.Code(), testResult.Code()) { + d1 := text.NewDecompiler(expectedResult) + d2 := text.NewDecompiler(testResult) + require.Equal(t, d1.Decompile(), d2.Decompile()) + } +} |
