Procházet zdrojové kódy

Stack VM implement of base

runningwater před 3 roky
rodič
revize
67cb811cae
2 změnil soubory, kde provedl 156 přidání a 0 odebrání
  1. 71 0
      vm/vm.go
  2. 85 0
      vm/vm_test.go

+ 71 - 0
vm/vm.go

@@ -0,0 +1,71 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2022/10/22 13:55
+// Desc: Stack VM implement
+
+package vm
+
+import (
+	"fmt"
+	"github/runnignwater/monkey/code"
+	"github/runnignwater/monkey/compiler"
+	"github/runnignwater/monkey/object"
+)
+
+const StackSize = 2048
+
+type VM struct {
+	constants    []object.Object
+	instructions code.Instructions
+
+	stack []object.Object
+	sp    int // Always points to the next value. Top of stack is stack[sp-1]
+}
+
+func New(byteCode *compiler.ByteCode) *VM {
+	return &VM{
+		instructions: byteCode.Instructions,
+		constants:    byteCode.Constants,
+
+		stack: make([]object.Object, StackSize),
+		sp:    0,
+	}
+}
+
+func (vm *VM) StackTop() object.Object {
+	if vm.sp == 0 {
+		return nil
+	}
+	return vm.stack[vm.sp-1]
+}
+
+func (vm *VM) Run() error {
+	for ip := 0; ip < len(vm.instructions); ip++ {
+		op := code.Opcode(vm.instructions[ip])
+
+		switch op {
+		case code.OpConstant:
+			constIndex := code.ReadUint16(vm.instructions[ip+1:])
+			ip += 2
+
+			// 执行
+			err := vm.push(vm.constants[constIndex])
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+func (vm *VM) push(o object.Object) error {
+	if vm.sp >= StackSize {
+		return fmt.Errorf("stack overflow")
+	}
+
+	vm.stack[vm.sp] = o
+	vm.sp++
+
+	return nil
+}

+ 85 - 0
vm/vm_test.go

@@ -0,0 +1,85 @@
+package vm
+
+import (
+	"fmt"
+	"github/runnignwater/monkey/ast"
+	"github/runnignwater/monkey/compiler"
+	"github/runnignwater/monkey/lexer"
+	"github/runnignwater/monkey/object"
+	"github/runnignwater/monkey/parser"
+	"testing"
+)
+
+type vmTestCase struct {
+	input    string
+	expected interface{}
+}
+
+func TestIntegerArithmetic(t *testing.T) {
+	tests := []vmTestCase{
+		{"1", 1},
+		{"2", 2},
+		{"1 + 2", 2},
+	}
+
+	runVmTests(t, tests)
+}
+
+func runVmTests(t *testing.T, tests []vmTestCase) {
+	t.Helper()
+
+	for _, tt := range tests {
+		program := parse(tt.input)
+
+		comp := compiler.New()
+		err := comp.Compile(program)
+		if err != nil {
+			t.Fatalf("compler error: %s", err)
+		}
+
+		vm := New(comp.ByteCode())
+		err = vm.Run()
+		if err != nil {
+			t.Fatalf("vm error: %s", err)
+		}
+
+		stackElem := vm.StackTop()
+
+		testExpectedObject(t, tt.expected, stackElem)
+	}
+}
+
+func parse(input string) *ast.Program {
+	l := lexer.New(input)
+	p := parser.New(l)
+	return p.ParseProgram()
+}
+
+func testExpectedObject(
+	t *testing.T,
+	expected interface{},
+	actual object.Object,
+) {
+	t.Helper()
+
+	switch expected := expected.(type) {
+	case int:
+		err := testIntegerObject(int64(expected), actual)
+		if err != nil {
+			t.Errorf("testIntegerObject failed: %s", err)
+		}
+	}
+}
+
+func testIntegerObject(expected int64, actual object.Object) error {
+	result, ok := actual.(*object.Integer)
+	if !ok {
+		return fmt.Errorf("object is not Integer. got=%T (%+v)", actual, actual)
+	}
+
+	if result.Value != expected {
+		return fmt.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected)
+	}
+
+	return nil
+}