aboutsummaryrefslogtreecommitdiff
path: root/pkg/lang/vm
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/vm')
-rw-r--r--pkg/lang/vm/exec.go44
-rw-r--r--pkg/lang/vm/stack.go45
-rw-r--r--pkg/lang/vm/value/data.go12
-rw-r--r--pkg/lang/vm/value/value.go5
-rw-r--r--pkg/lang/vm/vm.go9
-rw-r--r--pkg/lang/vm/vm_test.go18
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)