diff options
Diffstat (limited to 'script/vm/runner.lua')
-rw-r--r-- | script/vm/runner.lua | 392 |
1 files changed, 0 insertions, 392 deletions
diff --git a/script/vm/runner.lua b/script/vm/runner.lua deleted file mode 100644 index 75bb3a7f..00000000 --- a/script/vm/runner.lua +++ /dev/null @@ -1,392 +0,0 @@ ----@class vm -local vm = require 'vm.vm' -local guide = require 'parser.guide' -local linked = require 'linked-table' - ----@alias vm.runner.callback fun(src: parser.object, node?: vm.node) - ----@class vm.runner ----@field _loc parser.object ----@field _casts parser.object[] ----@field _callback vm.runner.callback ----@field _mark table ----@field _has table<parser.object, true> ----@field _main parser.object ----@field _uri uri -local mt = {} -mt.__index = mt -mt._index = 1 - ----@return parser.object[] -function mt:_getCasts() - local root = guide.getRoot(self._loc) - if not root._casts then - root._casts = {} - local docs = root.docs - for _, doc in ipairs(docs) do - if doc.type == 'doc.cast' and doc.loc then - root._casts[#root._casts+1] = doc - end - end - end - return root._casts -end - ----@param obj parser.object -function mt:_markHas(obj) - while true do - if self._has[obj] then - return - end - self._has[obj] = true - if obj == self._main then - return - end - obj = obj.parent - end -end - -function mt:collect() - local startPos = self._loc.start - local finishPos = 0 - - for _, ref in ipairs(self._loc.ref) do - if ref.type == 'getlocal' - or ref.type == 'setlocal' then - self:_markHas(ref) - if ref.finish > finishPos then - finishPos = ref.finish - end - end - end - - local casts = self:_getCasts() - for _, cast in ipairs(casts) do - if cast.loc[1] == self._loc[1] - and cast.start > startPos - and cast.finish < finishPos - and guide.getLocal(self._loc, self._loc[1], cast.start) == self._loc then - self._casts[#self._casts+1] = cast - end - end -end - ----@param pos integer ----@param topNode vm.node ----@return vm.node -function mt:_fastWardCasts(pos, topNode) - for i = self._index, #self._casts do - local action = self._casts[i] - if action.start > pos then - self._index = i - return topNode - end - topNode = topNode:copy() - for _, cast in ipairs(action.casts) do - if cast.mode == '+' then - if cast.optional then - topNode:addOptional() - end - if cast.extends then - topNode:merge(vm.compileNode(cast.extends)) - end - elseif cast.mode == '-' then - if cast.optional then - topNode:removeOptional() - end - if cast.extends then - topNode:removeNode(vm.compileNode(cast.extends)) - end - else - if cast.extends then - topNode:clear() - topNode:merge(vm.compileNode(cast.extends)) - end - end - end - end - self._index = self._index + 1 - return topNode -end - ----@param action parser.object ----@param topNode vm.node ----@param outNode? vm.node ----@return vm.node topNode ----@return vm.node outNode -function mt:_lookIntoChild(action, topNode, outNode) - if not self._has[action] - or self._mark[action] then - return topNode, topNode or outNode - end - self._mark[action] = true - topNode = self:_fastWardCasts(action.start, topNode) - if action.type == 'getlocal' then - if action.node == self._loc then - self._callback(action, topNode) - if outNode then - topNode = topNode:copy():setTruthy() - outNode = outNode:copy():setFalsy() - end - end - elseif action.type == 'function' then - self:lookIntoBlock(action, topNode:copy()) - elseif action.type == 'unary' then - if not action[1] then - goto RETURN - end - if action.op.type == 'not' then - outNode = outNode or topNode:copy() - outNode, topNode = self:_lookIntoChild(action[1], topNode, outNode) - outNode = outNode:copy() - end - elseif action.type == 'binary' then - if not action[1] or not action[2] then - goto RETURN - end - if action.op.type == 'and' then - topNode = self:_lookIntoChild(action[1], topNode, topNode:copy()) - topNode = self:_lookIntoChild(action[2], topNode, topNode:copy()) - elseif action.op.type == 'or' then - outNode = outNode or topNode:copy() - local topNode1, outNode1 = self:_lookIntoChild(action[1], topNode, outNode) - local topNode2, outNode2 = self:_lookIntoChild(action[2], outNode1, outNode1:copy()) - topNode = vm.createNode(topNode1, topNode2) - outNode = outNode2:copy() - elseif action.op.type == '==' - or action.op.type == '~=' then - local handler, checker - for i = 1, 2 do - if guide.isLiteral(action[i]) then - checker = action[i] - handler = action[3-i] -- Copilot tells me use `3-i` instead of `i%2+1` - end - end - if not handler then - goto RETURN - end - if handler.type == 'getlocal' - and handler.node == self._loc then - -- if x == y then - topNode = self:_lookIntoChild(handler, topNode, outNode) - local checkerNode = vm.compileNode(checker) - local checkerName = vm.getNodeName(checker) - if checkerName then - topNode = topNode:copy() - if action.op.type == '==' then - topNode:narrow(self._uri, checkerName) - if outNode then - outNode:removeNode(checkerNode) - end - else - topNode:removeNode(checkerNode) - if outNode then - outNode:narrow(self._uri, checkerName) - end - end - end - elseif handler.type == 'call' - and checker.type == 'string' - and handler.node.special == 'type' - and handler.args - and handler.args[1] - and handler.args[1].type == 'getlocal' - and handler.args[1].node == self._loc then - -- if type(x) == 'string' then - self:_lookIntoChild(handler, topNode:copy()) - if action.op.type == '==' then - topNode:narrow(self._uri, checker[1]) - if outNode then - outNode:remove(checker[1]) - end - else - topNode:remove(checker[1]) - if outNode then - outNode:narrow(self._uri, checker[1]) - end - end - elseif handler.type == 'getlocal' - and checker.type == 'string' then - local nodeValue = vm.getObjectValue(handler.node) - if nodeValue - and nodeValue.type == 'select' - and nodeValue.sindex == 1 then - local call = nodeValue.vararg - if call - and call.type == 'call' - and call.node.special == 'type' - and call.args - and call.args[1] - and call.args[1].type == 'getlocal' - and call.args[1].node == self._loc then - -- `local tp = type(x);if tp == 'string' then` - if action.op.type == '==' then - topNode:narrow(self._uri, checker[1]) - if outNode then - outNode:remove(checker[1]) - end - else - topNode:remove(checker[1]) - if outNode then - outNode:narrow(self._uri, checker[1]) - end - end - end - end - end - end - elseif action.type == 'loop' - or action.type == 'in' - or action.type == 'repeat' - or action.type == 'for' then - topNode = self:lookIntoBlock(action, topNode:copy()) - elseif action.type == 'while' then - local blockNode, mainNode - if action.filter then - blockNode, mainNode = self:_lookIntoChild(action.filter, topNode:copy(), topNode:copy()) - else - blockNode = topNode:copy() - mainNode = topNode:copy() - end - blockNode = self:lookIntoBlock(action, blockNode:copy()) - topNode = mainNode:merge(blockNode) - if action.filter then - -- look into filter again - guide.eachSource(action.filter, function (src) - self._mark[src] = nil - end) - blockNode, topNode = self:_lookIntoChild(action.filter, topNode:copy(), topNode:copy()) - end - elseif action.type == 'if' then - local hasElse - local mainNode = topNode:copy() - local blockNodes = {} - for _, subBlock in ipairs(action) do - local blockNode = mainNode:copy() - if subBlock.filter then - blockNode, mainNode = self:_lookIntoChild(subBlock.filter, blockNode, mainNode) - else - hasElse = true - mainNode:clear() - end - blockNode = self:lookIntoBlock(subBlock, blockNode:copy()) - local neverReturn = subBlock.hasReturn - or subBlock.hasGoTo - or subBlock.hasBreak - or subBlock.hasError - if not neverReturn then - blockNodes[#blockNodes+1] = blockNode - end - end - if not hasElse and not topNode:hasKnownType() then - mainNode:merge(vm.declareGlobal('type', 'unknown')) - end - for _, blockNode in ipairs(blockNodes) do - mainNode:merge(blockNode) - end - topNode = mainNode - elseif action.type == 'call' then - if action.node.special == 'assert' and action.args and action.args[1] then - topNode = self:_lookIntoChild(action.args[1], topNode, topNode:copy()) - end - elseif action.type == 'paren' then - topNode, outNode = self:_lookIntoChild(action.exp, topNode, outNode) - elseif action.type == 'setlocal' then - if action.node == self._loc then - if action.value then - self:_lookIntoChild(action.value, topNode) - end - topNode = self._callback(action, topNode) - end - elseif action.type == 'local' then - if action.value - and action.ref - and action.value.type == 'select' then - local index = action.value.sindex - local call = action.value.vararg - if index == 1 - and call.type == 'call' - and call.node - and call.node.special == 'type' - and call.args then - local getLoc = call.args[1] - if getLoc - and getLoc.type == 'getlocal' - and getLoc.node == self._loc then - for _, ref in ipairs(action.ref) do - self:_markHas(ref) - end - end - end - end - end - ::RETURN:: - guide.eachChild(action, function (src) - if self._has[src] then - self:_lookIntoChild(src, topNode) - end - end) - return topNode, outNode or topNode -end - ----@param block parser.object ----@param topNode vm.node ----@return vm.node topNode -function mt:lookIntoBlock(block, topNode) - if not self._has[block] then - return topNode - end - for _, action in ipairs(block) do - if self._has[action] then - topNode = self:_lookIntoChild(action, topNode) - end - end - topNode = self:_fastWardCasts(block.finish, topNode) - return topNode -end - ----@alias runner.info { target?: parser.object, loc: parser.object } - ----@async ----@param loc parser.object ----@param start fun() ----@param finish fun() ----@param callback vm.runner.callback -function vm.launchRunner(loc, start, finish, callback) - local locNode = vm.getNode(loc) - if not locNode then - return - end - - local function launch() - start() - if not loc.ref then - finish() - return - end - local main = guide.getParentBlock(loc) - if not main then - finish() - return - end - local self = setmetatable({ - _loc = loc, - _casts = {}, - _mark = {}, - _has = {}, - _main = main, - _uri = guide.getUri(loc), - _callback = callback, - }, mt) - - self:collect() - - self:lookIntoBlock(main, locNode:copy()) - - locNode:setData('runner', nil) - - finish() - end - - launch() -end |