diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2022-06-29 15:27:12 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2022-06-29 15:27:12 +0800 |
commit | c18f88dded6e14a0c931467dc856e5a1b2170076 (patch) | |
tree | ccc4e20d772b7f56021e1787f8e631a958ff3c2d /script | |
parent | 0c95dbcc67e096e2da61736e9fbea44c3e82e953 (diff) | |
download | lua-language-server-c18f88dded6e14a0c931467dc856e5a1b2170076.zip |
cleanup
Diffstat (limited to 'script')
-rw-r--r-- | script/vm/compiler.lua | 193 |
1 files changed, 110 insertions, 83 deletions
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua index c25b7df1..265d8b8b 100644 --- a/script/vm/compiler.lua +++ b/script/vm/compiler.lua @@ -10,6 +10,7 @@ local vm = require 'vm.vm' ---@field _compiledNodes boolean ---@field _node vm.node ---@field _globalBase table +---@field cindex integer -- 该函数有副作用,会给source绑定node! local function bindDocs(source) @@ -550,89 +551,20 @@ local function matchCall(source) end end ----@return vm.node? +---@return vm.node local function getReturn(func, index, args) - if func.special == 'setmetatable' then - if not args then - return nil - end - return getReturnOfSetMetaTable(args) - end - if func.special == 'pcall' and index > 1 then - if not args then - return nil - end - local newArgs = {} - for i = 2, #args do - newArgs[#newArgs+1] = args[i] - end - return getReturn(args[1], index - 1, newArgs) - end - if func.special == 'xpcall' and index > 1 then - if not args then - return nil - end - local newArgs = {} - for i = 3, #args do - newArgs[#newArgs+1] = args[i] - end - return getReturn(args[1], index - 1, newArgs) - end - if func.special == 'require' then - if not args then - return nil - end - local nameArg = args[1] - if not nameArg or nameArg.type ~= 'string' then - return nil - end - local name = nameArg[1] - if not name or type(name) ~= 'string' then - return nil - end - local uri = rpath.findUrisByRequirePath(guide.getUri(func), name)[1] - if not uri then - return nil - end - local state = files.getState(uri) - local ast = state and state.ast - if not ast then - return nil - end - return vm.compileNode(ast) + if not func._callReturns then + func._callReturns = {} end - local funcNode = vm.compileNode(func) - ---@type vm.node? - local result - for mfunc in funcNode:eachObject() do - if mfunc.type == 'function' - or mfunc.type == 'doc.type.function' then - ---@cast mfunc parser.object - local returnObject = vm.getReturnOfFunction(mfunc, index) - if returnObject then - local returnNode = vm.compileNode(returnObject) - for rnode in returnNode:eachObject() do - if rnode.type == 'generic' then - returnNode = rnode:resolve(guide.getUri(func), args) - break - end - end - if returnNode then - for rnode in returnNode:eachObject() do - -- TODO: narrow type - if rnode.type ~= 'doc.generic.name' then - result = result or vm.createNode() - result:merge(rnode) - end - end - if result and returnNode:isOptional() then - result:addOptional() - end - end - end - end + if not func._callReturns[index] then + func._callReturns[index] = { + type = 'call.return', + parent = func, + cindex = index, + args = args, + } end - return result + return vm.compileNode(func._callReturns[index]) end ---@param source parser.object @@ -765,13 +697,13 @@ function vm.selectNode(list, index) local result if exp.type == 'call' then result = getReturn(exp.node, index, exp.args) - if not result then - return vm.createNode(vm.declareGlobal('type', 'unknown')), exp + if result:isEmpty() then + result:merge(vm.declareGlobal('type', 'unknown')) end else ---@type vm.node result = vm.compileNode(exp) - if result and exp.type == 'varargs' and result:isEmpty() then + if exp.type == 'varargs' and result:isEmpty() then result:merge(vm.declareGlobal('type', 'unknown')) end end @@ -1596,6 +1528,101 @@ local compilerSwitch = util.switch() vm.setNode(source, vm.declareGlobal('type', 'nil')) end end) + : case 'call.return' + ---@param source parser.object + : call(function (source) + local func = source.parent + local args = source.args + local index = source.cindex + if func.special == 'setmetatable' then + if not args then + return + end + vm.setNode(source, getReturnOfSetMetaTable(args)) + return + end + if func.special == 'pcall' and index > 1 then + if not args then + return + end + local newArgs = {} + for i = 2, #args do + newArgs[#newArgs+1] = args[i] + end + local node = getReturn(args[1], index - 1, newArgs) + if node then + vm.setNode(source, node) + end + return + end + if func.special == 'xpcall' and index > 1 then + if not args then + return + end + local newArgs = {} + for i = 3, #args do + newArgs[#newArgs+1] = args[i] + end + local node = getReturn(args[1], index - 1, newArgs) + if node then + vm.setNode(source, node) + end + return + end + if func.special == 'require' then + if not args then + return + end + local nameArg = args[1] + if not nameArg or nameArg.type ~= 'string' then + return + end + local name = nameArg[1] + if not name or type(name) ~= 'string' then + return + end + local uri = rpath.findUrisByRequirePath(guide.getUri(func), name)[1] + if not uri then + return + end + local state = files.getState(uri) + local ast = state and state.ast + if not ast then + return + end + vm.setNode(source, vm.compileNode(ast)) + return + end + local funcNode = vm.compileNode(func) + ---@type vm.node? + for mfunc in funcNode:eachObject() do + if mfunc.type == 'function' + or mfunc.type == 'doc.type.function' then + ---@cast mfunc parser.object + local returnObject = vm.getReturnOfFunction(mfunc, index) + if returnObject then + local returnNode = vm.compileNode(returnObject) + for rnode in returnNode:eachObject() do + if rnode.type == 'generic' then + returnNode = rnode:resolve(guide.getUri(func), args) + break + end + end + if returnNode then + for rnode in returnNode:eachObject() do + -- TODO: narrow type + if rnode.type ~= 'doc.generic.name' then + vm.setNode(source, rnode) + end + end + if returnNode:isOptional() then + vm.getNode(source):addOptional() + end + end + end + end + end + end) : case 'main' : call(function (source) if source.returns then |