about summary refs log tree commit diff
path: root/pkg/lang
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-05-20 21:04:37 +0200
committerMel <einebeere@gmail.com>2022-05-20 21:04:37 +0200
commit3f4efe745a0404953266476ec52db54b182de2f8 (patch)
tree107542d587c97b99f749c537870e84d196058210 /pkg/lang
parent25eb5ca1b0a8b9b35f36deedec4901bca02bf43e (diff)
downloadjinx-3f4efe745a0404953266476ec52db54b182de2f8.tar.zst
jinx-3f4efe745a0404953266476ec52db54b182de2f8.zip
Call and return from functions
Diffstat (limited to 'pkg/lang')
-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)