package compiler import ( "fmt" "jinx/pkg/lang/ast" "jinx/pkg/lang/vm/code" ) type Compiler struct { ast ast.Program builder code.Builder scopes ScopeChain } func New(ast ast.Program) *Compiler { return &Compiler{ ast: ast, builder: code.NewBuilder(), scopes: NewScopeChain(), } } func (comp *Compiler) Compile() (code.Code, error) { for _, stmt := range comp.ast.Stmts { if err := comp.compileStmt(stmt); err != nil { return code.Code{}, err } } return comp.builder.Build(), nil } func (comp *Compiler) compileStmt(stmt ast.Stmt) error { var err error switch stmt.Kind { case ast.StmtKindEmpty: // Do nothing. case ast.StmtKindVarDecl: decl := stmt.Value.(ast.StmtVarDecl) err = comp.compileVarDeclStmt(decl) case ast.StmtKindExpr: expr := stmt.Value.(ast.StmtExpr).Value err = comp.compileExpr(expr) default: panic("statements other than expressions, variable declarations, var and empties not implemented") } return err } func (comp *Compiler) compileVarDeclStmt(decl ast.StmtVarDecl) error { if !comp.scopes.Declare(decl.Name.Value) { return fmt.Errorf("variable %s already declared", decl.Name.Value) } if err := comp.compileExpr(decl.Value); err != nil { return err } return 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: return comp.compileIdentExpr(expr.Value.(ast.ExprIdent)) 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 expr.Op == ast.BinOpAssign { return comp.compileAssignExpr(expr) } 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.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) compileAssignExpr(expr ast.ExprBinary) error { if expr.Left.Kind != ast.ExprKindIdent { return fmt.Errorf("lvalues other than identifiers not implemented") } name := expr.Left.Value.(ast.ExprIdent).Value.Value symbol, ok := comp.scopes.Lookup(name) if !ok { return fmt.Errorf("variable %s not declared", name) } if err := comp.compileExpr(expr.Right); err != nil { return err } comp.builder.AppendOp(code.OpSetLocal) comp.builder.AppendInt(int64(symbol.localIndex)) 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 } if err := comp.compileExpr(expr.Obj); err != nil { return err } comp.builder.AppendOp(code.OpIndex) return nil } func (comp *Compiler) compileGroupExpr(expr ast.ExprGroup) error { return comp.compileExpr(expr.Value) } func (comp *Compiler) compileIdentExpr(expr ast.ExprIdent) error { symbol, ok := comp.scopes.Lookup(expr.Value.Value) if !ok { return fmt.Errorf("undefined symbol %s", expr.Value.Value) } // TODO: Add boundries to check how the symbol should be fetched. (local, env, global, etc.) comp.builder.AppendOp(code.OpGetLocal) comp.builder.AppendInt(int64(symbol.localIndex)) return nil } 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) compileNullLitExpr(expr ast.ExprNullLit) error { comp.builder.AppendOp(code.OpPushNull) return nil }