functions with bindings
This commit is contained in:
20
vm/frame.go
Normal file
20
vm/frame.go
Normal 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
|
||||
}
|
||||
45
vm/vm.go
45
vm/vm.go
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user