package vm import ( "jinx/pkg/lang/modules" "jinx/pkg/lang/vm/code" "jinx/pkg/lang/vm/mem" "jinx/pkg/lang/vm/stack" "jinx/pkg/lang/vm/value" ) type VM struct { pos code.Pos main modules.Module modules []modules.Module stack stack.Stack memory mem.Mem canAddGlobals bool globals map[string]mem.Ptr corePtrs value.CorePtrs output Output } func New(main modules.Module, output Output) *VM { vm := &VM{ pos: code.NewPos(-1, 0), main: main, modules: []modules.Module{}, stack: stack.New(), memory: mem.New(), canAddGlobals: false, globals: make(map[string]mem.Ptr), corePtrs: value.CorePtrs{}, output: output, } if err := vm.setup(); err != nil { panic(err) } return vm } func (vm *VM) GetResult() (string, error) { if vm.stack.Len() == 0 { return "", stack.ErrStackUnderflow } res, err := vm.stack.Get(vm.stack.Len() - 1) 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 { vm.canAddGlobals = true for i := 0; i < len(vm.modules); i++ { if err := vm.executeModule(i); err != nil { return err } // Drop all calls from the stack after the module has finished for vm.stack.CallDepth() > 1 { if _, err := vm.popCallAndDrop(); err != nil { return err } } // Drop the root stack values for !vm.stack.IsEmpty() { if _, err := vm.popAndDrop(); err != nil { return err } } } // All the core ptrs should be initialized once core is loaded if !vm.corePtrs.Complete() { panic("core ptrs not initialized after loading core (perhaps core is missing?)") } vm.canAddGlobals = false if err := vm.executeModule(-1); err != nil { return err } return nil } func (vm *VM) executeModule(moduleID int) error { vm.pos = code.NewPos(moduleID, 0) if err := vm.stack.PutRootCall(vm.pos); err != nil { return err } for { module := vm.module() code := module.Code() if vm.pc() >= code.Len() { return nil } op, advance := code.GetOp(vm.pc()) vm.advancePC(advance) if decision, err := vm.step(code, op); err != nil { loc, ok := code.Debug().PCToLoc(vm.pc()) line := loc.Row if !ok { line = -1 } return Error{ Pos: vm.pos, Module: module.Name(), Line: line, Err: err, } } else if decision == stepDecisionHalt { return nil } } } type stepDecision int const ( stepDecisionContinue stepDecision = iota stepDecisionHalt ) func (vm *VM) step(module *code.Code, 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 := module.GetInt(vm.pc()) vm.advancePC(advance) vm.execPushInt(x) case code.OpPushFloat: x, advance := module.GetFloat(vm.pc()) vm.advancePC(advance) vm.execPushFloat(x) case code.OpPushString: str, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = 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 := module.GetUint(vm.pc()) vm.advancePC(advance) vm.execPushFunction(int(x)) case code.OpPushObject: err = vm.execPushObject() case code.OpPushType: name, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execPushType(name) case code.OpDrop: dropAmount, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execDrop(uint(dropAmount)) case code.OpAddGlobal: globalName, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execAddGlobal(globalName) case code.OpGetGlobal: globalName, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execGetGlobal(globalName) case code.OpSetGlobal: globalName, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execSetGlobal(globalName) case code.OpGetLocal: offset, advance := module.GetInt(vm.pc()) vm.advancePC(advance) err = vm.execGetLocal(int(offset)) case code.OpSetLocal: offset, advance := module.GetInt(vm.pc()) vm.advancePC(advance) err = vm.execSetLocal(int(offset)) case code.OpGetMember: name, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execGetMember(name) case code.OpSetMember: name, advance := module.GetString(vm.pc()) vm.advancePC(advance) err = vm.execSetMember(name) case code.OpGetEnv: envIndex, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execGetEnv(int(envIndex)) case code.OpSetEnv: envIndex, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execSetEnv(int(envIndex)) case code.OpAddToEnv: local, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execAddToEnv(int(local)) case code.OpAnchorType: err = vm.execAnchorType() case code.OpSetArgCount: argCount, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execSetArgCount(uint(argCount)) case code.OpAdd: err = vm.execAdd() case code.OpSub: err = vm.execSub() case code.OpMul: err = vm.execMul() case code.OpDiv: err = vm.execDiv() case code.OpMod: err = vm.execMod() case code.OpEq: err = vm.execEq() case code.OpLt: err = vm.execLt() case code.OpGt: err = vm.execGt() case code.OpLte: err = vm.execLte() case code.OpGte: err = vm.execGte() case code.OpIndex: err = vm.execIndex() case code.OpSetAtIndex: err = vm.execSetAtIndex() case code.OpCall: argCount, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execCall(uint(argCount)) case code.OpJmp: pc, _ := module.GetUint(vm.pc()) vm.setPC(int(pc)) case code.OpJt: pc, advance := module.GetUint(vm.pc()) vm.advancePC(advance) err = vm.execJumpIf(int(pc), true) case code.OpJf: pc, advance := module.GetUint(vm.pc()) vm.advancePC(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 }