compile functions
Some checks failed
Build / build (push) Failing after 1m57s
Test / build (push) Successful in 2m15s

This commit is contained in:
Chuck Smith
2024-02-28 16:57:01 -05:00
parent 4185926e3e
commit e56fb40f83
5 changed files with 320 additions and 34 deletions

View File

@@ -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