diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2022-04-21 22:19:26 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2022-04-21 22:19:26 +0800 |
commit | 7291440d890483f70ddd7ba00db483af0257b499 (patch) | |
tree | 27d2a33c88a2f7d8ef908297bc5d7e9771db68d7 /script | |
parent | 8ba44c3e21ac761b88226fec0cc8a5347a4a5804 (diff) | |
parent | a19f2ee7394c5f5a10342a37497fbd7b7587316c (diff) | |
download | lua-language-server-7291440d890483f70ddd7ba00db483af0257b499.zip |
Merge remote-tracking branch 'origin/type-narrow'
Diffstat (limited to 'script')
-rw-r--r-- | script/parser/guide.lua | 2 | ||||
-rw-r--r-- | script/parser/luadoc.lua | 4 | ||||
-rw-r--r-- | script/vm/compiler.lua | 72 | ||||
-rw-r--r-- | script/vm/init.lua | 1 | ||||
-rw-r--r-- | script/vm/node.lua | 56 | ||||
-rw-r--r-- | script/vm/runner.lua | 134 |
6 files changed, 231 insertions, 38 deletions
diff --git a/script/parser/guide.lua b/script/parser/guide.lua index 906a9926..f15e6e72 100644 --- a/script/parser/guide.lua +++ b/script/parser/guide.lua @@ -56,6 +56,7 @@ local type = type ---@field init parser.object ---@field step parser.object ---@field redundant { max: integer, passed: integer } +---@field filter parser.object ---@field _root parser.object ---@class guide @@ -71,6 +72,7 @@ local blockTypes = { ['repeat'] = true, ['do'] = true, ['function'] = true, + ['if'] = true, ['ifblock'] = true, ['elseblock'] = true, ['elseifblock'] = true, diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua index 4cb8b520..2a570454 100644 --- a/script/parser/luadoc.lua +++ b/script/parser/luadoc.lua @@ -709,6 +709,10 @@ function parseType(parent) if not result.start then result.start = getFinish() end + if checkToken('symbol', '?', 1) then + nextToken() + result.optional = true + end result.finish = getFinish() result.firstFinish = result.finish diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua index ef91b4b1..94968292 100644 --- a/script/vm/compiler.lua +++ b/script/vm/compiler.lua @@ -751,21 +751,9 @@ end ---@param source parser.object ---@return vm.node -local function compileLocalBase(source) - if not source._localBase then - source._localBase = { - type = 'localbase', - parent = source, - } - end - local baseNode = vm.getNode(source._localBase) - if baseNode then - return baseNode - end - baseNode = vm.createNode() - vm.setNode(source._localBase, baseNode, true) - +local function compileLocal(source) vm.setNode(source, source) + local hasMarkDoc if source.bindDocs then hasMarkDoc = bindDocs(source) @@ -852,12 +840,7 @@ local function compileLocalBase(source) end end - baseNode:merge(vm.getNode(source)) - vm.removeNode(source) - - baseNode:setData('hasDefined', hasMarkDoc or hasMarkParam or hasMarkValue) - - return baseNode + vm.getNode(source):setData('hasDefined', hasMarkDoc or hasMarkParam or hasMarkValue) end local compilerSwitch = util.switch() @@ -917,35 +900,50 @@ local compilerSwitch = util.switch() end) : case 'local' : case 'self' + ---@param source parser.object : call(function (source) - local baseNode = compileLocalBase(source) - vm.setNode(source, baseNode, true) - if not baseNode:getData 'hasDefined' and source.ref then + compileLocal(source) + local refs = source.ref + if not refs then + return + end + + local hasMark = vm.getNode(source):getData 'hasDefined' + + local runner = vm.createRunner(source) + runner:launch(function (src, node) + if src.type == 'setlocal' then + if src.value and not hasMark then + if src.value.type == 'table' then + vm.setNode(src, src.value) + else + vm.setNode(src, vm.compileNode(src.value)) + end + else + vm.setNode(src, node) + end + return vm.getNode(src) + elseif src.type == 'getlocal' then + vm.setNode(src, node, true) + end + end) + + if not hasMark then + local parentFunc = guide.getParentFunction(source) for _, ref in ipairs(source.ref) do - if ref.type == 'setlocal' then - vm.setNode(source, vm.compileNode(ref)) + if ref.type == 'setlocal' + and guide.getParentFunction(ref) == parentFunc then + vm.setNode(source, vm.getNode(ref)) end end end end) : case 'setlocal' : call(function (source) - local baseNode = compileLocalBase(source.node) - if not baseNode:getData 'hasDefined' and source.value then - if source.value.type == 'table' then - vm.setNode(source, source.value) - else - vm.setNode(source, vm.compileNode(source.value)) - end - end - baseNode:merge(vm.getNode(source)) - vm.setNode(source, baseNode, true) vm.compileNode(source.node) end) : case 'getlocal' : call(function (source) - local baseNode = compileLocalBase(source.node) - vm.setNode(source, baseNode, true) vm.compileNode(source.node) end) : case 'setfield' diff --git a/script/vm/init.lua b/script/vm/init.lua index 0058c698..aa55a6c7 100644 --- a/script/vm/init.lua +++ b/script/vm/init.lua @@ -8,5 +8,6 @@ require 'vm.field' require 'vm.doc' require 'vm.type' require 'vm.library' +require 'vm.runner' require 'vm.manager' return vm diff --git a/script/vm/node.lua b/script/vm/node.lua index 81405c48..81d9f335 100644 --- a/script/vm/node.lua +++ b/script/vm/node.lua @@ -93,6 +93,59 @@ function mt:isFalsy() return false end +---@return vm.node +function mt:copyTruly() + local newNode = vm.createNode() + newNode.optional = false + local hasBoolean, hasTrue + for _, c in ipairs(self) do + if c.type == 'nil' + or (c.type == 'boolean' and c[1] == false) + or (c.type == 'doc.type.boolean' and c[1] == false) then + goto CONTINUE + end + if c.type == 'global' and c.cate == 'type' and c.name == 'boolean' then + hasBoolean = true + goto CONTINUE + end + if c.type == 'boolean' or c.type == 'doc.type.boolean' then + hasTrue = true + end + newNode:merge(c) + ::CONTINUE:: + end + if hasBoolean and not hasTrue then + newNode:merge { + type = 'doc.type.boolean', + [1] = true, + } + end + return newNode +end + +---@param name string +---@return vm.node +function mt:copyWithout(name) + local newNode = vm.createNode() + if self:isOptional() then + newNode:addOptional() + end + for _, c in ipairs(self) do + if (c.type == 'global' and c.cate == 'type' and c.name == name) + or (c.type == name) + or (c.type == 'doc.type.integer' and (name == 'number' or name == 'integer')) + or (c.type == 'doc.type.boolean' and name == 'boolean') + or (c.type == 'doc.type.table' and name == 'table') + or (c.type == 'doc.type.array' and name == 'table') + or (c.type == 'doc.type.function' and name == 'function') then + goto CONTINUE + end + newNode:merge(c) + ::CONTINUE:: + end + return newNode +end + ---@return fun():vm.object function mt:eachObject() local i = 0 @@ -126,13 +179,14 @@ function vm.setNode(source, node, cover) me:merge(node) else if node.type == 'vm.node' then - vm.nodeCache[source] = node + vm.nodeCache[source] = node:copy() else vm.nodeCache[source] = vm.createNode(node) end end end +---@param source vm.object ---@return vm.node? function vm.getNode(source) return vm.nodeCache[source] diff --git a/script/vm/runner.lua b/script/vm/runner.lua new file mode 100644 index 00000000..3a315d5c --- /dev/null +++ b/script/vm/runner.lua @@ -0,0 +1,134 @@ +---@class vm +local vm = require 'vm.vm' +local guide = require 'parser.guide' +local globalMgr = require 'vm.global-manager' + +---@class vm.runner +---@field loc parser.object +---@field mainBlock parser.object +---@field blocks table<parser.object, true> +---@field steps vm.runner.step[] +local mt = {} +mt.__index = mt +mt.index = 1 + +---@class parser.object +---@field _hasSorted boolean + +---@class vm.runner.step +---@field type 'truly' | 'optional' | 'add' | 'remove' | 'object' | 'save' | 'load' +---@field pos integer +---@field node? vm.node +---@field object? parser.object +---@field ref? vm.runner.step +---@field name? string + +---@param filter parser.object +---@param pos integer +function mt:_compileNarrowByFilter(filter, pos) + if filter.type == 'unary' then + elseif filter.type == 'binary' then + else + if filter.type == 'getlocal' and filter.node == self.loc then + self.steps[#self.steps+1] = { + type = 'truly', + pos = pos, + } + end + end +end + +function mt:_dropBlock(block) + local savePoint = { + type = 'save', + pos = block.start, + } + self.steps[#self.steps+1] = savePoint + self.steps[#self.steps+1] = { + type = 'load', + pos = block.finish, + ref = savePoint, + } +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 == 'ifblock' + or block.type == 'elseif' then + if block[1] then + self:_compileNarrowByFilter(block.filter, block[1].start) + end + end + + if block.type == 'if' then + self:_dropBlock(block) + end + + if block.type == 'function' then + self:_dropBlock(block) + end +end + +function mt:_preCompile() + for _, ref in ipairs(self.loc.ref) do + self.steps[#self.steps+1] = { + type = 'object', + object = ref, + pos = ref.start, + } + local block = guide.getParentBlock(ref) + self:_compileBlock(block) + end + table.sort(self.steps, function (a, b) + return a.pos < b.pos + end) +end + +---@param callback fun(src: parser.object, node: vm.node) +function mt:launch(callback) + local node = vm.getNode(self.loc) + for _, step in ipairs(self.steps) do + if step.type == 'truly' then + node = node:copyTruly() + elseif step.type == 'optional' then + node = node:copy():addOptional() + elseif step.type == 'add' then + node = node:copy():merge(globalMgr.getGlobal('type', step.name)) + elseif step.type == 'remove' then + node = node:copyWithout(step.name) + elseif step.type == 'object' then + node = callback(step.object, node) or node + elseif step.type == 'save' then + -- Nothing need to do + elseif step.type == 'load' then + node = step.ref.node + end + step.node = node + end +end + +---@param loc parser.object +---@return vm.runner +function vm.createRunner(loc) + local self = setmetatable({ + loc = loc, + mainBlock = guide.getParentBlock(loc), + blocks = {}, + steps = {}, + }, mt) + + self:_preCompile() + + return self +end |