// 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 var True = &object.Boolean{Value: true} var False = &object.Boolean{Value: false} var Null = &object.Null{} 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 } case code.OpTrue: err := vm.push(True) if err != nil { return err } case code.OpFalse: err := vm.push(False) if err != nil { return err } case code.OpAdd, code.OpSub, code.OpMul, code.OpDiv: err := vm.executeBinaryOperation(op) if err != nil { return err } case code.OpEqual, code.OpNotEqual, code.OpGreaterThan: err := vm.executeComparison(op) if err != nil { return err } case code.OpMinus: err := vm.executeMinusOperator() if err != nil { return err } case code.OpBang: err := vm.executeBangOperator() if err != nil { return err } case code.OpPop: vm.pop() case code.OpJump: pos := int(code.ReadUint16(vm.instructions[ip+1:])) ip = pos - 1 case code.OpJumpNotTruthy: pos := int(code.ReadUint16(vm.instructions[ip+1:])) ip += 2 condition := vm.pop() if !isTruthy(condition) { ip = pos - 1 } case code.OpNull: err := vm.push(Null) if err != nil { return err } } } return nil } func isTruthy(obj object.Object) bool { switch obj := obj.(type) { case *object.Boolean: return obj.Value default: return true } } 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 } func (vm *VM) pop() object.Object { o := vm.stack[vm.sp-1] vm.sp-- return o } func (vm *VM) executeBinaryOperation(op code.Opcode) error { right := vm.pop() left := vm.pop() leftType := left.Type() rightType := right.Type() if leftType == object.IntegerObj && rightType == object.IntegerObj { return vm.executeBinaryIntegerOperation(op, left, right) } return fmt.Errorf("unsupported types of binary operation: %s %s", leftType, rightType) } func (vm *VM) executeComparison(op code.Opcode) error { right := vm.pop() left := vm.pop() if left.Type() == object.IntegerObj || right.Type() == object.IntegerObj { return vm.executeIntegerComparison(op, left, right) } switch op { case code.OpEqual: return vm.push(nativeBoolToBooleanObject(right == left)) case code.OpNotEqual: return vm.push(nativeBoolToBooleanObject(right != left)) default: return fmt.Errorf("unknown operator: %d (%s %s)", op, left.Type(), right.Type()) } } func (vm *VM) LastPopStackElem() object.Object { return vm.stack[vm.sp] } func (vm *VM) executeBinaryIntegerOperation( op code.Opcode, left, right object.Object, ) error { leftValue := left.(*object.Integer).Value rightValue := right.(*object.Integer).Value var result int64 switch op { case code.OpAdd: result = leftValue + rightValue case code.OpSub: result = leftValue - rightValue case code.OpMul: result = leftValue * rightValue case code.OpDiv: result = leftValue / rightValue default: return fmt.Errorf("unknown integer operator: %d", op) } return vm.push(&object.Integer{Value: result}) } func (vm *VM) executeIntegerComparison( op code.Opcode, left, right object.Object, ) error { leftValue := left.(*object.Integer).Value rightValue := right.(*object.Integer).Value switch op { case code.OpEqual: return vm.push(nativeBoolToBooleanObject(rightValue == leftValue)) case code.OpNotEqual: return vm.push(nativeBoolToBooleanObject(rightValue != leftValue)) case code.OpGreaterThan: return vm.push(nativeBoolToBooleanObject(leftValue > rightValue)) default: return fmt.Errorf("unknown operator: %d", op) } } func (vm *VM) executeMinusOperator() error { operand := vm.pop() if operand.Type() != object.IntegerObj { return fmt.Errorf("unsupported type for nagation: %s", operand.Type()) } value := operand.(*object.Integer).Value return vm.push(&object.Integer{Value: -value}) } func (vm *VM) executeBangOperator() error { operand := vm.pop() switch operand { case True: return vm.push(False) case False: return vm.push(True) default: return vm.push(False) } } func nativeBoolToBooleanObject(input bool) *object.Boolean { if input { return True } return False }