package vm import ( "jinx/pkg/lang/vm/code" "jinx/pkg/lang/vm/mem" "jinx/pkg/lang/vm/stack" ) type VM struct { code *code.Code pc int stack stack.Stack memory mem.Mem } func New(code *code.Code) *VM { vm := &VM{ code: code, pc: 0, stack: stack.New(), memory: mem.New(), } if err := vm.setup(); err != nil { panic(err) } return vm } func (vm *VM) GetResult() (string, error) { res, err := vm.stack.Pop() if err != nil { return "", err } if str, err := res.Data().String(vm.memory); err == nil { return str, nil } else { return "", err } } 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, Line: vm.code.Debug().PCToLine(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.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: name, advance := vm.code.GetString(vm.pc) vm.pc += advance err = vm.execGetMember(name) case code.OpGetEnv: envIndex, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execGetEnv(int(envIndex)) case code.OpSetEnv: envIndex, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execSetEnv(int(envIndex)) case code.OpAddToEnv: local, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execAddToEnv(int(local)) 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: argCount, advance := vm.code.GetUint(vm.pc) vm.pc += advance err = vm.execCall(uint(argCount)) 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 }