From 32fbab2b26bdb6b4c00ed8f467f12e5161ba4c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=80=E8=90=8C=E5=B0=8F=E6=B1=90?= Date: Mon, 13 Jun 2022 22:46:02 +0800 Subject: new runner --- script/vm/runner.lua | 454 ++++++++------------------------------------------- 1 file changed, 65 insertions(+), 389 deletions(-) (limited to 'script/vm/runner.lua') diff --git a/script/vm/runner.lua b/script/vm/runner.lua index 9fe0f172..38b0b66c 100644 --- a/script/vm/runner.lua +++ b/script/vm/runner.lua @@ -2,257 +2,19 @@ local vm = require 'vm.vm' local guide = require 'parser.guide' +---@alias vm.runner.callback fun(src: parser.object, node: vm.node) + ---@class vm.runner ----@field loc parser.object ----@field mainBlock parser.object ----@field blocks table ----@field steps vm.runner.step[] +---@field _loc parser.object +---@field _objs parser.object[] +---@field _callback vm.runner.callback local mt = {} mt.__index = mt -mt.index = 1 - ----@class parser.object ----@field _casts parser.object[] - ----@class vm.runner.step ----@field type 'truthy' | 'falsy' | 'as' | 'add' | 'remove' | 'object' | 'save' | 'push' | 'merge' | 'cast' ----@field pos integer ----@field order? integer ----@field node? vm.node ----@field object? parser.object ----@field name? string ----@field cast? parser.object ----@field tag? string ----@field copy? boolean ----@field new? boolean ----@field ref1? vm.runner.step ----@field ref2? vm.runner.step - ----@param filter parser.object ----@param outStep vm.runner.step ----@param blockStep vm.runner.step -function mt:_compileNarrowByFilter(filter, outStep, blockStep) - if not filter then - return - end - if filter.type == 'paren' then - if filter.exp then - self:_compileNarrowByFilter(filter.exp, outStep, blockStep) - end - return - end - if filter.type == 'unary' then - if not filter.op - or not filter[1] then - return - end - if filter.op.type == 'not' then - local exp = filter[1] - if exp.type == 'getlocal' and exp.node == self.loc then - self.steps[#self.steps+1] = { - type = 'falsy', - pos = filter.finish, - new = true, - } - self.steps[#self.steps+1] = { - type = 'truthy', - pos = filter.finish, - ref1 = outStep, - } - end - end - elseif filter.type == 'binary' then - if not filter.op - or not filter[1] - or not filter[2] then - return - end - if filter.op.type == 'and' then - local dummyStep = { - type = 'save', - copy = true, - ref1 = outStep, - pos = filter.start - 1, - } - self.steps[#self.steps+1] = dummyStep - self:_compileNarrowByFilter(filter[1], dummyStep, blockStep) - self:_compileNarrowByFilter(filter[2], dummyStep, blockStep) - end - if filter.op.type == 'or' then - self:_compileNarrowByFilter(filter[1], outStep, blockStep) - local dummyStep = { - type = 'push', - copy = true, - ref1 = outStep, - pos = filter.op.finish, - } - self.steps[#self.steps+1] = dummyStep - self:_compileNarrowByFilter(filter[2], outStep, dummyStep) - self.steps[#self.steps+1] = { - type = 'push', - tag = 'or reset', - ref1 = blockStep, - pos = filter.finish, - } - end - if filter.op.type == '==' - or filter.op.type == '~=' then - local loc, exp - for i = 1, 2 do - loc = filter[i] - if loc.type == 'getlocal' and loc.node == self.loc then - exp = filter[i % 2 + 1] - break - end - end - if not loc or not exp then - return - end - if guide.isLiteral(exp) then - if filter.op.type == '==' then - self.steps[#self.steps+1] = { - type = 'remove', - name = exp.type, - pos = filter.finish, - ref1 = outStep, - } - self.steps[#self.steps+1] = { - type = 'as', - name = exp.type, - pos = filter.finish, - new = true, - } - end - if filter.op.type == '~=' then - self.steps[#self.steps+1] = { - type = 'as', - name = exp.type, - pos = filter.finish, - ref1 = outStep, - } - self.steps[#self.steps+1] = { - type = 'remove', - name = exp.type, - pos = filter.finish, - new = true, - } - end - end - end - else - if filter.type == 'getlocal' and filter.node == self.loc then - self.steps[#self.steps+1] = { - type = 'truthy', - pos = filter.finish, - new = true, - } - self.steps[#self.steps+1] = { - type = 'falsy', - pos = filter.finish, - ref1 = outStep, - } - end - end -end - ----@param block parser.object -function mt:_compileBlock(block) - if self.blocks[block] then - return - end - self.blocks[block] = true - if block == self.mainBlock then - return - end - - local parentBlock = guide.getParentBlock(block) - self:_compileBlock(parentBlock) - - if block.type == 'if' then - ---@type vm.runner.step[] - local finals = {} - for i, childBlock in ipairs(block) do - local blockStep = { - type = 'save', - tag = 'block', - copy = true, - pos = childBlock.start, - } - local outStep = { - type = 'save', - tag = 'out', - copy = true, - pos = childBlock.start, - } - self.steps[#self.steps+1] = blockStep - self.steps[#self.steps+1] = outStep - self.steps[#self.steps+1] = { - type = 'push', - ref1 = blockStep, - pos = childBlock.start, - } - self:_compileNarrowByFilter(childBlock.filter, outStep, blockStep) - if not childBlock.hasReturn - and not childBlock.hasGoTo - and not childBlock.hasBreak then - local finalStep = { - type = 'save', - pos = childBlock.finish, - tag = 'final #' .. i, - } - finals[#finals+1] = finalStep - self.steps[#self.steps+1] = finalStep - end - self.steps[#self.steps+1] = { - type = 'push', - tag = 'reset child', - ref1 = outStep, - pos = childBlock.finish, - } - end - self.steps[#self.steps+1] = { - type = 'push', - tag = 'reset if', - pos = block.finish, - copy = true, - } - for _, final in ipairs(finals) do - self.steps[#self.steps+1] = { - type = 'merge', - ref2 = final, - pos = block.finish, - } - end - end - - if block.type == 'function' - or block.type == 'while' - or block.type == 'loop' - or block.type == 'in' - or block.type == 'repeat' - or block.type == 'for' then - local savePoint = { - type = 'save', - copy = true, - pos = block.start, - } - self.steps[#self.steps+1] = { - type = 'push', - copy = true, - pos = block.start, - } - self.steps[#self.steps+1] = savePoint - self.steps[#self.steps+1] = { - type = 'push', - pos = block.finish, - ref1 = savePoint, - } - end -end +mt._index = 1 ---@return parser.object[] function mt:_getCasts() - local root = guide.getRoot(self.loc) + local root = guide.getRoot(self._loc) if not root._casts then root._casts = {} local docs = root.docs @@ -265,180 +27,94 @@ function mt:_getCasts() return root._casts end -function mt:_preCompile() - local startPos = self.loc.start +function mt:_collect() + local startPos = self._loc.start local finishPos = 0 - for _, ref in ipairs(self.loc.ref) do - self.steps[#self.steps+1] = { - type = 'object', - object = ref, - pos = ref.range or ref.start, - } - if ref.start > finishPos then - finishPos = ref.start + for _, ref in ipairs(self._loc.ref) do + if ref.type == 'getlocal' + or ref.type == 'setlocal' then + self._objs[#self._objs+1] = ref + if ref.start > finishPos then + finishPos = ref.start + end end - local block = guide.getParentBlock(ref) - self:_compileBlock(block) end - for i, step in ipairs(self.steps) do - if step.type ~= 'object' then - step.order = i - end + if #self._objs == 0 then + return end local casts = self:_getCasts() for _, cast in ipairs(casts) do - if cast.loc[1] == self.loc[1] + 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.steps[#self.steps+1] = { - type = 'cast', - cast = cast, - pos = cast.start, - } + and guide.getLocal(self._loc, self._loc[1], cast.start) == self._loc then + self._objs[#self._objs+1] = cast end end - table.sort(self.steps, function (a, b) - if a.pos == b.pos then - return (a.order or 0) < (b.order or 0) - else - return a.pos < b.pos - end + table.sort(self._objs, function (a, b) + return (a.range or a.start) < (b.range or b.start) end) end ----@param loc parser.object + +---@param pos integer ---@param node vm.node +---@return parser.object ---@return vm.node -local function checkAssert(loc, node) - local parent = loc.parent - if parent.type == 'binary' then - if parent.op and (parent.op.type == '~=' or parent.op.type == '==') then - local exp - for i = 1, 2 do - if parent[i] == loc then - exp = parent[i % 2 + 1] - end - end - if exp and guide.isLiteral(exp) then - local callargs = parent.parent - if callargs.type == 'callargs' - and callargs.parent.node.special == 'assert' - and callargs[1] == parent then - if parent.op.type == '~=' then - node:remove(exp.type) - end - if parent.op.type == '==' then - node = vm.compileNode(exp) - end - end - end +function mt:_fastWard(pos, node) + for i = self._index, #self._objs do + local obj = self._objs[i] + if obj.start > pos then + self._index = i + return obj, node + end + if obj.type == 'getlocal' + or obj.type == 'setlocal' then + node = self._callback(obj, node) or node + else + error('unexpected type: ' .. obj.type) end end - if parent.type == 'callargs' - and parent.parent.node.special == 'assert' - and parent[1] == loc then - node:setTruthy() - end - return node end ----@param callback fun(src: parser.object, node: vm.node) -function mt:launch(callback) - local topNode = vm.getNode(self.loc):copy() - for _, step in ipairs(self.steps) do - local node = step.ref1 and step.ref1.node or topNode - if step.type == 'truthy' then - if step.new then - node = node:copy() - topNode = node - end - node:setTruthy() - elseif step.type == 'falsy' then - if step.new then - node = node:copy() - topNode = node - end - node:setFalsy() - elseif step.type == 'as' then - if step.new then - topNode = vm.createNode(vm.getGlobal('type', step.name)) - else - node:clear() - node:merge(vm.getGlobal('type', step.name)) - end - elseif step.type == 'add' then - if step.new then - node = node:copy() - topNode = node - end - node:merge(vm.getGlobal('type', step.name)) - elseif step.type == 'remove' then - if step.new then - node = node:copy() - topNode = node - end - node:remove(step.name) - elseif step.type == 'object' then - topNode = callback(step.object, node) or node - if step.object.type == 'getlocal' then - topNode = checkAssert(step.object, node) - end - elseif step.type == 'save' then - if step.copy then - node = node:copy() - end - step.node = node - elseif step.type == 'push' then - if step.copy then - node = node:copy() - end - topNode = node - elseif step.type == 'merge' then - node:merge(step.ref2.node) - elseif step.type == 'cast' then - topNode = node:copy() - for _, cast in ipairs(step.cast.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 +---@param block parser.object +---@param node vm.node +function mt:_launchBlock(block, node) + local top, topNode = self:_fastWard(block.start, node) + if not top then + return + end + for _, action in ipairs(block) do + if action.finish < top.start then + goto CONTINUE + end + top, topNode = self:_fastWard(action.finish, topNode) + if not top then + return end + ::CONTINUE:: end + self:_fastWard(math.huge, topNode) end ---@param loc parser.object ----@return vm.runner -function vm.createRunner(loc) +---@param callback vm.runner.callback +function vm.launchRunner(loc, callback) local self = setmetatable({ - loc = loc, - mainBlock = guide.getParentBlock(loc), - blocks = {}, - steps = {}, + _loc = loc, + _objs = {}, + _callback = callback, }, mt) - self:_preCompile() + self:_collect() + + if #self._objs == 0 then + return + end - return self + self:_launchBlock(guide.getParentBlock(loc), vm.getNode(loc)) end -- cgit v1.2.3