run functions
Some checks failed
Build / build (push) Failing after 1m36s
Test / build (push) Successful in 1m37s

This commit is contained in:
Chuck Smith
2024-03-04 16:11:25 -05:00
parent e56fb40f83
commit 9d06c90e41
2 changed files with 184 additions and 21 deletions

111
vm/vm.go
View File

@@ -9,30 +9,54 @@ import (
const StackSize = 2048 const StackSize = 2048
const GlobalsSize = 65536 const GlobalsSize = 65536
const MaxFrames = 1024
var Null = &object.Null{} var Null = &object.Null{}
var True = &object.Boolean{Value: true} var True = &object.Boolean{Value: true}
var False = &object.Boolean{Value: false} 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 { type VM struct {
constants []object.Object constants []object.Object
instructions code.Instructions
stack []object.Object stack []object.Object
sp int // Always points to the next value. Top of stack is stack[sp-1] sp int // Always points to the next value. Top of stack is stack[sp-1]
globals []object.Object globals []object.Object
frames []*Frame
framesIndex int
} }
func New(bytecode *compiler.Bytecode) *VM { func New(bytecode *compiler.Bytecode) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainFrame := NewFrame(mainFn)
frames := make([]*Frame, MaxFrames)
frames[0] = mainFrame
return &VM{ return &VM{
constants: bytecode.Constants, constants: bytecode.Constants,
instructions: bytecode.Instructions,
stack: make([]object.Object, StackSize), stack: make([]object.Object, StackSize),
sp: 0, sp: 0,
globals: make([]object.Object, GlobalsSize), globals: make([]object.Object, GlobalsSize),
frames: frames,
framesIndex: 1,
} }
} }
@@ -42,18 +66,40 @@ func NewWithGlobalState(bytecode *compiler.Bytecode, s []object.Object) *VM {
return vm return vm
} }
func (vm *VM) currentFrame() *Frame {
return vm.frames[vm.framesIndex-1]
}
func (vm *VM) pushFrame(f *Frame) {
vm.frames[vm.framesIndex] = f
vm.framesIndex++
}
func (vm *VM) popFrame() *Frame {
vm.framesIndex--
return vm.frames[vm.framesIndex]
}
func (vm *VM) LastPoppedStackElem() object.Object { func (vm *VM) LastPoppedStackElem() object.Object {
return vm.stack[vm.sp] return vm.stack[vm.sp]
} }
func (vm *VM) Run() error { func (vm *VM) Run() error {
for ip := 0; ip < len(vm.instructions); ip++ { var ip int
op := code.Opcode(vm.instructions[ip]) var ins code.Instructions
var op code.Opcode
for vm.currentFrame().ip < len(vm.currentFrame().Instructions())-1 {
vm.currentFrame().ip++
ip = vm.currentFrame().ip
ins = vm.currentFrame().Instructions()
op = code.Opcode(ins[ip])
switch op { switch op {
case code.OpConstant: case code.OpConstant:
constIndex := code.ReadUint16(vm.instructions[ip+1:]) constIndex := code.ReadUint16(ins[ip+1:])
ip += 2 vm.currentFrame().ip += 2
err := vm.push(vm.constants[constIndex]) err := vm.push(vm.constants[constIndex])
if err != nil { if err != nil {
@@ -100,16 +146,16 @@ func (vm *VM) Run() error {
} }
case code.OpJump: case code.OpJump:
pos := int(code.ReadUint16(vm.instructions[ip+1:])) pos := int(code.ReadUint16(ins[ip+1:]))
ip = pos - 1 vm.currentFrame().ip = pos - 1
case code.OpJumpNotTruthy: case code.OpJumpNotTruthy:
pos := int(code.ReadUint16(vm.instructions[ip+1:])) pos := int(code.ReadUint16(ins[ip+1:]))
ip += 2 vm.currentFrame().ip += 2
condition := vm.pop() condition := vm.pop()
if !isTruthy(condition) { if !isTruthy(condition) {
ip = pos - 1 vm.currentFrame().ip = pos - 1
} }
case code.OpNull: case code.OpNull:
@@ -119,14 +165,14 @@ func (vm *VM) Run() error {
} }
case code.OpSetGlobal: case code.OpSetGlobal:
globalIndex := code.ReadUint16(vm.instructions[ip+1:]) globalIndex := code.ReadUint16(ins[ip+1:])
ip += 2 vm.currentFrame().ip += 2
vm.globals[globalIndex] = vm.pop() vm.globals[globalIndex] = vm.pop()
case code.OpGetGlobal: case code.OpGetGlobal:
globalIndex := code.ReadUint16(vm.instructions[ip+1:]) globalIndex := code.ReadUint16(ins[ip+1:])
ip += 2 vm.currentFrame().ip += 2
err := vm.push(vm.globals[globalIndex]) err := vm.push(vm.globals[globalIndex])
if err != nil { if err != nil {
@@ -134,8 +180,8 @@ func (vm *VM) Run() error {
} }
case code.OpArray: case code.OpArray:
numElements := int(code.ReadUint16(vm.instructions[ip+1:])) numElements := int(code.ReadUint16(ins[ip+1:]))
ip += 2 vm.currentFrame().ip += 2
array := vm.buildArray(vm.sp-numElements, vm.sp) array := vm.buildArray(vm.sp-numElements, vm.sp)
vm.sp = vm.sp - numElements vm.sp = vm.sp - numElements
@@ -146,8 +192,8 @@ func (vm *VM) Run() error {
} }
case code.OpHash: case code.OpHash:
numElements := int(code.ReadUint16(vm.instructions[ip+1:])) numElements := int(code.ReadUint16(ins[ip+1:]))
ip += 2 vm.currentFrame().ip += 2
hash, err := vm.buildHash(vm.sp-numElements, vm.sp) hash, err := vm.buildHash(vm.sp-numElements, vm.sp)
if err != nil { if err != nil {
@@ -169,6 +215,33 @@ func (vm *VM) Run() error {
return err return err
} }
case code.OpCall:
fn, ok := vm.stack[vm.sp-1].(*object.CompiledFunction)
if !ok {
return fmt.Errorf("calling non-function")
}
frame := NewFrame(fn)
vm.pushFrame(frame)
case code.OpReturnValue:
returnValue := vm.pop()
vm.popFrame()
vm.pop()
err := vm.push(returnValue)
if err != nil {
return err
}
case code.OpReturn:
vm.popFrame()
vm.pop()
err := vm.push(Null)
if err != nil {
return err
}
} }
} }

View File

@@ -296,3 +296,93 @@ func TestIndexExpressions(t *testing.T) {
runVmTests(t, tests) runVmTests(t, tests)
} }
func TestCallingFunctionsWithoutArguments(t *testing.T) {
tests := []vmTestCase{
{
input: `
let fivePlusTen = fn() { 5 + 10; };
fivePlusTen();
`,
expected: 15,
},
{
input: `
let one = fn() { 1; };
let two = fn() { 2; };
one() + two()
`,
expected: 3,
},
{
input: `
let a = fn() { 1 };
let b = fn() { a() + 1 };
let c = fn() { b() + 1 };
c();
`,
expected: 3,
},
}
runVmTests(t, tests)
}
func TestFunctionsWithReturnStatements(t *testing.T) {
tests := []vmTestCase{
{
input: `
let earlyExit = fn() { return 99; 100; };
earlyExit();
`,
expected: 99,
},
{
input: `
let earlyExit = fn() { return 99; return 100; };
earlyExit();
`,
expected: 99,
},
}
runVmTests(t, tests)
}
func TestFunctionsWithoutReturnValue(t *testing.T) {
tests := []vmTestCase{
{
input: `
let noReturn = fn() { };
noReturn();
`,
expected: Null,
},
{
input: `
let noReturn = fn() { };
let noReturnTwo = fn() { noReturn(); };
noReturn();
noReturnTwo();
`,
expected: Null,
},
}
runVmTests(t, tests)
}
func TestFirstClassFunctions(t *testing.T) {
tests := []vmTestCase{
{
input: `
let returnsOne = fn() { 1; };
let returnsOneReturner = fn() { returnsOne; };
returnsOneReturner()();
`,
expected: 1,
},
}
runVmTests(t, tests)
}