|
|
@@ -18,22 +18,30 @@ type EmittedInstruction struct {
|
|
|
Position int
|
|
|
}
|
|
|
type Compiler struct {
|
|
|
- instructions code.Instructions // hold the generated bytecode
|
|
|
- constants []object.Object // slice that serves as our constant pool
|
|
|
+ constants []object.Object // slice that serves as our constant pool
|
|
|
|
|
|
+ symbolTable *SymbolTable
|
|
|
+
|
|
|
+ scopes []CompilationScope
|
|
|
+ scopeIndex int
|
|
|
+}
|
|
|
+type CompilationScope struct {
|
|
|
+ instructions code.Instructions // hold the generated bytecode
|
|
|
lastInstruction EmittedInstruction
|
|
|
previousInstruction EmittedInstruction
|
|
|
-
|
|
|
- symbolTable *SymbolTable
|
|
|
}
|
|
|
|
|
|
func New() *Compiler {
|
|
|
- return &Compiler{
|
|
|
+ mainScope := CompilationScope{
|
|
|
instructions: code.Instructions{},
|
|
|
- constants: []object.Object{},
|
|
|
lastInstruction: EmittedInstruction{},
|
|
|
previousInstruction: EmittedInstruction{},
|
|
|
- symbolTable: NewSymbolTable(),
|
|
|
+ }
|
|
|
+ return &Compiler{
|
|
|
+ constants: []object.Object{},
|
|
|
+ symbolTable: NewSymbolTable(),
|
|
|
+ scopes: []CompilationScope{mainScope},
|
|
|
+ scopeIndex: 0,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -134,7 +142,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|
|
// Emit an `OpJump` with a bogus value
|
|
|
jumpPos := c.emit(code.OpJump, 9999)
|
|
|
|
|
|
- afterConsequencePos := len(c.instructions)
|
|
|
+ afterConsequencePos := len(c.currentInstructions())
|
|
|
c.changOperand(jumpNotTruthyPos, afterConsequencePos)
|
|
|
|
|
|
if node.Alternative == nil {
|
|
|
@@ -149,7 +157,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- afterAlternativePos := len(c.instructions)
|
|
|
+ afterAlternativePos := len(c.currentInstructions())
|
|
|
c.changOperand(jumpPos, afterAlternativePos)
|
|
|
case *ast.BlockStatement:
|
|
|
for _, s := range node.Statements {
|
|
|
@@ -226,6 +234,24 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|
|
}
|
|
|
|
|
|
c.emit(code.OpIndex)
|
|
|
+ case *ast.ReturnStatement:
|
|
|
+ err := c.Compile(node.ReturnValue)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ c.emit(code.OpReturnValue)
|
|
|
+ case *ast.FunctionLiteral:
|
|
|
+ c.enterScope()
|
|
|
+
|
|
|
+ err := c.Compile(node.Body)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ instructions := c.leaveScope()
|
|
|
+
|
|
|
+ compiledFn := &object.CompileFunction{Instructions: instructions}
|
|
|
+ c.emit(code.OpConstant, c.addConstant(compiledFn))
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
@@ -236,8 +262,11 @@ func (c *Compiler) addConstant(obj object.Object) int {
|
|
|
}
|
|
|
|
|
|
func (c *Compiler) addInstruction(ins []byte) int {
|
|
|
- posNewInstruction := len(c.instructions)
|
|
|
- c.instructions = append(c.instructions, ins...)
|
|
|
+ posNewInstruction := len(c.currentInstructions())
|
|
|
+ updatedInstructions := append(c.currentInstructions(), ins...)
|
|
|
+
|
|
|
+ c.scopes[c.scopeIndex].instructions = updatedInstructions
|
|
|
+
|
|
|
return posNewInstruction
|
|
|
}
|
|
|
|
|
|
@@ -258,37 +287,68 @@ func (c *Compiler) emit(op code.Opcode, operands ...int) int {
|
|
|
}
|
|
|
|
|
|
func (c *Compiler) setLastInstruction(op code.Opcode, pos int) {
|
|
|
- previous := c.lastInstruction
|
|
|
+ previous := c.scopes[c.scopeIndex].lastInstruction
|
|
|
last := EmittedInstruction{Opcode: op, Position: pos}
|
|
|
|
|
|
- c.previousInstruction = previous
|
|
|
- c.lastInstruction = last
|
|
|
+ c.scopes[c.scopeIndex].previousInstruction = previous
|
|
|
+ c.scopes[c.scopeIndex].lastInstruction = last
|
|
|
}
|
|
|
|
|
|
func (c *Compiler) lastInstructionIsPop() bool {
|
|
|
- return c.lastInstruction.Opcode == code.OpPop
|
|
|
+ return c.scopes[c.scopeIndex].lastInstruction.Opcode == code.OpPop
|
|
|
}
|
|
|
|
|
|
func (c *Compiler) removeLastPop() {
|
|
|
- c.instructions = c.instructions[:c.lastInstruction.Position]
|
|
|
- c.lastInstruction = c.previousInstruction
|
|
|
+ last := c.scopes[c.scopeIndex].lastInstruction
|
|
|
+ previous := c.scopes[c.scopeIndex].previousInstruction
|
|
|
+
|
|
|
+ oldIns := c.currentInstructions()
|
|
|
+ newIns := oldIns[:last.Position]
|
|
|
+
|
|
|
+ c.scopes[c.scopeIndex].instructions = newIns
|
|
|
+ c.scopes[c.scopeIndex].lastInstruction = previous
|
|
|
+
|
|
|
}
|
|
|
|
|
|
func (c *Compiler) replaceInstruction(pos int, newInstruction []byte) {
|
|
|
+ ins := c.currentInstructions()
|
|
|
for i := 0; i < len(newInstruction); i++ {
|
|
|
- c.instructions[pos+i] = newInstruction[i]
|
|
|
+ ins[pos+i] = newInstruction[i]
|
|
|
}
|
|
|
}
|
|
|
func (c *Compiler) changOperand(opPos int, operand int) {
|
|
|
- op := code.Opcode(c.instructions[opPos])
|
|
|
+ op := code.Opcode(c.currentInstructions()[opPos])
|
|
|
newInstruction := code.Make(op, operand)
|
|
|
|
|
|
c.replaceInstruction(opPos, newInstruction)
|
|
|
}
|
|
|
|
|
|
+func (c *Compiler) currentInstructions() code.Instructions {
|
|
|
+ return c.scopes[c.scopeIndex].instructions
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Compiler) enterScope() {
|
|
|
+ scope := CompilationScope{
|
|
|
+ instructions: code.Instructions{},
|
|
|
+ lastInstruction: EmittedInstruction{},
|
|
|
+ previousInstruction: EmittedInstruction{},
|
|
|
+ }
|
|
|
+ c.scopes = append(c.scopes, scope)
|
|
|
+ c.scopeIndex++
|
|
|
+}
|
|
|
+
|
|
|
+func (c *Compiler) leaveScope() code.Instructions {
|
|
|
+ ins := c.currentInstructions()
|
|
|
+
|
|
|
+ c.scopes = c.scopes[:len(c.scopes)-1]
|
|
|
+ c.scopeIndex--
|
|
|
+
|
|
|
+ return ins
|
|
|
+}
|
|
|
+
|
|
|
func (c *Compiler) ByteCode() *ByteCode {
|
|
|
return &ByteCode{
|
|
|
- Instructions: c.instructions,
|
|
|
+ Instructions: c.currentInstructions(),
|
|
|
Constants: c.constants,
|
|
|
}
|
|
|
}
|