functions with bindings
Some checks failed
Build / build (push) Failing after 1m42s
Test / build (push) Successful in 2m27s

This commit is contained in:
Chuck Smith
2024-03-08 14:19:20 -05:00
parent 9d06c90e41
commit ec9a586f7f
10 changed files with 350 additions and 30 deletions

20
vm/frame.go Normal file
View File

@@ -0,0 +1,20 @@
package vm
import (
"monkey/code"
"monkey/object"
)
type Frame struct {
fn *object.CompiledFunction
ip int
basePointer int
}
func NewFrame(fn *object.CompiledFunction, basePointer int) *Frame {
return &Frame{fn: fn, ip: -1, basePointer: basePointer}
}
func (f *Frame) Instructions() code.Instructions {
return f.fn.Instructions
}

View File

@@ -15,19 +15,6 @@ var Null = &object.Null{}
var True = &object.Boolean{Value: true}
var False = &object.Boolean{Value: false}
type Frame struct {
fn *object.CompiledFunction
ip int
}
func NewFrame(fn *object.CompiledFunction) *Frame {
return &Frame{fn: fn, ip: -1}
}
func (f *Frame) Instructions() code.Instructions {
return f.fn.Instructions
}
type VM struct {
constants []object.Object
@@ -42,7 +29,7 @@ type VM struct {
func New(bytecode *compiler.Bytecode) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainFrame := NewFrame(mainFn)
mainFrame := NewFrame(mainFn, 0)
frames := make([]*Frame, MaxFrames)
frames[0] = mainFrame
@@ -220,13 +207,15 @@ func (vm *VM) Run() error {
if !ok {
return fmt.Errorf("calling non-function")
}
frame := NewFrame(fn)
frame := NewFrame(fn, vm.sp)
vm.pushFrame(frame)
vm.sp = frame.basePointer + fn.NumLocals
case code.OpReturnValue:
returnValue := vm.pop()
vm.popFrame()
vm.pop()
frame := vm.popFrame()
vm.sp = frame.basePointer - 1
err := vm.push(returnValue)
if err != nil {
@@ -234,14 +223,32 @@ func (vm *VM) Run() error {
}
case code.OpReturn:
vm.popFrame()
vm.pop()
frame := vm.popFrame()
vm.sp = frame.basePointer - 1
err := vm.push(Null)
if err != nil {
return err
}
case code.OpSetLocal:
localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
frame := vm.currentFrame()
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
case code.OpGetLocal:
localIndex := code.ReadUint8(ins[ip+1:])
vm.currentFrame().ip += 1
frame := vm.currentFrame()
err := vm.push(vm.stack[frame.basePointer+int(localIndex)])
if err != nil {
return err
}
}
}

View File

@@ -379,6 +379,16 @@ func TestFirstClassFunctions(t *testing.T) {
let returnsOne = fn() { 1; };
let returnsOneReturner = fn() { returnsOne; };
returnsOneReturner()();
`,
expected: 1,
},
{
input: `
let returnsOneReturner = fn() {
let returnsOne = fn() { 1; };
returnsOne;
};
returnsOneReturner()();
`,
expected: 1,
},
@@ -386,3 +396,55 @@ func TestFirstClassFunctions(t *testing.T) {
runVmTests(t, tests)
}
func TestCallingFunctionsWithBindings(t *testing.T) {
tests := []vmTestCase{
{
input: `
let one = fn() { let one = 1; one };
one();
`,
expected: 1,
},
{
input: `
let oneAndTwo = fn() { let one = 1; let two = 2; one + two; };
oneAndTwo();
`,
expected: 3,
},
{
input: `
let oneAndTwo = fn() { let one = 1; let two = 2; one + two; };
let threeAndFour = fn() { let three = 3; let four = 4; three + four; };
oneAndTwo() + threeAndFour();
`,
expected: 10,
},
{
input: `
let firstFoobar = fn() { let foobar = 50; foobar; };
let secondFoobar = fn() { let foobar = 100; foobar; };
firstFoobar() + secondFoobar();
`,
expected: 150,
},
{
input: `
let globalSeed = 50;
let minusOne = fn() {
let num = 1;
globalSeed - num;
}
let minusTwo = fn() {
let num = 2;
globalSeed - num;
}
minusOne() + minusTwo();
`,
expected: 97,
},
}
runVmTests(t, tests)
}