package vm import ( "fmt" "monkey/code" "monkey/compiler" "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{ constants: bytecode.Constants, instructions: bytecode.Instructions, stack: make([]object.Object, StackSize), sp: 0, } } func (vm *VM) LastPoppedStackElem() object.Object { return vm.stack[vm.sp] } 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.OpAdd, code.OpSub, code.OpMul, code.OpDiv: err := vm.executeBinaryOperation(op) if err != nil { return err } case code.OpPop: vm.pop() } } 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 } 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() rightRight := right.Type() if leftType == object.INTEGER_OBJ && rightRight == object.INTEGER_OBJ { return vm.executeBinaryIntegerOperation(op, left, right) } return fmt.Errorf("unsupported types for binary operation: %s %s", leftType, rightRight) } 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}) }