package vm import ( "jinx/pkg/lang/vm/code" "jinx/pkg/lang/vm/mem" "jinx/pkg/lang/vm/value" ) type VM struct { code *code.Code pc int stack Stack memory mem.Mem } func New(code *code.Code) *VM { return &VM{ code: code, pc: 0, stack: NewStack(), memory: mem.New(), } } func (vm *VM) GetResult() (string, error) { res, err := vm.stack.Pop() if err != nil { return "", err } str := res.Data().String(&vm.memory) return str, nil } func (vm *VM) Run() error { for vm.pc < vm.code.Len() { op, advance := vm.code.GetOp(vm.pc) vm.pc += advance if decision, err := vm.step(op); err != nil { return Error{ Pc: vm.pc, Err: err, } } else if decision == stepDecisionHalt { return nil } } return nil } type stepDecision int const ( stepDecisionContinue stepDecision = iota stepDecisionHalt ) func (vm *VM) step(op code.Op) (stepDecision, error) { var err error switch op { case code.OpNop: // do nothing case code.OpHalt: return stepDecisionHalt, nil case code.OpPushInt: x, advance := vm.code.GetInt(vm.pc) vm.pc += advance vm.execPushInt(x) case code.OpPushFloat: x, advance := vm.code.GetFloat(vm.pc) vm.pc += advance vm.execPushFloat(x) case code.OpPushString: str, advance := vm.code.GetString(vm.pc) vm.pc += advance vm.execPushString(str) case code.OpPushNull: vm.execPushNull() case code.OpPushTrue: vm.execPushBool(true) case code.OpPushFalse: vm.execPushBool(false) case code.OpPushArray: vm.execPushArray() case code.OpPushFunction: x, advance := vm.code.GetUint(vm.pc) vm.pc += advance vm.execPushFunction(int(x)) case code.OpPushObject: panic("not implemented") case code.OpShift: by, advance := vm.code.GetInt(vm.pc) vm.pc += advance err = vm.stack.ShiftTopCallBase(int(by)) case code.OpDrop: _, err = vm.stack.Pop() case code.OpGetGlobal: panic("not implemented") case code.OpGetLocal: offset, advance := vm.code.GetInt(vm.pc) vm.pc += advance err = vm.execGetLocal(int(offset)) case code.OpGetMember: panic("not implemented") case code.OpGetMethod: panic("not implemented") case code.OpGetEnv: panic("not implemented") case code.OpAdd: err = vm.execAdd() case code.OpSub: err = vm.execSub() case code.OpIndex: err = vm.execIndex() case code.OpLte: err = vm.execLte() case code.OpCall: err = vm.execCall() case code.OpJmp: pc, _ := vm.code.GetUint(vm.pc) vm.pc = int(pc) case code.OpJt: pc, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execJumpIf(int(pc), true) case code.OpJf: pc, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execJumpIf(int(pc), false) case code.OpRet: err = vm.execRet() case code.OpTempArrLen: err = vm.execTempArrLen() case code.OpTempArrPush: err = vm.execTempArrPush() default: err = ErrInvalidOp{Op: uint8(op)} } return stepDecisionContinue, err } func (vm *VM) popAndDrop() (value.Value, error) { v, err := vm.stack.Pop() if err != nil { return value.Value{}, err } v.Drop(&vm.memory) return v, nil } func (vm *VM) popCallAndDrop() (int, error) { for !vm.stack.ReachedBaseOfCall() { _, err := vm.popAndDrop() if err != nil { return 0, err } } return vm.stack.PopCall() }