compile functions
This commit is contained in:
@@ -13,24 +13,37 @@ type EmittedInstruction struct {
|
||||
Position int
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
instructions code.Instructions
|
||||
constants []object.Object
|
||||
|
||||
type CompilationScope struct {
|
||||
instructions code.Instructions
|
||||
lastInstruction EmittedInstruction
|
||||
previousInstruction EmittedInstruction
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
constants []object.Object
|
||||
|
||||
symbolTable *SymbolTable
|
||||
|
||||
scopes []CompilationScope
|
||||
scopeIndex int
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) currentInstructions() code.Instructions {
|
||||
return c.scopes[c.scopeIndex].instructions
|
||||
}
|
||||
|
||||
func NewWithState(s *SymbolTable, constants []object.Object) *Compiler {
|
||||
@@ -141,14 +154,14 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.lastInstructionIsPop() {
|
||||
if c.lastInstructionIs(code.OpPop) {
|
||||
c.removeLastPop()
|
||||
}
|
||||
|
||||
// Emit an `OnJump` with a bogus value
|
||||
jumpPos := c.emit(code.OpJump, 9999)
|
||||
|
||||
afterConsequencePos := len(c.instructions)
|
||||
afterConsequencePos := len(c.currentInstructions())
|
||||
c.changeOperand(jumpNotTruthyPos, afterConsequencePos)
|
||||
|
||||
if node.Alternative == nil {
|
||||
@@ -159,12 +172,12 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.lastInstructionIsPop() {
|
||||
if c.lastInstructionIs(code.OpPop) {
|
||||
c.removeLastPop()
|
||||
}
|
||||
}
|
||||
|
||||
afterAlternativePos := len(c.instructions)
|
||||
afterAlternativePos := len(c.currentInstructions())
|
||||
c.changeOperand(jumpPos, afterAlternativePos)
|
||||
|
||||
case *ast.BlockStatement:
|
||||
@@ -240,6 +253,42 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
|
||||
c.emit(code.OpIndex)
|
||||
|
||||
case *ast.FunctionLiteral:
|
||||
c.enterScope()
|
||||
|
||||
err := c.Compile(node.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.lastInstructionIs(code.OpPop) {
|
||||
c.replaceLastPopWithReturn()
|
||||
}
|
||||
if !c.lastInstructionIs(code.OpReturnValue) {
|
||||
c.emit(code.OpReturn)
|
||||
}
|
||||
|
||||
instructions := c.leaveScope()
|
||||
|
||||
compiledFn := &object.CompiledFunction{Instructions: instructions}
|
||||
c.emit(code.OpConstant, c.addConstant(compiledFn))
|
||||
|
||||
case *ast.ReturnStatement:
|
||||
err := c.Compile(node.ReturnValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(code.OpReturnValue)
|
||||
|
||||
case *ast.CallExpression:
|
||||
err := c.Compile(node.Function)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(code.OpCall)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -260,48 +309,89 @@ 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) Bytecode() *Bytecode {
|
||||
return &Bytecode{
|
||||
Instructions: c.instructions,
|
||||
Instructions: c.currentInstructions(),
|
||||
Constants: c.constants,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) addInstruction(ins []byte) int {
|
||||
postNewInstruction := len(c.instructions)
|
||||
c.instructions = append(c.instructions, ins...)
|
||||
postNewInstruction := len(c.currentInstructions())
|
||||
updatedInstructions := append(c.currentInstructions(), ins...)
|
||||
|
||||
c.scopes[c.scopeIndex].instructions = updatedInstructions
|
||||
|
||||
return postNewInstruction
|
||||
}
|
||||
|
||||
func (c *Compiler) lastInstructionIsPop() bool {
|
||||
return c.lastInstruction.Opcode == code.OpPop
|
||||
func (c *Compiler) lastInstructionIs(op code.Opcode) bool {
|
||||
if len(c.currentInstructions()) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return c.scopes[c.scopeIndex].lastInstruction.Opcode == op
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
old := c.currentInstructions()
|
||||
new := old[:last.Position]
|
||||
|
||||
c.scopes[c.scopeIndex].instructions = new
|
||||
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) changeOperand(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) enterScope() {
|
||||
scope := CompilationScope{
|
||||
instructions: code.Instructions{},
|
||||
lastInstruction: EmittedInstruction{},
|
||||
previousInstruction: EmittedInstruction{},
|
||||
}
|
||||
c.scopes = append(c.scopes, scope)
|
||||
c.scopeIndex++
|
||||
}
|
||||
|
||||
func (c *Compiler) leaveScope() code.Instructions {
|
||||
instructions := c.currentInstructions()
|
||||
|
||||
c.scopes = c.scopes[:len(c.scopes)-1]
|
||||
c.scopeIndex--
|
||||
|
||||
return instructions
|
||||
}
|
||||
|
||||
func (c *Compiler) replaceLastPopWithReturn() {
|
||||
lastPos := c.scopes[c.scopeIndex].lastInstruction.Position
|
||||
c.replaceInstruction(lastPos, code.Make(code.OpReturnValue))
|
||||
|
||||
c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue
|
||||
}
|
||||
|
||||
type Bytecode struct {
|
||||
Instructions code.Instructions
|
||||
Constants []object.Object
|
||||
|
||||
Reference in New Issue
Block a user