diff options
Diffstat (limited to 'pkg/lang/compiler/compiler.go')
| -rw-r--r-- | pkg/lang/compiler/compiler.go | 132 |
1 files changed, 120 insertions, 12 deletions
diff --git a/pkg/lang/compiler/compiler.go b/pkg/lang/compiler/compiler.go index b1fd961..3a5efd9 100644 --- a/pkg/lang/compiler/compiler.go +++ b/pkg/lang/compiler/compiler.go @@ -48,6 +48,9 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error { case ast.StmtKindForCond: forCondStmt := stmt.Value.(ast.StmtForCond) err = comp.compileForCondStmt(t, forCondStmt) + case ast.StmtKindForIn: + forCondIn := stmt.Value.(ast.StmtForIn) + err = comp.compileForInStmt(t, forCondIn) case ast.StmtKindExpr: expr := stmt.Value.(ast.StmtExpr).Value err = comp.compileExpr(t, expr) @@ -59,14 +62,14 @@ func (comp *Compiler) compileStmt(t *code.Builder, stmt ast.Stmt) error { } func (comp *Compiler) compileVarDeclStmt(t *code.Builder, 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(t, decl.Value); err != nil { return err } + if _, ok := comp.scopes.Declare(decl.Name.Value); !ok { + return fmt.Errorf("variable %s already declared", decl.Name.Value) + } + return nil } @@ -90,7 +93,6 @@ func (comp *Compiler) compileIfStmt(t *code.Builder, ifStmt ast.StmtIf) error { subUnits := make([]code.Builder, 0, len(ifStmt.Conds)) totalLength := 0 - jmpLength := 9 // The length of either of the jump parts: op: 1 + uint: 8 = 9 for i, cond := range ifStmt.Conds { // Then block @@ -101,7 +103,7 @@ func (comp *Compiler) compileIfStmt(t *code.Builder, ifStmt ast.StmtIf) error { totalLength += thenTarget.Len() if i != len(ifStmt.Conds)-1 { - totalLength += jmpLength + totalLength += lengthOfAJumpInstruction } // Condition check @@ -111,7 +113,7 @@ func (comp *Compiler) compileIfStmt(t *code.Builder, ifStmt ast.StmtIf) error { return err } - totalLength += conditionTarget.Len() + jmpLength // condjmp + totalLength += conditionTarget.Len() + lengthOfAJumpInstruction // condjmp conditionTarget.AppendOp(code.OpJf) // Condition jump @@ -153,8 +155,6 @@ func (comp *Compiler) compileForCondStmt(t *code.Builder, forCondStmt ast.StmtFo return err } - jmpLength := 9 - conditionTarget := code.NewBuilder() if !forCondStmt.Cond.IsEmpty() { // Condition check @@ -162,7 +162,7 @@ func (comp *Compiler) compileForCondStmt(t *code.Builder, forCondStmt ast.StmtFo return err } - endOfFor := conditionTarget.Len() + doTarget.Len() + jmpLength*2 + endOfFor := conditionTarget.Len() + doTarget.Len() + lengthOfAJumpInstruction*2 // Condition jump conditionTarget.AppendOp(code.OpJf) @@ -181,6 +181,111 @@ func (comp *Compiler) compileForCondStmt(t *code.Builder, forCondStmt ast.StmtFo return nil } +func (comp *Compiler) compileForInStmt(t *code.Builder, forInStmt ast.StmtForIn) error { + // Mostly same as ForCond, but the condition is implicit. + + // Example for: `for x in [] {}` + + // 0. Preparation + // push_array # collection stored in local 0 + // push_int 0 # i stored in local 1 + // push_null # x stored in local 2 + // 1. Condition check (i < x.length()) + // @check: + // get_local 1 + // get_local 0 + // get_member "$length" + // call 0 + // lt + // 2. Condition jump + // jf @end + // 3.1 Do preparation (aka setting the x variable) + // get_local 0 + // get_local 1 + // index + // set_local 2 + // 3. Do block + // ... + // 4. Repeat jump: + // jmp @check + // @end: + // halt + + // Preparation + preparationTarget := code.NewBuilder() + if err := comp.compileExpr(&preparationTarget, forInStmt.Collection); err != nil { + return err + } + collectionLocal := comp.scopes.DeclareAnonymous() + + preparationTarget.AppendOp(code.OpPushInt) + preparationTarget.AppendInt(0) + iLocal := comp.scopes.DeclareAnonymous() + + preparationTarget.AppendOp(code.OpPushNull) + nameLocal, ok := comp.scopes.Declare(forInStmt.Name.Value) + if !ok { + return fmt.Errorf("variable %s already declared", forInStmt.Name.Value) + } + + // Condition check + conditionTarget := code.NewBuilder() + + conditionTarget.AppendOp(code.OpGetLocal) + conditionTarget.AppendInt(int64(iLocal)) + + conditionTarget.AppendOp(code.OpGetLocal) + conditionTarget.AppendInt(int64(collectionLocal)) + + conditionTarget.AppendOp(code.OpGetMember) + conditionTarget.AppendString("length") + + conditionTarget.AppendOp(code.OpCall) + conditionTarget.AppendInt(0) + + conditionTarget.AppendOp(code.OpLt) + + // Do Preparation + doPreparationTarget := code.NewBuilder() + + doPreparationTarget.AppendOp(code.OpGetLocal) + doPreparationTarget.AppendInt(int64(collectionLocal)) + + doPreparationTarget.AppendOp(code.OpGetLocal) + doPreparationTarget.AppendInt(int64(iLocal)) + + doPreparationTarget.AppendOp(code.OpIndex) + + doPreparationTarget.AppendOp(code.OpSetLocal) + doPreparationTarget.AppendInt(int64(nameLocal)) + + // Do block + doTarget := code.NewBuilder() + if err := comp.compileBlockNode(&doTarget, forInStmt.Do); err != nil { + return err + } + + // Condition Jump + + endOfFor := preparationTarget.Len() + conditionTarget.Len() + doPreparationTarget.Len() + doTarget.Len() + lengthOfAJumpInstruction*2 + + conditionTarget.AppendOp(code.OpJf) + conditionTarget.AppendReferenceToPc(int64(endOfFor)) + + subUnit := preparationTarget + subUnit.AppendBuilderWithoutAdjustingReferences(conditionTarget) + subUnit.AppendBuilder(doPreparationTarget) + subUnit.AppendBuilder(doTarget) + + // Repeat jump + subUnit.AppendOp(code.OpJmp) + subUnit.AppendReferenceToPc(int64(preparationTarget.Len())) + + t.AppendBuilder(subUnit) + + return nil +} + func (comp *Compiler) compileExpr(t *code.Builder, expr ast.Expr) error { switch expr.Kind { case ast.ExprKindBinary: @@ -252,8 +357,7 @@ func (comp *Compiler) compileBinaryExpr(t *code.Builder, expr ast.ExprBinary) er // t.AppendOp(code.OpNeq) panic("not implemented") case ast.BinOpLt: - // t.AppendOp(code.OpLt) - panic("not implemented") + t.AppendOp(code.OpLt) case ast.BinOpLte: t.AppendOp(code.OpLte) case ast.BinOpGt: @@ -417,3 +521,7 @@ func (comp *Compiler) compileBlockNode(t *code.Builder, block ast.BlockNode) err return nil } + +const ( + lengthOfAJumpInstruction = 9 // The length of a jump Op (jmp, jf, jt) and it's following 64-bit integer. +) |
