diff options
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/lang/vm/exec.go | 44 | ||||
| -rw-r--r-- | pkg/lang/vm/stack.go | 45 | ||||
| -rw-r--r-- | pkg/lang/vm/value/data.go | 12 | ||||
| -rw-r--r-- | pkg/lang/vm/value/value.go | 5 | ||||
| -rw-r--r-- | pkg/lang/vm/vm.go | 9 | ||||
| -rw-r--r-- | pkg/lang/vm/vm_test.go | 18 |
6 files changed, 119 insertions, 14 deletions
diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go index 4f37ad3..655f7f1 100644 --- a/pkg/lang/vm/exec.go +++ b/pkg/lang/vm/exec.go @@ -29,6 +29,10 @@ func (vm *VM) execPushArray() { vm.stack.Push(value.NewArray([]value.Value{})) } +func (vm *VM) execPushFunction(pc int) { + vm.stack.Push(value.NewFunction(pc)) +} + func (vm *VM) execGetLocal(offset int) error { local, err := vm.stack.Local(int(offset)) if err != nil { @@ -262,6 +266,30 @@ func (vm *VM) execLte() error { return nil } +func (vm *VM) execCall() error { + f, err := vm.stack.Pop() + if err != nil { + return err + } + + if f.Type().Kind != value.FunctionType { + return ErrInvalidOperandType{ + Op: code.OpCall, + X: f.Type(), + } + } + + fn := f.Data().(value.FunctionData) + + if err = vm.stack.PushCall(fn.Pc(), vm.pc); err != nil { + return err + } + + vm.pc = fn.Pc() + + return nil +} + func (vm *VM) execJumpIf(pc int, cond bool) error { b, err := vm.stack.Pop() if err != nil { @@ -291,6 +319,22 @@ func (vm *VM) execJumpIf(pc int, cond bool) error { return nil } +func (vm *VM) execRet() error { + returned, err := vm.stack.Pop() + if err != nil { + return err + } + + pc, err := vm.stack.PopCall() + if err != nil { + return err + } + + vm.stack.Push(returned) + vm.pc = pc + return nil +} + func (vm *VM) execTempArrLen() error { a, err := vm.stack.Pop() if err != nil { diff --git a/pkg/lang/vm/stack.go b/pkg/lang/vm/stack.go index 937be32..51e43c8 100644 --- a/pkg/lang/vm/stack.go +++ b/pkg/lang/vm/stack.go @@ -64,6 +64,39 @@ func (stack *Stack) Top() (value.Value, error) { return stack.data[stack.Len()-1], nil } +func (stack *Stack) Len() int { + return len(stack.data) +} + +func (stack *Stack) IsEmpty() bool { + return len(stack.data) == 0 +} + +func (stack *Stack) PushCall(newPc, returnPc int) error { + if stack.CallDepth() == 1000 { + return ErrReachedMaxCallDepth + } + + stack.calls = append(stack.calls, callFrame{ + pc: newPc, + returnPc: returnPc, + base: stack.Len(), + }) + + return nil +} + +func (stack *Stack) PopCall() (int, error) { + if stack.CallDepth() == 0 { + return 0, ErrReachedRootCallFrame + } + + call := stack.TopCall() + stack.calls = stack.calls[:stack.CallDepth()-1] + + return call.returnPc, nil +} + func (stack *Stack) ShiftTopCallBase(by int) error { call := stack.TopCall() newBase := call.base - by @@ -76,18 +109,14 @@ func (stack *Stack) ShiftTopCallBase(by int) error { return nil } -func (stack *Stack) Len() int { - return len(stack.data) -} - -func (stack *Stack) IsEmpty() bool { - return len(stack.data) == 0 -} - func (stack *Stack) TopCall() *callFrame { return &stack.calls[len(stack.calls)-1] } +func (stack *Stack) CallDepth() int { + return len(stack.calls) +} + func (stack *Stack) ReachedBaseOfCall() bool { return stack.TopCall().base == stack.Len() } diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go index 6365f45..5d1398d 100644 --- a/pkg/lang/vm/value/data.go +++ b/pkg/lang/vm/value/data.go @@ -83,6 +83,16 @@ func (a ArrayData) Push(v Value) { type NullData struct{} -type FunctionData struct{} // TODO +type FunctionData struct { + pc int +} + +func (f FunctionData) Pc() int { + return f.pc +} + +func (f FunctionData) String() string { + return fmt.Sprintf("<fn %d>", f.pc) +} type ObjectData struct{} // TODO diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go index daa17dd..93989cf 100644 --- a/pkg/lang/vm/value/value.go +++ b/pkg/lang/vm/value/value.go @@ -35,8 +35,9 @@ func NewNull() Value { return Value{t: t} } -func NewFunction() Value { - panic("not implemented") +func NewFunction(pc int) Value { + t := Type{Kind: FunctionType} + return Value{t: t, d: FunctionData{pc: pc}} } func NewObject() Value { diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go index a428bfd..dfe1ae2 100644 --- a/pkg/lang/vm/vm.go +++ b/pkg/lang/vm/vm.go @@ -85,7 +85,10 @@ func (vm *VM) step(op code.Op) (stepDecision, error) { case code.OpPushArray: vm.execPushArray() case code.OpPushFunction: - panic("not implemented") + x, advance := vm.code.GetUint(vm.pc) + vm.pc += advance + + vm.execPushFunction(int(x)) case code.OpPushObject: panic("not implemented") @@ -120,7 +123,7 @@ func (vm *VM) step(op code.Op) (stepDecision, error) { case code.OpLte: err = vm.execLte() case code.OpCall: - panic("not implemented") + err = vm.execCall() case code.OpJmp: pc, _ := vm.code.GetUint(vm.pc) @@ -134,7 +137,7 @@ func (vm *VM) step(op code.Op) (stepDecision, error) { vm.pc += advance err = vm.execJumpIf(int(pc), false) case code.OpRet: - panic("not implemented") + err = vm.execRet() case code.OpTempArrLen: err = vm.execTempArrLen() diff --git a/pkg/lang/vm/vm_test.go b/pkg/lang/vm/vm_test.go index f87182f..298cfb2 100644 --- a/pkg/lang/vm/vm_test.go +++ b/pkg/lang/vm/vm_test.go @@ -90,6 +90,24 @@ func TestFibonacci(t *testing.T) { test(t, src, "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]") } +func TestFunction(t *testing.T) { + src := ` + push_int 44 + push_function @subtract_two + call + halt + + @subtract_two: + shift 1 + push_int 2 + get_local 0 + sub + ret + ` + + test(t, src, "42") +} + func test(t *testing.T, src string, expected string) { bc := compile(t, src) |
