summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--script/core/completion/completion.lua1
-rw-r--r--script/core/definition.lua11
-rw-r--r--script/core/diagnostics/deprecated.lua1
-rw-r--r--script/core/diagnostics/duplicate-doc-field.lua1
-rw-r--r--script/core/diagnostics/duplicate-index.lua2
-rw-r--r--script/core/diagnostics/undefined-field.lua1
-rw-r--r--script/core/diagnostics/undefined-global.lua1
-rw-r--r--script/core/generic.lua283
-rw-r--r--script/core/hover/description.lua1
-rw-r--r--script/core/infer.lua16
-rw-r--r--script/core/noder.lua1914
-rw-r--r--script/core/reference.lua11
-rw-r--r--script/core/rename.lua3
-rw-r--r--script/core/searcher.lua1350
-rw-r--r--script/parser/guide.lua117
-rw-r--r--script/parser/newparser.lua10
-rw-r--r--script/utility.lua31
-rw-r--r--script/vm/eachDef.lua11
-rw-r--r--script/vm/getDef.lua175
-rw-r--r--script/vm/getDocs.lua3
-rw-r--r--script/vm/getGlobals.lua1
-rw-r--r--script/vm/getRef.lua (renamed from script/vm/eachRef.lua)1
-rw-r--r--script/vm/global-node.lua142
-rw-r--r--script/vm/init.lua5
-rw-r--r--script/vm/local-id.lua116
-rw-r--r--script/vm/node/class.lua11
-rw-r--r--script/vm/node/compiler.lua190
-rw-r--r--script/vm/node/global.lua79
-rw-r--r--script/vm/node/union.lua49
-rw-r--r--script/vm/state.lua80
-rw-r--r--script/vm/vm.lua3
-rw-r--r--test.lua2
-rw-r--r--test/basic/init.lua1
-rw-r--r--test/basic/linker.txt141
-rw-r--r--test/basic/noder.lua143
-rw-r--r--test/definition/field.lua23
-rw-r--r--test/definition/init.lua1
-rw-r--r--test/definition/table.lua52
38 files changed, 1000 insertions, 3983 deletions
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua
index 2c3a354d..569847a4 100644
--- a/script/core/completion/completion.lua
+++ b/script/core/completion/completion.lua
@@ -19,7 +19,6 @@ local lang = require 'language'
local lookBackward = require 'core.look-backward'
local guide = require 'parser.guide'
local infer = require 'core.infer'
-local noder = require 'core.noder'
local await = require 'await'
local postfix = require 'core.completion.postfix'
diff --git a/script/core/definition.lua b/script/core/definition.lua
index b08b7706..9270367f 100644
--- a/script/core/definition.lua
+++ b/script/core/definition.lua
@@ -1,4 +1,3 @@
-local searcher = require 'core.searcher'
local workspace = require 'workspace'
local files = require 'files'
local vm = require 'vm'
@@ -132,21 +131,11 @@ return function (uri, offset)
end
local defs = vm.getAllDefs(source)
- local values = {}
- for _, src in ipairs(defs) do
- local value = searcher.getObjectValue(src)
- if value and value ~= src and guide.isLiteral(value) then
- values[value] = true
- end
- end
for _, src in ipairs(defs) do
if src.dummy then
goto CONTINUE
end
- if values[src] then
- goto CONTINUE
- end
local root = guide.getRoot(src)
if not root then
goto CONTINUE
diff --git a/script/core/diagnostics/deprecated.lua b/script/core/diagnostics/deprecated.lua
index 649f4dab..bd2a95b0 100644
--- a/script/core/diagnostics/deprecated.lua
+++ b/script/core/diagnostics/deprecated.lua
@@ -5,7 +5,6 @@ local guide = require 'parser.guide'
local config = require 'config'
local define = require 'proto.define'
local await = require 'await'
-local noder = require 'core.noder'
local types = {'getglobal', 'getfield', 'getindex', 'getmethod'}
---@async
diff --git a/script/core/diagnostics/duplicate-doc-field.lua b/script/core/diagnostics/duplicate-doc-field.lua
index 71610ef5..0ad2045e 100644
--- a/script/core/diagnostics/duplicate-doc-field.lua
+++ b/script/core/diagnostics/duplicate-doc-field.lua
@@ -1,6 +1,5 @@
local files = require 'files'
local lang = require 'language'
-local noder = require 'core.noder'
return function (uri, callback)
local state = files.getState(uri)
diff --git a/script/core/diagnostics/duplicate-index.lua b/script/core/diagnostics/duplicate-index.lua
index d1141901..bf46996e 100644
--- a/script/core/diagnostics/duplicate-index.lua
+++ b/script/core/diagnostics/duplicate-index.lua
@@ -2,8 +2,6 @@ local files = require 'files'
local guide = require 'parser.guide'
local lang = require 'language'
local define = require 'proto.define'
-local vm = require 'vm'
-local noder = require 'core.noder'
return function (uri, callback)
local ast = files.getState(uri)
diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index 7d309096..ea15dcda 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -2,7 +2,6 @@ local files = require 'files'
local vm = require 'vm'
local lang = require 'language'
local guide = require 'parser.guide'
-local noder = require 'core.noder'
local await = require 'await'
local skipCheckClass = {
diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua
index b570ca65..cf31ec4e 100644
--- a/script/core/diagnostics/undefined-global.lua
+++ b/script/core/diagnostics/undefined-global.lua
@@ -3,7 +3,6 @@ local vm = require 'vm'
local lang = require 'language'
local config = require 'config'
local guide = require 'parser.guide'
-local noder = require 'core.noder'
local collector = require 'core.collector' 'searcher'
local await = require 'await'
diff --git a/script/core/generic.lua b/script/core/generic.lua
deleted file mode 100644
index f260dc0b..00000000
--- a/script/core/generic.lua
+++ /dev/null
@@ -1,283 +0,0 @@
-local guide = require 'parser.guide'
-local noder = require "core.noder"
-
----@class generic.value: parser.guide.object
----@field type string
----@field closure generic.closure
----@field proto parser.guide.object
----@field parent parser.guide.object
-
----@class generic.closure: parser.guide.object
----@field type string
----@field proto parser.guide.object
----@field upvalues table<string, generic.value[]>
----@field params generic.value[]
----@field returns generic.value[]
-
-local m = {}
-
----@param closure generic.closure
----@param proto parser.guide.object
-local function instantValue(closure, proto)
- ---@type generic.value
- local value = {
- type = 'generic.value',
- closure = closure,
- proto = proto,
- parent = proto.parent,
- }
- closure.values[#closure.values+1] = value
- return value
-end
-
----递归实例化对象
----@param proto parser.guide.object
----@return generic.value
-local function createValue(closure, proto, callback, road)
- road = road or {}
- if proto.type == 'doc.type' then
- local types = {}
- local hasGeneric
- for i, tp in ipairs(proto.types) do
- local genericValue = createValue(closure, tp, callback, road)
- if genericValue then
- hasGeneric = true
- types[i] = genericValue
- else
- types[i] = tp
- end
- end
- if not hasGeneric then
- return nil
- end
- local value = instantValue(closure, proto)
- value.types = types
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.name' then
- if not proto.typeGeneric then
- return nil
- end
- local key = proto[1]
- local value = instantValue(closure, proto)
- if callback then
- callback(road, key, proto)
- end
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.function' then
- local hasGeneric
- local args = {}
- local returns = {}
- for i, arg in ipairs(proto.args) do
- local value = createValue(closure, arg, callback, road)
- if value then
- hasGeneric = true
- end
- args[i] = value or arg
- end
- for i, rtn in ipairs(proto.returns) do
- local value = createValue(closure, rtn, callback, road)
- if value then
- hasGeneric = true
- end
- returns[i] = value or rtn
- end
- if not hasGeneric then
- return nil
- end
- local value = instantValue(closure, proto)
- value.args = args
- value.returns = returns
- value.isGeneric = true
- noder.pushSource(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.array' then
- road[#road+1] = noder.WEAK_ANY_FIELD
- local node = createValue(closure, proto.node, callback, road)
- road[#road] = nil
- if not node then
- return nil
- end
- local value = instantValue(closure, proto)
- value.node = node
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.table' then
- road[#road+1] = noder.WEAK_TABLE_KEY
- local tkey = createValue(closure, proto.tkey, callback, road)
- road[#road] = nil
-
- road[#road+1] = noder.WEAK_ANY_FIELD
- local tvalue = createValue(closure, proto.tvalue, callback, road)
- road[#road] = nil
-
- if not tkey and not tvalue then
- return nil
- end
- local value = instantValue(closure, proto)
- value.tkey = tkey or proto.tkey
- value.tvalue = tvalue or proto.tvalue
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.ltable' then
- local fields = {}
- for i, field in ipairs(proto.fields) do
- fields[i] = createValue(closure, field, callback, road) or field
- end
- if #fields == 0 then
- return nil
- end
- local value = instantValue(closure, proto)
- value.fields = fields
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
- if proto.type == 'doc.type.field' then
- local name = proto.name[1]
- if type(name) == 'string' then
- road[#road+1] = ('%s%s'):format(
- noder.STRING_FIELD,
- name
- )
- else
- road[#road+1] = ('%s%s'):format(
- noder.SPLIT_CHAR,
- name
- )
- end
- local typeUnit = createValue(closure, proto.extends, callback, road)
- road[#road] = nil
- if not typeUnit then
- return nil
- end
- local value = instantValue(closure, proto)
- value.name = proto.name
- value.extends = typeUnit
- noder.compileNode(noder.getNoders(proto), value)
- return value
- end
-end
-
-local function buildValue(road, key, proto, param, upvalues)
- local paramID
- if proto.literal then
- local str = param.type == 'string' and param[1]
- if not str then
- return
- end
- paramID = 'dn:' .. str
- else
- paramID = noder.getID(param)
- end
- if not paramID then
- return
- end
- local myUri = guide.getUri(param)
- local myHead = noder.URI_CHAR .. myUri .. noder.URI_CHAR
- paramID = myHead .. paramID
- if not upvalues[key] then
- upvalues[key] = {}
- end
- upvalues[key][#upvalues[key]+1] = paramID .. table.concat(road)
-end
-
--- 为所有的 param 与 return 创建副本
----@param closure generic.closure
-local function buildValues(closure)
- local protoFunction = closure.proto
- local upvalues = closure.upvalues
- local params = closure.call.args
- local args = protoFunction.args
- local paramMap = {}
- if params then
- for i, param in ipairs(params) do
- local arg = args and args[i]
- if arg then
- if arg.type == 'local' then
- paramMap[arg[1]] = param
- elseif arg.type == 'doc.type.arg' then
- paramMap[arg.name[1]] = param
- end
- end
- end
- end
-
- if protoFunction.type == 'function' then
- for _, doc in ipairs(protoFunction.bindDocs) do
- if doc.type == 'doc.param' then
- local name = doc.param[1]
- local extends = doc.extends
- if name and extends then
- local param = paramMap[name]
- closure.params[name] = param and createValue(closure, extends, function (road, key, proto)
- buildValue(road, key, proto, param, upvalues)
- end) or extends
- end
- end
- end
- for _, doc in ipairs(protoFunction.bindDocs) do
- if doc.type == 'doc.return' then
- for _, rtn in ipairs(doc.returns) do
- closure.returns[rtn.returnIndex] = createValue(closure, rtn) or rtn
- end
- end
- end
- end
- if protoFunction.type == 'doc.type.function' then
- for index, arg in ipairs(protoFunction.args) do
- local name = arg.name[1]
- local extends = arg.extends
- local param = paramMap[name]
-
- closure.params[name] = param and createValue(closure, extends, function (road, key, proto)
- buildValue(road, key, proto, param, upvalues)
- end) or extends
- end
- for index, rtn in ipairs(protoFunction.returns) do
- closure.returns[index] = createValue(closure, rtn) or rtn
- end
- end
-end
-
----创建一个闭包
----@param proto parser.guide.object|generic.value # 原型函数|泛型值
----@return generic.closure
-function m.createClosure(proto, call)
- local protoFunction, parentClosure
- if proto.type == 'function' then
- protoFunction = proto
- elseif proto.type == 'doc.type.function' then
- protoFunction = proto
- elseif proto.type == 'generic.value' then
- protoFunction = proto.proto
- parentClosure = proto.closure
- end
- ---@type generic.closure
- local closure = {
- type = 'generic.closure',
- parent = protoFunction.parent,
- proto = protoFunction,
- call = call,
- upvalues = parentClosure and parentClosure.upvalues or {},
- params = {},
- returns = {},
- values = {},
- }
- buildValues(closure)
-
- if #closure.returns == 0 then
- return nil
- end
-
- noder.compileNode(noder.getNoders(proto), closure)
-
- return closure
-end
-
-return m
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index d694660b..a5b30cf3 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -6,7 +6,6 @@ local config = require 'config'
local lang = require 'language'
local util = require 'utility'
local guide = require 'parser.guide'
-local noder = require 'core.noder'
local rpath = require 'workspace.require-path'
local function collectRequire(mode, literal, uri)
diff --git a/script/core/infer.lua b/script/core/infer.lua
index 88028a6c..35a054ca 100644
--- a/script/core/infer.lua
+++ b/script/core/infer.lua
@@ -1,6 +1,4 @@
-local searcher = require 'core.searcher'
local config = require 'config'
-local noder = require 'core.noder'
local util = require 'utility'
local vm = require "vm.vm"
local guide = require "parser.guide"
@@ -398,7 +396,7 @@ function m.viewDocFunction(doc)
end
---显示对象的推断类型
----@param source parser.guide.object
+---@param source parser.object
---@param mark table
---@return string
local function searchInfer(source, infers, mark)
@@ -469,7 +467,7 @@ local function getCachedInfers(source, field)
end
---搜索对象的推断类型
----@param source parser.guide.object
+---@param source parser.object
---@param field? string
---@param mark? table
---@return string[]
@@ -513,7 +511,7 @@ function m.searchInfers(source, field, mark)
end
---搜索对象的字面量值
----@param source parser.guide.object
+---@param source parser.object
---@param field? string
---@param mark? table
---@return table
@@ -534,7 +532,7 @@ function m.searchLiterals(source, field, mark)
end
---搜索并显示推断值
----@param source parser.guide.object
+---@param source parser.object
---@param field? string
---@return string
function m.searchAndViewLiterals(source, field, mark)
@@ -550,7 +548,7 @@ function m.searchAndViewLiterals(source, field, mark)
end
---判断对象的推断值是否是 true
----@param source parser.guide.object
+---@param source parser.object
---@param mark? table
function m.isTrue(source, mark)
if not source then
@@ -596,7 +594,7 @@ function m.hasType(source, tp, mark)
end
---搜索并显示推断类型
----@param source parser.guide.object
+---@param source parser.object
---@param field? string
---@return string
function m.searchAndViewInfers(source, field, mark)
@@ -613,7 +611,7 @@ function m.searchAndViewInfers(source, field, mark)
end
---搜索并显示推断的class
----@param source parser.guide.object
+---@param source parser.object
---@return string?
function m.getClass(source)
if not source then
diff --git a/script/core/noder.lua b/script/core/noder.lua
deleted file mode 100644
index fcc6b6f4..00000000
--- a/script/core/noder.lua
+++ /dev/null
@@ -1,1914 +0,0 @@
-local util = require 'utility'
-local guide = require 'parser.guide'
-local collector = require 'core.collector' 'searcher'
-local files = require 'files'
-local config = require 'config'
-
-local tostring = tostring
-local error = error
-local ipairs = ipairs
-local type = type
-local next = next
-local log = log
-local ssub = string.sub
-local sformat = string.format
-local sgsub = string.gsub
-local smatch = string.match
-local sfind = string.find
-
-_ENV = nil
-
-local SPLIT_CHAR = '\x1F'
-local LAST_REGEX = SPLIT_CHAR .. '[^' .. SPLIT_CHAR .. ']*$'
-local FIRST_REGEX = '^[^' .. SPLIT_CHAR .. ']*'
-local HEAD_REGEX = '^' .. SPLIT_CHAR .. '?[^' .. SPLIT_CHAR .. ']*'
-local STRING_CHAR = '.'
-local ANY_FIELD_CHAR = '*'
-local INDEX_CHAR = '['
-local RETURN_INDEX = SPLIT_CHAR .. '#'
-local PARAM_INDEX = SPLIT_CHAR .. '&'
-local EVENT_ENUM = SPLIT_CHAR .. '>'
-local TABLE_KEY = SPLIT_CHAR .. '<'
-local WEAK_TABLE_KEY = SPLIT_CHAR .. '<<'
-local STRING_FIELD = SPLIT_CHAR .. STRING_CHAR
-local INDEX_FIELD = SPLIT_CHAR .. INDEX_CHAR
-local ANY_FIELD = SPLIT_CHAR .. ANY_FIELD_CHAR
-local WEAK_ANY_FIELD = SPLIT_CHAR .. ANY_FIELD_CHAR .. ANY_FIELD_CHAR
-local URI_CHAR = '@'
-local URI_REGEX = URI_CHAR .. '([^' .. URI_CHAR .. ']*)' .. URI_CHAR .. '(.*)'
-
-local INFO_DEEP = {
- deep = true,
-}
-local INFO_REJECT_SET = {
- reject = 'set',
-}
-local INFO_DEEP_AND_REJECT_SET = {
- reject = 'set',
- deep = true,
-}
-local INFO_META_INDEX = {
- filter = function (id, field)
- if field then
- return true
- end
- return ssub(id, 1, 2) ~= 'f:'
- end,
- filterValid = function (id, field)
- return not field
- end
-}
-local INFO_CLASS_TO_EXNTENDS = {
- filter = function (_, field, mode)
- return field ~= nil
- or mode == 'field'
- or mode == 'allfield'
- end,
- filterValid = function (_, field)
- return not field
- end,
- reject = 'set',
-}
-local INFO_DEEP_AND_DONT_CROSS = {
- deep = true,
- dontCross = true,
-}
-
----@alias node.id string
----@alias node.filter fun(id: string, field?: string):boolean
-
----@class noders
--- 使用该ID的单元
----@field source table<node.id, parser.guide.object>
--- 使用该ID的单元
----@field sources table<node.id, parser.guide.object[]>
--- 前进的关联ID
----@field forward table<node.id, node.id>
--- 第一个前进关联的info
----@field finfo? table<node.id, node.info>
--- 前进的关联ID与info
----@field forwards table<node.id, node.id[]|table<node.id, node.info>>
--- 后退的关联ID
----@field backward table<node.id, node.id>
--- 第一个后退关联的info
----@field binfo? table<node.id, node.info>
--- 后退的关联ID与info
----@field backwards table<node.id, node.id[]|table<node.id, node.info>>
--- 第一个继承
----@field extend table<node.id, node.id>
--- 其他继承
----@field extends table<node.id, node.id[]>
--- 函数调用参数信息(用于泛型)
----@field call table<node.id, parser.guide.object>
----@field require table<node.id, string>
----@field skip table<node.id, boolean>
-
----@class node.info
----@field reject? string
----@field deep? boolean
----@field filter? node.filter
----@field filterValid? node.filter
----@field dontCross? boolean
-
----如果对象是 arg self, 则认为 id 是 method 的 node
----@param source parser.guide.object
----@return nil
-local function getMethodNode(source)
- if source.type ~= 'local' or source[1] ~= 'self' then
- return nil
- end
- if source._mnode ~= nil then
- return source._mnode or nil
- end
- source._mnode = false
- local func = guide.getParentFunction(source)
- if not func then
- return
- end
- if func.isGeneric then
- return
- end
- if source.parent.type ~= 'funcargs' then
- return
- end
- local setmethod = func.parent
- if setmethod and ( setmethod.type == 'setmethod'
- or setmethod.type == 'setfield'
- or setmethod.type == 'setindex') then
- source._mnode = setmethod.node
- return setmethod.node
- end
-end
-
-local function getFieldEventName(field)
- if field._eventName then
- return field._eventName or nil
- end
- field._eventName = false
- local fieldType = field.extends
- if not fieldType then
- return nil
- end
- local docFunc = fieldType.types[1]
- if not docFunc or docFunc.type ~= 'doc.type.function' then
- return nil
- end
- local firstArg = docFunc.args and docFunc.args[1]
- if not firstArg then
- return nil
- end
- local secondArg
- if firstArg.name[1] == 'self' then
- firstArg = docFunc.args[2]
- if not firstArg then
- return nil
- end
- secondArg = docFunc.args[3]
- else
- secondArg = docFunc.args[2]
- end
- if not secondArg then
- return
- end
- local firstType = firstArg.extends
- if not firstType then
- return nil
- end
- local firstEnum = firstType.types[1]
- if not firstEnum then
- return nil
- end
- local secondType = secondArg.extends
- if not secondType then
- return nil
- end
- local secondTypeUnit = secondType.types[1]
- if not secondTypeUnit or secondTypeUnit.type ~= 'doc.type.function' then
- return nil
- end
- local enmuStr = firstEnum[1]
- if type(enmuStr) ~= 'string' then
- return nil
- end
- local eventName = enmuStr:match [[^['"](.+)['"]$]]
- field._eventName = eventName
- return eventName
-end
-
-local getKey, getID
-local getKeyMap = util.switch()
- : case 'local'
- : call(function (source)
- if source.parent.type == 'funcargs' then
- return 'p:' .. source.start, nil
- end
- return 'l:' .. source.start, nil
- end)
- : case 'setlocal'
- : case 'getlocal'
- : call(function (source)
- return getKey(source.node)
- end)
- : case 'setglobal'
- : case 'getglobal'
- : call(function (source)
- local node = source.node
- if node.tag == '_ENV' then
- return STRING_CHAR .. (source[1] or ''), nil
- else
- return STRING_CHAR .. (source[1] or ''), node
- end
- end)
- : case 'getfield'
- : case 'setfield'
- : call(function (source)
- return STRING_CHAR .. (source.field and source.field[1] or ''), source.node
- end)
- : case 'tablefield'
- : call(function (source)
- local t = source.parent
- local parent = t.parent
- local node
- if parent.value == t then
- node = parent
- else
- node = t
- end
- return STRING_CHAR .. (source.field and source.field[1] or ''), node
- end)
- : case 'getmethod'
- : case 'setmethod'
- : call(function (source)
- return STRING_CHAR .. (source.method and source.method[1] or ''), source.node
- end)
- : case 'setindex'
- : case 'getindex'
- : call(function (source)
- local index = source.index
- if not index then
- return INDEX_CHAR, source.node
- end
- if index.type == 'string' then
- return STRING_CHAR .. (index[1] or ''), source.node
- elseif index.type == 'boolean'
- or index.type == 'integer'
- or index.type == 'number' then
- return tostring(index[1] or ''), source.node
- else
- return INDEX_CHAR, source.node
- end
- end)
- : case 'tableindex'
- : call(function (source)
- local t = source.parent
- local parent = t.parent
- local node
- if parent.value == t then
- node = parent
- else
- node = t
- end
- local index = source.index
- if not index then
- return ANY_FIELD_CHAR, node
- end
- if index.type == 'string' then
- return STRING_CHAR .. (index[1] or ''), node
- elseif index.type == 'boolean'
- or index.type == 'integer'
- or index.type == 'number' then
- return tostring(index[1] or ''), node
- elseif index.type ~= 'function'
- and index.type ~= 'table' then
- return ANY_FIELD_CHAR, node
- end
- end)
- : case 'tableexp'
- : call(function (source)
- local t = source.parent
- local parent = t.parent
- local node
- if parent.value == t then
- node = parent
- else
- node = t
- end
- return tostring(source.tindex), node
- end)
- : case 'table'
- : call(function (source)
- return 't:' .. source.start, nil
- end)
- : case 'label'
- : call(function (source)
- return 'l:' .. source.start, nil
- end)
- : case 'goto'
- : call(function (source)
- if source.node then
- return 'l:' .. source.node.start, nil
- end
- return nil, nil
- end)
- : case 'function'
- : call(function (source)
- return 'f:' .. source.start, nil
- end)
- : case 'string'
- : call(function (source)
- return 'str:', nil
- end)
- : case 'integer'
- : call(function (source)
- return 'int:', nil
- end)
- : case 'number'
- : call(function (source)
- return 'num:', nil
- end)
- : case 'boolean'
- : call(function (source)
- return 'bool:', nil
- end)
- : case 'nil'
- : call(function (source)
- return 'nil:', nil
- end)
- : case '...'
- : call(function (source)
- return 'va:' .. source.start, nil
- end)
- : case 'varargs'
- : call(function (source)
- if source.node then
- return 'va:' .. source.node.start, nil
- end
- end)
- : case 'select'
- : call(function (source)
- return sformat('s:%d%s%d', source.start, RETURN_INDEX, source.sindex)
- end)
- : case 'call'
- : call(function (source)
- local node = source.node
- if node.special == 'rawget'
- or node.special == 'rawset' then
- if not source.args then
- return nil, nil
- end
- local tbl, key = source.args[1], source.args[2]
- if not tbl or not key then
- return nil, nil
- end
- if key.type == 'string' then
- return STRING_CHAR .. (key[1] or ''), tbl
- else
- return '', tbl
- end
- end
- return 'c:' .. source.finish, nil
- end)
- : case 'doc.class.name'
- : case 'doc.alias.name'
- : case 'doc.extends.name'
- : call(function (source)
- local name = source[1]
- return 'dn:' .. name, nil
- end)
- : case 'doc.type.name'
- : call(function (source)
- local name = source[1]
- if source.typeGeneric then
- local first = source.typeGeneric[name][1]
- if first then
- return 'dg:' .. first.start, nil
- end
- else
- return 'dn:' .. name, nil
- end
- end)
- : case 'doc.see.name'
- : call(function (source)
- local name = source[1]
- return 'dsn:' .. name, nil
- end)
- : case 'doc.class'
- : call(function (source)
- return 'dc:' .. source.start
- end)
- : case 'doc.type'
- : call(function (source)
- return 'dt:' .. source.start
- end)
- : case 'doc.param'
- : call(function (source)
- return 'dp:' .. source.start
- end)
- : case 'doc.vararg'
- : call(function (source)
- return 'dv:' .. source.start
- end)
- : case 'doc.field.name'
- : call(function (source)
- return 'dfn:' .. source.start
- end)
- : case 'doc.type.enum'
- : case 'doc.resume'
- : call(function (source)
- return 'de:' .. source.start
- end)
- : case 'doc.type.table'
- : call(function (source)
- return 'dtable:' .. source.start
- end)
- : case 'doc.type.ltable'
- : call(function (source)
- return 'dltable:' .. source.start
- end)
- : case 'doc.type.field'
- : call(function (source)
- return 'dfield:' .. source.start
- end)
- : case 'doc.type.array'
- : call(function (source)
- return 'darray:' .. source.finish
- end)
- : case 'doc.type.function'
- : call(function (source)
- return 'dfun:' .. source.start, nil
- end)
- : case 'doc.see.field'
- : call(function (source)
- return STRING_CHAR .. (source[1]), source.parent.name
- end)
- : case 'generic.closure'
- : call(function (source)
- return 'gc:' .. source.call.start, nil
- end)
- : case 'generic.value'
- : call(function (source)
- local tail = ''
- if guide.getUri(source.closure.call) ~= guide.getUri(source.proto) then
- tail = URI_CHAR .. guide.getUri(source.closure.call)
- end
- return sformat('gv:%s|%s%s'
- , source.closure.call.start
- , getKey(source.proto)
- , tail
- )
- end)
- : getMap()
-
----获取语法树单元的key
----@param source parser.guide.object
----@return string? key
----@return parser.guide.object? node
-function getKey(source)
- local f = getKeyMap[source.type]
- if f then
- return f(source)
- end
- return nil
-end
-
-local function getLocalValueID(source)
- if source.type ~= 'local' then
- return nil
- end
- local value = source.value
- if not value then
- return nil
- end
- local id = getID(value)
- if not id then
- return nil
- end
- local ct = id:sub(1, 2)
- if ct == 'g:'
- or ct == 'p:'
- or ct == 'l:' then
- return id
- end
- return nil
-end
-
-local function getNodeKey(source)
- if source.type == 'getlocal'
- or source.type == 'setlocal' then
- source = source.node
- end
- local methodNode = getMethodNode(source)
- if methodNode then
- return getNodeKey(methodNode)
- end
- if config.get(guide.getUri(source), 'Lua.IntelliSense.traceFieldInject') then
- local localValueID = getLocalValueID(source)
- if localValueID then
- return localValueID
- end
- end
- local key, node = getKey(source)
- if key and guide.isGlobal(source) then
- return 'g:' .. key, nil
- end
- return key, node
-end
-
----获取语法树单元的字符串ID
----@param source parser.guide.object
----@return string? id
-function getID(source)
- if not source then
- return nil
- end
- if source._id ~= nil then
- return source._id or nil
- end
- if source.type == 'field'
- or source.type == 'method' then
- source._id = false
- return nil
- end
- local current = source
- while current.type == 'paren' do
- current = current.exp
- if not current then
- source._id = false
- return nil
- end
- end
- local id, node = getNodeKey(current)
- if not id then
- source._id = false
- return nil
- end
- if node then
- local pid = getID(node)
- if not pid then
- source._id = false
- return nil
- end
- id = pid .. SPLIT_CHAR .. id
- end
- source._id = id
- return id
-end
-
----添加关联的前进ID
----@param noders noders
----@param id node.id
----@param forwardID node.id
----@param info? node.info
-local function pushForward(noders, id, forwardID, info)
- if not id
- or not forwardID
- or forwardID == ''
- or id == forwardID then
- return
- end
- if not noders.forward[id] then
- noders.forward[id] = forwardID
- noders.finfo[id] = info
- return
- end
- if noders.forward[id] == forwardID then
- return
- end
- local forwards = noders.forwards[id]
- if not forwards then
- forwards = {}
- noders.forwards[id] = forwards
- end
- if forwards[forwardID] ~= nil then
- return
- end
- forwards[forwardID] = info or false
- forwards[#forwards+1] = forwardID
-end
-
----添加关联的后退ID
----@param noders noders
----@param id node.id
----@param backwardID node.id
----@param info? node.info
-local function pushBackward(noders, id, backwardID, info)
- if not id
- or not backwardID
- or backwardID == ''
- or id == backwardID then
- return
- end
- if not noders.backward[id] then
- noders.backward[id] = backwardID
- noders.binfo[id] = info
- return
- end
- if noders.backward[id] == backwardID then
- return
- end
- local backwards = noders.backwards[id]
- if not backwards then
- backwards = {}
- noders.backwards[id] = backwards
- end
- if backwards[backwardID] ~= nil then
- return
- end
- backwards[backwardID] = info or false
- backwards[#backwards+1] = backwardID
-end
-
----添加继承的关联ID
----@param noders noders
----@param id node.id
----@param extendID node.id
-local function pushExtend(noders, id, extendID)
- if not id
- or not extendID
- or extendID == ''
- or id == extendID then
- return
- end
- if not noders.extend[id] then
- noders.extend[id] = extendID
- return
- end
- if noders.extend[id] == extendID then
- return
- end
- local extends = noders.extends[id]
- if not extends then
- extends = {}
- noders.extends[id] = extends
- end
- if extends[extendID] ~= nil then
- return
- end
- extends[extendID] = false
- extends[#extends+1] = extendID
-end
-
----@class noder
-local m = {}
-
-m.SPLIT_CHAR = SPLIT_CHAR
-m.STRING_CHAR = STRING_CHAR
-m.STRING_FIELD = STRING_FIELD
-m.RETURN_INDEX = RETURN_INDEX
-m.PARAM_INDEX = PARAM_INDEX
-m.TABLE_KEY = TABLE_KEY
-m.ANY_FIELD = ANY_FIELD
-m.URI_CHAR = URI_CHAR
-m.INDEX_FIELD = INDEX_FIELD
-m.WEAK_TABLE_KEY = WEAK_TABLE_KEY
-m.WEAK_ANY_FIELD = WEAK_ANY_FIELD
-
---- 寻找doc的主体
----@param obj parser.guide.object
----@return parser.guide.object
-local function getDocStateWithoutCrossFunction(obj)
- for _ = 1, 1000 do
- local parent = obj.parent
- if not parent then
- return obj
- end
- if parent.type == 'doc' then
- return obj
- end
- if parent.type == 'doc.type.function' then
- return nil
- end
- obj = parent
- end
- error('guide.getDocState overstack')
-end
-
-local dontPushSourceMap = util.arrayToHash {
- 'str:', 'nil:', 'num:', 'int:', 'bool:'
-}
-
----添加关联单元
----@param noders noders
----@param source parser.guide.object
-function m.pushSource(noders, source, id)
- id = id or getID(source)
- if not id then
- return
- end
- if dontPushSourceMap[id] then
- return
- end
- if not noders.source[id] then
- noders.source[id] = source
- return
- end
- local sources = noders.sources[id]
- if not sources then
- sources = {}
- noders.sources[id] = sources
- end
- sources[#sources+1] = source
-end
-
-local DUMMY_FUNCTION = function () end
-
----遍历关联单元
----@param noders noders
----@param id node.id
----@return fun():parser.guide.object
-function m.eachSource(noders, id)
- local source = noders.source[id]
- if not source then
- return DUMMY_FUNCTION
- end
- local index
- local sources = noders.sources[id]
- return function ()
- if not index then
- index = 0
- return source
- end
- if not sources then
- return nil
- end
- index = index + 1
- return sources[index]
- end
-end
-
----遍历forward
----@param noders noders
----@param id node.id
----@return fun():string, node.info
-function m.eachForward(noders, id)
- local forward = noders.forward[id]
- if not forward then
- return DUMMY_FUNCTION
- end
- local index
- local forwards = noders.forwards[id]
- return function ()
- if not index then
- index = 0
- return forward, noders.finfo[id]
- end
- if not forwards then
- return nil
- end
- index = index + 1
- local id = forwards[index]
- local tag = forwards[id]
- return id, tag
- end
-end
-
----遍历backward
----@param noders noders
----@param id node.id
----@return fun():string, node.info
-function m.eachBackward(noders, id)
- local backward = noders.backward[id]
- if not backward then
- return DUMMY_FUNCTION
- end
- local index
- local backwards = noders.backwards[id]
- return function ()
- if not index then
- index = 0
- return backward, noders.binfo[id]
- end
- if not backwards then
- return nil
- end
- index = index + 1
- local id = backwards[index]
- local tag = backwards[id]
- return id, tag
- end
-end
-
----遍历extend
----@param noders noders
----@param id node.id
----@return fun():string, node.info
-function m.eachExtend(noders, id)
- local extend = noders.extend[id]
- if not extend then
- return DUMMY_FUNCTION
- end
- local index
- local extends = noders.extends[id]
- return function ()
- if not index then
- index = 0
- return extend
- end
- if not extends then
- return nil
- end
- index = index + 1
- local id = extends[index]
- return id
- end
-end
-
-local function bindValue(noders, source, id)
- local value = source.value
- if not value then
- return
- end
- local valueID = getID(value)
- if not valueID then
- return
- end
-
- local bindDocs = source.bindDocs
- if source.type == 'getlocal'
- or source.type == 'setlocal' then
- if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceLocalSet') then
- return
- end
- bindDocs = source.node.bindDocs
- end
- if bindDocs and value.type ~= 'table' then
- for _, doc in ipairs(bindDocs) do
- if doc.type == 'doc.class'
- or doc.type == 'doc.type' then
- return
- end
- end
- end
- -- x = y : x -> y
- pushForward(noders, id, valueID, INFO_REJECT_SET)
- if not config.get(guide.getUri(source), 'Lua.IntelliSense.traceBeSetted')
- and source.type ~= 'local' then
- return
- end
- -- 参数/call禁止反向查找赋值
- local valueType = smatch(valueID, '^(.-:).')
- if not valueType then
- return
- end
- pushBackward(noders, valueID, id, INFO_DEEP_AND_REJECT_SET)
-end
-
-local function compileCallParam(noders, call, sourceID)
- if not sourceID then
- return
- end
- if not call.args then
- return
- end
- local node = call.node
- local fixIndex = 0
- if call.node.special == 'pcall' then
- fixIndex = 1
- node = call.args[1]
- elseif call.node.special == 'xpcall' then
- fixIndex = 2
- node = call.args[1]
- end
- local nodeID = getID(node)
- if not nodeID then
- return
- end
- local methodIndex = 0
- if node.type == 'getmethod' then
- fixIndex = fixIndex + 1
- methodIndex = 1
- end
- local eventNodeID
- for firstIndex, callArg in ipairs(call.args) do
- firstIndex = firstIndex - fixIndex
- if firstIndex == 1 and callArg.type == 'string' then
- if callArg[1] then
- eventNodeID = sformat('%s%s%s'
- , nodeID
- , EVENT_ENUM
- , callArg[1]
- )
- end
- end
- if firstIndex > 0 and callArg.type == 'function' then
- if callArg.args then
- for secondIndex, funcParam in ipairs(callArg.args) do
- local paramID = sformat('%s%s%s%s%s'
- , nodeID
- , PARAM_INDEX
- , firstIndex + methodIndex
- , PARAM_INDEX
- , secondIndex
- )
- pushForward(noders, getID(funcParam), paramID)
- if eventNodeID then
- local eventParamID = sformat('%s%s%s%s%s'
- , eventNodeID
- , PARAM_INDEX
- , firstIndex + methodIndex
- , PARAM_INDEX
- , secondIndex
- )
- pushForward(noders, getID(funcParam), eventParamID)
- end
- end
- end
- end
- if callArg.type == 'table' then
- local paramID = sformat('%s%s%s'
- , nodeID
- , PARAM_INDEX
- , firstIndex + methodIndex
- )
- pushForward(noders, getID(callArg), paramID)
- end
- end
-end
-
-local function compileCallReturn(noders, call, sourceID, returnIndex)
- if not sourceID then
- return
- end
- local node = call.node
- local nodeID = getID(node)
- if not nodeID then
- return
- end
- local callID = getID(call)
- if not callID then
- return
- end
- -- 将setmetatable映射到 param1 以及 param2.__index 上
- if node.special == 'setmetatable' then
- local tblID = getID(call.args and call.args[1])
- local metaID = getID(call.args and call.args[2])
- local indexID
- if metaID then
- indexID = sformat('%s%s%s'
- , metaID
- , STRING_FIELD
- , '__index'
- )
- end
- pushForward(noders, sourceID, tblID)
- pushForward(noders, sourceID, indexID, INFO_META_INDEX)
- pushBackward(noders, tblID, sourceID)
- --pushBackward(noders, indexID, callID)
- return
- end
- if node.special == 'require' then
- local arg1 = call.args and call.args[1]
- if arg1 and arg1.type == 'string' then
- noders.require[sourceID] = arg1[1]
- end
- pushBackward(noders, callID, sourceID, INFO_DEEP)
- return
- end
- if node.special == 'pcall'
- or node.special == 'xpcall' then
- local index = returnIndex - 1
- if index <= 0 then
- return
- end
- local funcID = call.args and getID(call.args[1])
- if not funcID then
- return
- end
- local pfuncXID = sformat('%s%s%s'
- , funcID
- , RETURN_INDEX
- , index
- )
- pushForward(noders, sourceID, pfuncXID)
- pushBackward(noders, pfuncXID, sourceID, INFO_DEEP)
- return
- end
- local funcXID = sformat('%s%s%s'
- , nodeID
- , RETURN_INDEX
- , returnIndex
- )
- noders.call[sourceID] = call
- pushForward(noders, sourceID, funcXID)
- pushBackward(noders, funcXID, sourceID, INFO_DEEP)
-end
-
-local specialMap = util.arrayToHash {
- 'require', 'dofile', 'loadfile',
- 'rawset', 'rawget', 'setmetatable',
-}
-
-local compileNodeMap
-compileNodeMap = util.switch()
- : case 'string'
- : call(function (noders, id, source)
- pushForward(noders, id, 'str:')
- end)
- : case 'boolean'
- : call(function (noders, id, source)
- pushForward(noders, id, 'dn:boolean')
- end)
- : case 'number'
- : call(function (noders, id, source)
- pushForward(noders, id, 'dn:number')
- end)
- : case 'integer'
- : call(function (noders, id, source)
- pushForward(noders, id, 'dn:integer')
- end)
- : case 'nil'
- : call(function (noders, id, source)
- pushForward(noders, id, 'dn:nil')
- end)
- : case 'doc.type'
- : call(function (noders, id, source)
- if source.bindSources then
- for _, src in ipairs(source.bindSources) do
- if src.parent.type ~= 'funcargs'
- and not src.dummy then
- pushForward(noders, getID(src), id)
- end
- end
- end
- for _, typeUnit in ipairs(source.types) do
- local unitID = getID(typeUnit)
- pushForward(noders, id, unitID)
- if source.bindSources then
- for _, src in ipairs(source.bindSources) do
- if src.parent.type ~= 'funcargs'
- and not src.dummy then
- pushBackward(noders, unitID, getID(src))
- end
- end
- end
- end
- end)
- : case 'doc.type.table'
- : call(function (noders, id, source)
- if source.node then
- pushForward(noders, id, getID(source.node), INFO_CLASS_TO_EXNTENDS)
- end
- if source.tkey then
- local keyID = id .. TABLE_KEY
- pushForward(noders, keyID, getID(source.tkey))
- end
- if source.tvalue then
- local valueID = id .. ANY_FIELD
- pushForward(noders, valueID, getID(source.tvalue))
- end
- end)
- : case 'doc.type.ltable'
- : call(function (noders, id, source)
- local firstField = source.fields[1]
- if not firstField then
- return
- end
- local keyID = id .. WEAK_TABLE_KEY
- local valueID = id .. WEAK_ANY_FIELD
- pushForward(noders, keyID, 'dn:string')
- pushForward(noders, valueID, getID(firstField.extends))
- for _, field in ipairs(source.fields) do
- local fname = field.name[1]
- local extendsID
- if type(fname) == 'string' then
- extendsID = sformat('%s%s%s'
- , id
- , STRING_FIELD
- , fname
- )
- else
- extendsID = sformat('%s%s%s'
- , id
- , SPLIT_CHAR
- , fname
- )
- end
- pushForward(noders, extendsID, getID(field))
- pushForward(noders, extendsID, getID(field.extends))
- end
- end)
- : case 'doc.type.array'
- : call(function (noders, id, source)
- if source.node then
- local nodeID = id .. ANY_FIELD
- pushForward(noders, nodeID, getID(source.node))
- end
- local keyID = id .. TABLE_KEY
- pushForward(noders, keyID, 'dn:integer')
- end)
- : case 'doc.alias'
- : call(function (noders, id, source)
- pushForward(noders, getID(source.alias), getID(source.extends))
- end)
- : case 'doc.class'
- : call(function (noders, id, source)
- pushForward(noders, id, getID(source.class))
- pushForward(noders, getID(source.class), id)
- if source.extends then
- for _, ext in ipairs(source.extends) do
- pushExtend(noders, id, getID(ext))
- end
- end
- if source.bindSources then
- for _, src in ipairs(source.bindSources) do
- if src.parent.type ~= 'funcargs'
- and src.type ~= 'setmethod'
- and not src.dummy then
- pushForward(noders, getID(src), id)
- pushForward(noders, id, getID(src))
- end
- end
- end
- for _, field in ipairs(source.fields) do
- local key = field.field[1]
- if key then
- local keyID
- if type(key) == 'string' then
- keyID = sformat('%s%s%s'
- , id
- , STRING_FIELD
- , key
- )
- local eventName = getFieldEventName(field)
- if eventName then
- keyID = sformat('%s%s%s'
- , keyID
- , EVENT_ENUM
- , eventName
- )
- end
- else
- keyID = sformat('%s%s%s'
- , id
- , SPLIT_CHAR
- , key
- )
- end
- pushForward(noders, keyID, getID(field.field))
- pushForward(noders, getID(field.field), keyID)
- pushForward(noders, keyID, getID(field.extends))
- end
- end
- end)
- : case 'doc.module'
- : call(function (noders, id, source)
- if not source.module then
- return
- end
- for _, src in ipairs(source.bindSources) do
- if guide.isSet(src) then
- local sourceID = getID(src)
- if sourceID then
- noders.require[sourceID] = source.module
- end
- end
- end
- end)
- : case 'doc.param'
- : call(function (noders, id, source)
- pushForward(noders, id, getID(source.extends))
- for _, src in ipairs(source.bindSources) do
- if src.type == 'local' and src.parent.type == 'in' then
- pushForward(noders, getID(src), id)
- end
- end
- if source.bindSources then
- for _, src in ipairs(source.bindSources) do
- if src.type == 'function' and src.args then
- for _, arg in ipairs(src.args) do
- if arg[1] == source.param[1] then
- pushForward(noders, getID(arg), id)
- end
- end
- end
- end
- end
- end)
- : case 'doc.vararg'
- : call(function (noders, id, source)
- pushForward(noders, getID(source), getID(source.vararg))
- end)
- : case 'doc.see'
- : call(function (noders, id, source)
- local nameID = getID(source.name)
- local classID = sgsub(nameID, '^dsn:', 'dn:')
- pushForward(noders, nameID, classID)
- if source.field then
- local fieldID = getID(source.field)
- local fieldClassID = sgsub(fieldID, '^dsn:', 'dn:')
- pushForward(noders, fieldID, fieldClassID)
- end
- end)
- : case 'call'
- : call(function (noders, id, source)
- if source.parent.type ~= 'select' then
- compileCallReturn(noders, source, id, 1)
- end
- compileCallParam(noders, source, id)
- end)
- : case 'select'
- : call(function (noders, id, source)
- if source.vararg.type == 'call' then
- local call = source.vararg
- compileCallReturn(noders, call, id, source.sindex)
- end
- if source.vararg.type == 'varargs' then
- pushForward(noders, id, getID(source.vararg))
- end
- end)
- : case 'doc.type.function'
- : call(function (noders, id, source)
- if source.args then
- for index, param in ipairs(source.args) do
- local indexID = sformat('%s%s%s'
- , id
- , PARAM_INDEX
- , index
- )
- pushForward(noders, indexID, getID(param.extends))
- end
- end
- if source.returns then
- for index, rtn in ipairs(source.returns) do
- local returnID = sformat('%s%s%s'
- , id
- , RETURN_INDEX
- , index
- )
- pushForward(noders, returnID, getID(rtn))
- end
- end
- -- @type fun(x: T):T 的情况
- local docType = getDocStateWithoutCrossFunction(source)
- if docType and docType.type == 'doc.type' then
- guide.eachSourceType(source, 'doc.type.name', function (typeName)
- if typeName.typeGeneric then
- source.isGeneric = true
- return false
- end
- end)
- end
- end)
- : case 'doc.type.name'
- : call(function (noders, id, source)
- local uri = guide.getUri(source)
- collector:subscribe(uri, id, noders)
- end)
- : case 'doc.class.name'
- : case 'doc.alias.name'
- : call(function (noders, id, source)
- local uri = guide.getUri(source)
- collector:subscribe(uri, id, noders)
-
- local defID = 'def:' .. id
- collector:subscribe(uri, defID, noders)
-
- local defAnyID = 'def:dn:'
- collector:subscribe(uri, defAnyID, noders)
- end)
- : case 'function'
- : call(function (noders, id, source)
- local hasDocReturn
- -- 检查 luadoc
- if source.bindDocs then
- for _, doc in ipairs(source.bindDocs) do
- if doc.type == 'doc.return' then
- hasDocReturn = true
- end
- if doc.type == 'doc.vararg' then
- if source.args then
- for _, param in ipairs(source.args) do
- if param.type == '...' then
- pushForward(noders, getID(param), getID(doc))
- end
- end
- end
- end
- if doc.type == 'doc.generic' then
- source.isGeneric = true
- end
- if doc.type == 'doc.overload' then
- pushForward(noders, id, getID(doc.overload))
- end
- end
- end
- if source.args then
- local parent = source.parent
- local parentID = guide.isSet(parent) and getID(parent)
- for i, arg in ipairs(source.args) do
- if arg[1] == 'self' then
- goto CONTINUE
- end
- local indexID = sformat('%s%s%s'
- , id
- , PARAM_INDEX
- , i
- )
- pushForward(noders, indexID, getID(arg))
- if arg.type ~= 'local' then
- for j = i + 1, i + 10 do
- pushForward(noders, sformat('%s%s%s'
- , id
- , PARAM_INDEX
- , j
- ), getID(arg))
- end
- end
- ::CONTINUE::
- end
- end
- -- 检查实体返回值
- if source.returns and not hasDocReturn then
- for _, rtn in ipairs(source.returns) do
- for index, rtnObj in ipairs(rtn) do
- local returnID = sformat('%s%s%s'
- , id
- , RETURN_INDEX
- , index
- )
- pushForward(noders, returnID, getID(rtnObj))
- if config.get(guide.getUri(source), 'Lua.IntelliSense.traceReturn') then
- pushBackward(noders, getID(rtnObj), returnID, INFO_DEEP_AND_DONT_CROSS)
- end
- end
- end
- end
- end)
- : case 'table'
- : call(function (noders, id, source)
- local firstField = source[1]
- if firstField then
- if firstField.type == 'varargs' then
- local keyID = id .. TABLE_KEY
- local valueID = id .. ANY_FIELD
- source.array = firstField
- pushForward(noders, keyID, 'dn:integer')
- pushForward(noders, valueID, getID(firstField))
- else
- local keyID = id .. WEAK_TABLE_KEY
- local valueID = id .. WEAK_ANY_FIELD
- if firstField.type == 'tablefield' then
- pushForward(noders, keyID, 'dn:string')
- pushForward(noders, valueID, getID(firstField.value))
- elseif firstField.type == 'tableindex' then
- pushForward(noders, keyID, getID(firstField.index))
- pushForward(noders, valueID, getID(firstField.value))
- elseif firstField.type == 'tableexp' then
- pushForward(noders, keyID, 'dn:integer')
- pushForward(noders, valueID, getID(firstField))
- end
- end
- end
- local parent = source.parent
- if guide.isSet(parent) then
- pushForward(noders, id, getID(parent))
- end
- end)
- : case 'loop'
- : call(function (noders, id, source)
- local loc = source.loc
- if loc then
- pushForward(noders, getID(loc), 'dn:integer')
- end
- end)
- : case 'in'
- : call(function (noders, id, source)
- local keys = source.keys
- ---@type parser.guide.object[]
- local exps = source.exps
- if not keys or not exps then
- return
- end
- local node = exps[1]
- local param1 = exps[2]
- local param2 = exps[3]
- if node.type == 'call' then
- if not param1 then
- param1 = {
- type = 'select',
- dummy = true,
- sindex = 2,
- start = node.start,
- finish = node.finish,
- vararg = node,
- parent = source,
- }
- compileCallReturn(noders, node, getID(param1), 2)
- if not param2 then
- param2 = {
- type = 'select',
- dummy = true,
- sindex = 3,
- start = node.start,
- finish = node.finish,
- vararg = node,
- parent = source,
- }
- compileCallReturn(noders, node, getID(param2), 3)
- end
- end
- end
- local call = {
- type = 'call',
- dummy = true,
- start = source.keyword[3],
- finish = exps[#exps].finish,
- node = node,
- args = { param1, param2 },
- parent = source,
- }
- for i = 1, #keys do
- compileCallReturn(noders, call, getID(keys[i]), i)
- end
- end)
- : case 'main'
- : call(function (noders, id, source)
- if source.returns then
- for _, rtn in ipairs(source.returns) do
- local rtnObj = rtn[1]
- if rtnObj then
- pushForward(noders, 'mainreturn', getID(rtnObj), INFO_REJECT_SET)
- pushBackward(noders, getID(rtnObj), 'mainreturn', INFO_DEEP_AND_REJECT_SET)
- end
- end
- end
- end)
- : case 'doc.return'
- : call(function (noders, id, source)
- if not source.bindSources then
- return
- end
- for _, rtn in ipairs(source.returns) do
- for _, src in ipairs(source.bindSources) do
- if src.type == 'function' then
- local fullID = sformat('%s%s%s'
- , getID(src)
- , RETURN_INDEX
- , rtn.returnIndex
- )
- pushForward(noders, fullID, getID(rtn))
- for _, typeUnit in ipairs(rtn.types) do
- pushBackward(noders, getID(typeUnit), fullID, INFO_DEEP_AND_DONT_CROSS)
- end
- end
- end
- end
- end)
- : case 'generic.closure'
- : call(function (noders, id, source)
- for i, rtn in ipairs(source.returns) do
- local closureID = sformat('%s%s%s'
- , id
- , RETURN_INDEX
- , i
- )
- local returnID = getID(rtn)
- pushForward(noders, closureID, returnID)
- end
- end)
- : case 'generic.value'
- : call(function (noders, id, source)
- local proto = source.proto
- local closure = source.closure
- local upvalues = closure.upvalues
- if proto.type == 'doc.type.name' then
- local key = proto[1]
- if upvalues[key] then
- for _, paramID in ipairs(upvalues[key]) do
- pushForward(noders, id, paramID)
- end
- end
- end
- local f = compileNodeMap[proto.type]
- if f then
- f(noders, id, source)
- end
- end)
- : getMap()
-
----@param noders noders
----@param source parser.guide.object
----@return parser.guide.object[]
-function m.compileNode(noders, source)
- if source._noded then
- return
- end
- source._noded = true
- m.pushSource(noders, source)
- local id = getID(source)
- bindValue(noders, source, id)
-
- if id and specialMap[source.special] then
- noders.skip[id] = true
- end
-
- local f = compileNodeMap[source.type]
- if f then
- f(noders, id, source)
- end
-
- if id and ssub(id, 1, 2) == 'g:' then
- local uri = guide.getUri(source)
- collector:subscribe(uri, id, noders)
- if guide.isSet(source)
- -- local t = Global --> t: g:.Global
- and source.type ~= 'local'
- and source.type ~= 'setlocal' then
-
- local defID = 'def:' .. id
- collector:subscribe(uri, defID, noders)
-
- local fieldID = m.getLastID(id)
- if fieldID then
- local defNodeID = 'field:' .. fieldID
- collector:subscribe(uri, defNodeID, noders)
- end
-
- if guide.isGlobal(source) then
- local defAnyID = 'def:g:'
- collector:subscribe(uri, defAnyID, noders)
- end
- end
- end
-end
-
----根据ID来获取第一个节点的ID
----@param id string
----@return string
-function m.getFirstID(id)
- local firstID, count = smatch(id, FIRST_REGEX)
- if count == 0 then
- return nil
- end
- if firstID == '' then
- return nil
- end
- return firstID
-end
-
----根据ID来获取第一个节点的ID或field
----@param id string
----@return string
-function m.getHeadID(id)
- local headID, count = smatch(id, HEAD_REGEX)
- if count == 0 then
- return nil
- end
- if headID == '' then
- return nil
- end
- return headID
-end
-
----根据ID来获取上个节点的ID
----@param id string
----@return string
-function m.getLastID(id)
- local lastID, count = sgsub(id, LAST_REGEX, '')
- if count == 0 then
- return nil
- end
- if lastID == '' then
- return nil
- end
- return lastID
-end
-
-function m.getFieldID(id)
- local fieldID = smatch(id, LAST_REGEX)
- return fieldID
-end
-
----获取ID的长度
----@param id string
----@return integer
-function m.getIDLength(id)
- if not id then
- return 0
- end
- local _, count = sgsub(id, SPLIT_CHAR, SPLIT_CHAR)
- return count + 1
-end
-
----测试id是否包含field,如果遇到函数调用则中断
----@param id string
----@return boolean
-function m.hasField(id)
- local firstID = m.getFirstID(id)
- if firstID == id or not firstID then
- return false
- end
- local nextChar = ssub(id, #firstID + 1, #firstID + 1)
- if nextChar ~= SPLIT_CHAR then
- return false
- end
- local next2Char = ssub(id, #firstID + 2, #firstID + 2)
- if next2Char == RETURN_INDEX
- or next2Char == PARAM_INDEX then
- return false
- end
- return true
-end
-
----把形如 `@file:\\\XXXXX@gv:1|1`拆分成uri与id
----@param id string
----@return uri? string
----@return string id
-function m.getUriAndID(id)
- local uri, newID = smatch(id, URI_REGEX)
- return uri, newID
-end
-
----是否是普通的field,例如数字或字符串,而不是函数返回值等
----@param field any
-function m.isCommonField(field)
- if not field then
- return false
- end
- if ssub(field, 1, #RETURN_INDEX) == RETURN_INDEX then
- return false
- end
- return true
-end
-
----是否是普通的field,例如数字或字符串,而不是函数返回值等
-function m.hasCall(field)
- if not field then
- return false
- end
- if sfind(field, RETURN_INDEX, 1, true) then
- return true
- end
- return false
-end
-
-function m.isGlobalID(id)
- return ssub(id, 1, 2) == 'g:'
- or ssub(id, 1, 3) == 'dn:'
-end
-
----获取source的ID
----@param source parser.guide.object
----@return string
-function m.getID(source)
- return getID(source)
-end
-
----获取source的key
----@param source parser.guide.object
----@return string
-function m.getKey(source)
- return getKey(source)
-end
-
----清除临时id(用于泛型的临时对象)
----@param noders noders
----@param id string
-function m.removeID(noders, id)
- if not id then
- return
- end
- for _, t in next, noders do
- t[id] = nil
- end
-end
-
----寻找doc的主体
----@param doc parser.guide.object
-function m.getDocState(doc)
- return getDocStateWithoutCrossFunction(doc)
-end
-
----@param noders noders
----@return fun():node.id
-function m.eachID(noders)
- return next, noders.source
-end
-
-m.getFieldEventName = getFieldEventName
-
----获取对象的noders
----@param source parser.guide.object
----@return noders
-function m.getNoders(source)
- local root = guide.getRoot(source)
- if not root._noders then
- ---@type noders
- root._noders = {
- source = {},
- sources = {},
- forward = {},
- finfo = {},
- forwards = {},
- backward = {},
- binfo = {},
- backwards = {},
- extend = {},
- extends = {},
- call = {},
- require = {},
- skip = {},
- }
- end
- return root._noders
-end
-
----获取对象的noders
----@param uri uri
----@return noders
-function m.getNodersByUri(uri)
- local state = files.getState(uri)
- if not state then
- return nil
- end
- return m.getNoders(state.ast)
-end
-
----编译整个文件的node
----@param source parser.guide.object
----@return table
-function m.compileAllNodes(source)
- local root = guide.getRoot(source)
- local noders = m.getNoders(source)
- if root._initedNoders then
- return noders
- end
- root._initedNoders = true
- if not root._compiledGlobals then
- collector:dropUri(guide.getUri(root))
- end
- root._compiledGlobals = true
- --log.debug('compileNodes:', guide.getUri(root))
- guide.eachSource(root, function (src)
- m.compileNode(noders, src)
- end)
- --log.debug('compileNodes finish:', guide.getUri(root))
- return noders
-end
-
-local partNodersMap = util.switch()
- : case 'local'
- : call(function (noders, source)
- local refs = source.ref
- if refs then
- for i = 1, #refs do
- local ref = refs[i]
- m.compilePartNodes(noders, ref)
- end
- end
- end)
- : case 'setlocal'
- : case 'getlocal'
- : call(function (noders, source)
- m.compilePartNodes(noders, source.node)
-
- local nxt = source.next
- if nxt then
- m.compilePartNodes(noders, nxt)
- end
-
- local parent = source.parent
- if parent.value == source then
- m.compilePartNodes(noders, parent)
- end
-
- if parent.type == 'call' then
- local node = parent.node
- if node.special == 'rawset'
- or node.special == 'rawget' then
- m.compilePartNodes(noders, parent)
- end
- end
- end)
- : case 'setfield'
- : case 'getfield'
- : case 'setmethod'
- : case 'getmethod'
- : call(function (noders, source)
- local node = source.node
- m.compilePartNodes(noders, node)
-
- local nxt = source.next
- if nxt then
- m.compilePartNodes(noders, nxt)
- end
-
- local parent = source.parent
- if parent.value == source then
- m.compilePartNodes(noders, parent)
- end
- end)
- : case 'setglobal'
- : case 'getglobal'
- : call(function (noders, source)
- local nxt = source.next
- if nxt then
- m.compilePartNodes(noders, nxt)
- end
-
- local parent = source.parent
- if parent.value == source then
- m.compilePartNodes(noders, parent)
- end
-
- if parent.type == 'call' then
- local node = parent.node
- if node.special == 'rawset'
- or node.special == 'rawget' then
- m.compilePartNodes(noders, parent)
- end
- end
- end)
- : case 'label'
- : call(function (noders, source)
- local refs = source.ref
- if not refs then
- return
- end
- for i = 1, #refs do
- local ref = refs[i]
- m.compilePartNodes(noders, ref)
- end
- end)
- : case 'goto'
- : call(function (noders, source)
- m.compilePartNodes(noders, source.node)
- end)
- : case 'table'
- : call(function (noders, source)
- for i = 1, #source do
- local field = source[i]
- m.compilePartNodes(noders, field)
- end
- end)
- : case 'tablefield'
- : case 'tableindex'
- : call(function (noders, source)
- m.compilePartNodes(noders, source.parent)
- end)
- : getMap()
-
----编译Class的node
----@param noders noders
----@param source parser.guide.object
----@return table
-function m.compilePartNodes(noders, source)
- if not source then
- return
- end
- if source._noded then
- return
- end
- m.compileNode(noders, source)
- local f = partNodersMap[source.type]
- if f then
- f(noders, source)
- end
-end
-
----编译全局变量的node
----@param root parser.guide.object
----@return table
-function m.compileGlobalNodes(root)
- if root._initedNoders then
- return
- end
- if not root._compiledGlobals then
- collector:dropUri(guide.getUri(root))
- end
- root._compiledGlobals = true
- local noders = m.getNoders(root)
- local env = guide.getENV(root)
-
- m.compilePartNodes(noders, env)
-
- local docs = root.docs
- guide.eachSourceTypes(docs, {
- 'doc.class.name',
- 'doc.alias.name',
- 'doc.type.name',
- }, function (doc)
- m.compileNode(noders, doc)
- end)
-end
-
-for uri in files.eachFile() do
- local state = files.getState(uri)
- if state then
- m.compileGlobalNodes(state.ast)
- end
-end
-
-files.watch(function (ev, uri)
- if ev == 'update' then
- local state = files.getState(uri)
- if state then
- m.compileGlobalNodes(state.ast)
- end
- end
- if ev == 'remove' then
- collector:dropUri(uri)
- end
-end)
-
-return m
diff --git a/script/core/reference.lua b/script/core/reference.lua
index 5e4a4cbf..60a25940 100644
--- a/script/core/reference.lua
+++ b/script/core/reference.lua
@@ -1,4 +1,3 @@
-local searcher = require 'core.searcher'
local guide = require 'parser.guide'
local files = require 'files'
local vm = require 'vm'
@@ -66,22 +65,12 @@ return function (uri, position)
local metaSource = vm.isMetaFile(uri)
local refs = vm.getAllRefs(source)
- local values = {}
- for _, src in ipairs(refs) do
- local value = searcher.getObjectValue(src)
- if value and value ~= src and guide.isLiteral(value) then
- values[value] = true
- end
- end
local results = {}
for _, src in ipairs(refs) do
if src.dummy then
goto CONTINUE
end
- if values[src] then
- goto CONTINUE
- end
local root = guide.getRoot(src)
if not root then
goto CONTINUE
diff --git a/script/core/rename.lua b/script/core/rename.lua
index 69021197..c8e011b9 100644
--- a/script/core/rename.lua
+++ b/script/core/rename.lua
@@ -1,11 +1,8 @@
local files = require 'files'
local vm = require 'vm'
-local proto = require 'proto'
-local define = require 'proto.define'
local util = require 'utility'
local findSource = require 'core.find-source'
local guide = require 'parser.guide'
-local noder = require 'core.noder'
local Forcing
diff --git a/script/core/searcher.lua b/script/core/searcher.lua
deleted file mode 100644
index eb70c349..00000000
--- a/script/core/searcher.lua
+++ /dev/null
@@ -1,1350 +0,0 @@
-local noder = require 'core.noder'
-local guide = require 'parser.guide'
-local files = require 'files'
-local generic = require 'core.generic'
-local rpath = require 'workspace.require-path'
-local vm = require 'vm.vm'
-local collector = require 'core.collector' 'searcher'
-local util = require 'utility'
-
-local TRACE = TRACE
-local FOOTPRINT = FOOTPRINT
-local TEST = TEST
-local log = log
-local select = select
-local tostring = tostring
-local next = next
-local error = error
-local type = type
-local setmetatable = setmetatable
-local ipairs = ipairs
-local tconcat = table.concat
-local ssub = string.sub
-local sfind = string.find
-local sformat = string.format
-
-local getUri = guide.getUri
-
-local getState = files.getState
-
-local getNoders = noder.getNoders
-local getID = noder.getID
-local getLastID = noder.getLastID
-local removeID = noder.removeID
-local getNodersByUri = noder.getNodersByUri
-local getFirstID = noder.getFirstID
-local getHeadID = noder.getHeadID
-local eachForward = noder.eachForward
-local getUriAndID = noder.getUriAndID
-local eachBackward = noder.eachBackward
-local eachExtend = noder.eachExtend
-local eachSource = noder.eachSource
-local compileAllNodes = noder.compileAllNodes
-local hasCall = noder.hasCall
-
-local SPLIT_CHAR = noder.SPLIT_CHAR
-local RETURN_INDEX = noder.RETURN_INDEX
-local TABLE_KEY = noder.TABLE_KEY
-local STRING_CHAR = noder.STRING_CHAR
-local STRING_FIELD = noder.STRING_FIELD
-local WEAK_TABLE_KEY = noder.WEAK_TABLE_KEY
-local ANY_FIELD = noder.ANY_FIELD
-local WEAK_ANY_FIELD = noder.WEAK_ANY_FIELD
-
-_ENV = nil
-
-local ignoredSources = {
- ['int:'] = true,
- ['num:'] = true,
- ['str:'] = true,
- ['bool:'] = true,
- ['nil:'] = true,
-}
-
-local ignoredIDs = {
- ['dn:unknown'] = true,
- ['dn:nil'] = true,
- ['dn:any'] = true,
- ['dn:boolean'] = true,
- ['dn:table'] = true,
- ['dn:number'] = true,
- ['dn:integer'] = true,
- ['dn:userdata'] = true,
- ['dn:lightuserdata'] = true,
- ['dn:function'] = true,
- ['dn:thread'] = true,
-}
-
----@class searcher
-local m = {}
-
----@alias guide.searchmode '"ref"'|'"def"'|'"field"'|'"allref"'|'"alldef"'|'"allfield"'
-
-local pushDefResultsMap = util.switch()
- : case 'local'
- : case 'setlocal'
- : case 'setglobal'
- : call(function (source, status)
- if source.type ~= 'local' then
- source = source.node
- end
- if source[1] == 'self'
- and source.parent.type == 'funcargs' then
- local func = source.parent.parent
- if status.source.start < func.start
- or status.source.start > func.finish then
- return false
- end
- end
- return true
- end)
- : case 'label'
- : case 'setfield'
- : case 'setmethod'
- : case 'setindex'
- : case 'tableindex'
- : case 'tablefield'
- : case 'tableexp'
- : case 'function'
- : case 'table'
- : case 'doc.class.name'
- : case 'doc.alias.name'
- : case 'doc.field.name'
- : case 'doc.type.enum'
- : case 'doc.resume'
- : case 'doc.param'
- : case 'doc.type.array'
- : case 'doc.type.table'
- : case 'doc.type.ltable'
- : case 'doc.type.field'
- : case 'doc.type.function'
- : call(function (source, status)
- return true
- end)
- : case 'doc.type.name'
- : call(function (source, status)
- return source.typeGeneric ~= nil
- end)
- : case 'call'
- : call(function (source, status)
- if source.node.special == 'rawset' then
- return true
- end
- end)
- : getMap()
-
-local pushRefResultsMap = util.switch()
- : case 'local'
- : case 'setlocal'
- : case 'getlocal'
- : case 'setglobal'
- : case 'getglobal'
- : case 'label'
- : case 'goto'
- : case 'setfield'
- : case 'getfield'
- : case 'setmethod'
- : case 'getmethod'
- : case 'setindex'
- : case 'getindex'
- : case 'tableindex'
- : case 'tablefield'
- : case 'tableexp'
- : case 'function'
- : case 'table'
- : case 'string'
- : case 'boolean'
- : case 'number'
- : case 'integer'
- : case 'nil'
- : case 'doc.class.name'
- : case 'doc.type.name'
- : case 'doc.alias.name'
- : case 'doc.extends.name'
- : case 'doc.field.name'
- : case 'doc.type.enum'
- : case 'doc.resume'
- : case 'doc.type.array'
- : case 'doc.type.table'
- : case 'doc.type.ltable'
- : case 'doc.type.field'
- : case 'doc.type.function'
- : call(function (source, status)
- return true
- end)
- : case 'call'
- : call(function (source, status)
- if source.node.special == 'rawset'
- or source.node.special == 'rawget' then
- return true
- end
- end)
- : getMap()
-
----添加结果
----@param status guide.status
----@param mode guide.searchmode
----@param source parser.guide.object
----@param force boolean
-local function pushResult(status, mode, source, force)
- if not source then
- return false
- end
- local results = status.results
- local mark = status.rmark
- if mark[source] then
- return true
- end
- mark[source] = true
- if force then
- results[#results+1] = source
- return true
- end
-
- if mode == 'def'
- or mode == 'alldef' then
- local f = pushDefResultsMap[source.type]
- if f and f(source, status) then
- results[#results+1] = source
- return true
- end
- elseif mode == 'ref'
- or mode == 'field'
- or mode == 'allfield'
- or mode == 'allref' then
- local f = pushRefResultsMap[source.type]
- if f and f(source, status) then
- results[#results+1] = source
- return true
- end
- end
-
- --local parent = source.parent
- --if parent.type == 'return' then
- -- if source ~= status.source then
- -- results[#results+1] = source
- -- return true
- -- end
- --end
-
- return false
-end
-
----@param obj parser.guide.object
----@return parser.guide.object?
-function m.getObjectValue(obj)
- while obj.type == 'paren' do
- obj = obj.exp
- if not obj then
- return nil
- end
- end
- if obj.type == 'boolean'
- or obj.type == 'number'
- or obj.type == 'integer'
- or obj.type == 'string' then
- return obj
- end
- if obj.value then
- return obj.value
- end
- if obj.type == 'field'
- or obj.type == 'method' then
- return obj.parent and obj.parent.value
- end
- if obj.type == 'call' then
- if obj.node.special == 'rawset' then
- return obj.args and obj.args[3]
- else
- return obj
- end
- end
- if obj.type == 'select' then
- return obj
- end
- return nil
-end
-
-local strs = {}
-local function footprint(status, ...)
- if TRACE then
- log.debug(...)
- end
- if FOOTPRINT then
- local n = select('#', ...)
- for i = 1, n do
- strs[i] = tostring(select(i, ...))
- end
- status.footprint[#status.footprint+1] = tconcat(strs, '\t', 1, n)
- end
-end
-
-local function checkCache(status, uri, expect, mode)
- local cache = status.cache
- local fileCache = cache[uri]
- local results = fileCache[expect]
- if results then
- for i = 1, #results do
- local res = results[i]
- pushResult(status, mode, res, true)
- end
- return true
- end
- return false, function ()
- fileCache[expect] = status.results
- if mode == 'def'
- or mode == 'alldef' then
- return
- end
- for id in next, status.ids do
- fileCache[id] = status.results
- end
- end
-end
-
-local function stop(status, msg)
- if TEST then
- if FOOTPRINT then
- log.debug(status.mode)
- log.debug(tconcat(status.footprint, '\n'))
- end
- error(msg)
- else
- log.warn(msg)
- if FOOTPRINT then
- FOOTPRINT = false
- log.error(status.mode)
- log.debug(tconcat(status.footprint, '\n'))
- end
- return
- end
-end
-
-local function isCallID(field)
- if not field then
- return false
- end
- if ssub(field, 1, 2) == RETURN_INDEX then
- return true
- end
- return false
-end
-
-local genercCache = {
- mark = {},
- genericCallArgs = {},
- closureCache = {},
-}
-
-local function flushGeneric()
- --清除来自泛型的临时对象
- for _, closure in next, genercCache.closureCache do
- if closure then
- local noders = getNoders(closure)
- removeID(noders, getID(closure))
- local values = closure.values
- for i = 1, #values do
- local value = values[i]
- removeID(noders, getID(value))
- end
- end
- end
- genercCache.mark = {}
- genercCache.closureCache = {}
- genercCache.genericCallArgs = {}
-end
-
-files.watch(function (ev)
- if ev == 'version' then
- flushGeneric()
- end
-end)
-
-local nodersMapMT = {__index = function (self, uri)
- local noders = getNodersByUri(uri)
- self[uri] = noders or false
- return noders
-end}
-
-local uriMapMT = {__index = function (self, uri)
- local t = {}
- self[uri] = t
- return t
-end}
-
-function m.searchRefsByID(status, suri, expect, mode)
- local state = getState(suri)
- if not state then
- return
- end
- local searchStep
-
- status.id = expect
-
- local callStack = status.callStack
- local ids = status.ids
- local dontCross = 0
- ---@type table<uri, noders>
- local nodersMap = setmetatable({}, nodersMapMT)
- local frejectMap = setmetatable({}, uriMapMT)
- local brejectMap = setmetatable({}, uriMapMT)
- local slockMap = setmetatable({}, uriMapMT)
- local elockMap = setmetatable({}, uriMapMT)
- local ecallMap = setmetatable({}, uriMapMT)
- local slockGlobalMap = slockMap['@global']
-
- compileAllNodes(state.ast)
-
- local function lockExpanding(elock, ecall, id, field)
- if not field then
- field = ''
- end
- local call = callStack[#callStack]
- local locked = elock[id]
- local called = ecall[id]
- if locked and called == call then
- if #locked <= #field then
- if ssub(field, -#locked) == locked then
- footprint(status, 'elocked:', id, locked, field)
- return false
- end
- end
- end
- elock[id] = field
- ecall[id] = call
- return true
- end
-
- local function releaseExpanding(elock, ecall, id, field)
- elock[id] = nil
- ecall[id] = nil
- end
-
- local function search(uri, id, field)
- local firstID = getFirstID(id)
- if ignoredIDs[firstID] and (field or firstID ~= id) then
- return
- end
-
- footprint(status, 'search:', id, field)
- searchStep(uri, id, field)
- footprint(status, 'pop:', id, field)
- end
-
- local function splitID(uri, id, field)
- if field then
- return
- end
- local leftID = ''
- local rightID
-
- while true do
- local firstID = getHeadID(rightID or id)
- if not firstID or firstID == id then
- break
- end
- leftID = leftID .. firstID
- if leftID == id then
- break
- end
- rightID = ssub(id, #leftID + 1)
- search(uri, leftID, rightID)
- local isCall = isCallID(firstID)
- if isCall then
- break
- end
- end
- end
-
- local function searchID(uri, id, field, sourceUri)
- if not id then
- return
- end
- if not nodersMap[uri] then
- return
- end
- if field then
- id = id .. field
- end
- ids[id] = true
- if slockMap[uri][id] then
- footprint(status, 'slocked:', id)
- return
- end
- slockMap[uri][id] = true
- if sourceUri and uri ~= sourceUri then
- footprint(status, 'cross uri:', uri)
- compileAllNodes(getState(uri).ast)
- end
- search(uri, id, nil)
- if sourceUri and uri ~= sourceUri then
- footprint(status, 'back uri:', sourceUri)
- end
- end
-
- ---@return parser.guide.object?
- local function findLastCall()
- for i = #callStack, 1, -1 do
- local call = callStack[i]
- if call then
- -- 标记此处的call失效,等待在堆栈平衡时弹出
- callStack[i] = false
- return call
- end
- end
- return nil
- end
-
- local genericCallArgs = genercCache.genericCallArgs
- local closureCache = genercCache.closureCache
- local function checkGeneric(uri, source, field)
- if not source.isGeneric then
- return
- end
- if not isCallID(field) then
- return
- end
- local call = findLastCall()
- if not call then
- return
- end
- local id = genercCache.mark[call]
- if id == false then
- return
- end
- if id then
- searchID(uri, id, field)
- return
- end
-
- local args = call.args
- if args then
- for i = 1, #args do
- local arg = args[i]
- genericCallArgs[arg] = true
- end
- end
-
- local cacheID = uri .. getID(source) .. getID(call)
- local closure = closureCache[cacheID]
- if closure == false then
- genercCache.mark[call] = false
- return
- end
- if not closure then
- closure = generic.createClosure(source, call)
- closureCache[cacheID] = closure or false
- if not closure then
- genercCache.mark[call] = false
- return
- end
- end
- id = getID(closure)
- genercCache.mark[call] = id
- searchID(uri, id, field)
- end
-
- local function checkENV(uri, source, field)
- if not field then
- return
- end
- if source.special ~= '_G' then
- return
- end
- local newID = 'g:' .. ssub(field, 2)
- searchID(uri, newID)
- end
-
- ---@param ward '"forward"'|'"backward"'
- ---@param info node.info
- local function pushThenCheckReject(uri, ward, info)
- local reject = info.reject
- local checkReject
- local pushReject
- if ward == 'forward' then
- checkReject = brejectMap[uri]
- pushReject = frejectMap[uri]
- else
- checkReject = frejectMap[uri]
- pushReject = brejectMap[uri]
- end
- pushReject[reject] = (pushReject[reject] or 0) + 1
- if checkReject[reject] and checkReject[reject] > 0 then
- return false
- end
- return true
- end
-
- ---@param ward '"forward"'|'"backward"'
- ---@param info node.info
- local function popReject(uri, ward, info)
- local reject = info.reject
- local popTags
- if ward == 'forward' then
- popTags = frejectMap[uri]
- else
- popTags = brejectMap[uri]
- end
- popTags[reject] = popTags[reject] - 1
- end
-
- ---@type table<node.filter, integer>
- local filters = {}
-
- ---@param id string
- ---@param info node.info
- local function pushInfoFilter(id, field, info)
- local filter = info.filter
- local filterValid = info.filterValid
- if filterValid and not filterValid(id, field) then
- return
- end
- filters[filter] = (filters[filter] or 0) + 1
- end
-
- ---@param id string
- ---@param info node.info
- local function releaseInfoFilter(id, field, info)
- local filter = info.filter
- local filterValid = info.filterValid
- if filterValid and not filterValid(id, field) then
- return
- end
- if filters[filter] <= 1 then
- filters[filter] = nil
- else
- filters[filter] = filters[filter] - 1
- end
- end
-
- ---@param id string
- ---@param info node.info
- local function checkInfoFilter(id, field, info)
- for filter in next, filters do
- if not filter(id, field, mode) then
- return false
- end
- end
- return true
- end
-
- local function checkForward(uri, id, field)
- for forwardID, info in eachForward(nodersMap[uri], id) do
- local targetUri, targetID
-
- --#region checkBeforeForward
- if info then
- if info.filter then
- pushInfoFilter(forwardID, field, info)
- end
- if info.reject then
- if not pushThenCheckReject(uri, 'forward', info) then
- goto RELEASE_THEN_CONTINUE
- end
- end
- end
- if not checkInfoFilter(forwardID, field, info) then
- goto RELEASE_THEN_CONTINUE
- end
- --#endregion
-
- targetUri, targetID = getUriAndID(forwardID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or forwardID, field)
- end
-
- ::RELEASE_THEN_CONTINUE::
- --#region releaseAfterForward
- if info then
- if info.reject then
- popReject(uri, 'forward', info)
- end
- if info.filter then
- releaseInfoFilter(id, field, info)
- end
- end
- --#endregion
- end
- end
-
- local function checkBackward(uri, id, field)
- if ignoredIDs[id]
- or id == 'dn:string' then
- return
- end
- if mode ~= 'ref'
- and mode ~= 'allfield'
- and mode ~= 'allref'
- and not field then
- return
- end
- for backwardID, info in eachBackward(nodersMap[uri], id) do
- local targetUri, targetID
- if info and info.deep and mode ~= 'allref' and mode ~= 'allfield' then
- goto CONTINUE
- end
-
- --#region checkBeforeBackward
- if info then
- if info.dontCross then
- dontCross = dontCross + 1
- end
- if info.filter then
- pushInfoFilter(backwardID, field, info)
- end
- if info.reject then
- if not pushThenCheckReject(uri, 'backward', info) then
- goto RELEASE_THEN_CONTINUE
- end
- end
- end
- if not checkInfoFilter(backwardID, field, info) then
- goto RELEASE_THEN_CONTINUE
- end
- --#endregion
-
- targetUri, targetID = getUriAndID(backwardID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or backwardID, field)
- end
-
- ::RELEASE_THEN_CONTINUE::
- --#region releaseAfterBackward
- if info then
- if info.reject then
- popReject(uri, 'backward', info)
- end
- if info.filter then
- releaseInfoFilter(backwardID, field, info)
- end
- if info.dontCross then
- dontCross = dontCross - 1
- end
- end
- --#endregion
-
- ::CONTINUE::
- end
- end
-
- local function checkExtend(uri, id, field)
- if not field
- and mode ~= 'field'
- and mode ~= 'allfield' then
- return
- end
- if field then
- local results = status.results
- for i = #results, 1, -1 do
- local res = results[i]
- if res.type == 'setfield'
- or res.type == 'setmethod'
- or res.type == 'setindex' then
- local resField = noder.getFieldID(getID(res))
- if field == resField then
- return
- end
- end
- if res.type == 'doc.field.name' then
- local resField = STRING_FIELD .. res[1]
- if field == resField then
- return
- end
- end
- end
- end
- for extendID in eachExtend(nodersMap[uri], id) do
- local targetUri, targetID
-
- targetUri, targetID = getUriAndID(extendID)
- if targetUri and targetUri ~= uri then
- if dontCross == 0 then
- searchID(targetUri, targetID, field, uri)
- end
- else
- searchID(uri, targetID or extendID, field)
- end
- end
- end
-
- local function searchSpecial(uri, id, field)
- -- Special rule: ('').XX -> stringlib.XX
- if id == 'str:'
- or id == 'dn:string' then
- if field or mode == 'field' or mode == 'allfield' then
- searchID(uri, 'dn:stringlib', field)
- else
- searchID(uri, 'dn:string', field)
- end
- end
- end
-
- local function checkRequire(uri, requireName, field)
- if not requireName then
- return
- end
- local uris = rpath.findUrisByRequirePath(suri, requireName)
- footprint(status, 'require:', requireName)
- for i = 1, #uris do
- local ruri = uris[i]
- if uri ~= ruri then
- searchID(ruri, 'mainreturn', field, uri)
- break
- end
- end
- end
-
- local function searchGlobal(uri, id, field)
- if dontCross ~= 0 then
- return
- end
- if ssub(id, 1, 2) ~= 'g:' then
- return
- end
- footprint(status, 'searchGlobal:', id, field)
- local crossed = {}
- if mode == 'def'
- or mode == 'alldef'
- or field
- or hasCall(field) then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- elseif mode == 'field'
- or mode == 'allfield' then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- for _, guri in collector:each(suri, 'field:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- else
- for _, guri in collector:each(suri, id) do
- if crossed[guri] then
- goto CONTINUE
- end
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- end
- end
-
- local function searchClass(uri, id, field)
- if dontCross ~= 0 then
- return
- end
- if ssub(id, 1, 3) ~= 'dn:' then
- return
- end
- footprint(status, 'searchClass:', id, field)
- local crossed = {}
- if mode == 'def'
- or mode == 'alldef'
- or mode == 'field'
- or ignoredIDs[id]
- or id == 'dn:string'
- or hasCall(field) then
- for _, guri in collector:each(suri, 'def:' .. id) do
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- else
- for _, guri in collector:each(suri, id) do
- if crossed[guri] then
- goto CONTINUE
- end
- if uri == guri then
- goto CONTINUE
- end
- searchID(guri, id, field, uri)
- ::CONTINUE::
- end
- end
- end
-
- local function checkMainReturn(uri, id, field)
- if id ~= 'mainreturn' then
- return
- end
- local calls = vm.getLinksTo(uri)
- for i = 1, #calls do
- local call = calls[i]
- local curi = getUri(call)
- local cid = getID(call)
- if curi ~= uri then
- searchID(curi, cid, field, uri)
- end
- end
- end
-
- local function searchNode(uri, id, field)
- local noders = nodersMap[uri]
- local call = noders.call[id]
- callStack[#callStack+1] = call
-
- if field == nil and not ignoredSources[id] then
- for source in eachSource(noders, id) do
- local force = genericCallArgs[source]
- pushResult(status, mode, source, force)
- end
- end
-
- local requireName = noders.require[id]
- if requireName then
- checkRequire(uri, requireName, field)
- end
-
- local elock = elockMap[uri]
- local ecall = ecallMap[uri]
-
- if lockExpanding(elock, ecall, id, field) then
- if noders.forward[id] then
- checkForward(uri, id, field)
- end
- if noders.backward[id] then
- checkBackward(uri, id, field)
- end
- if noders.extend[id] then
- checkExtend(uri, id, field)
- end
- releaseExpanding(elock, ecall, id, field)
- end
-
- local source = noders.source[id]
- if source then
- checkGeneric(uri, source, field)
- checkENV(uri, source, field)
- end
-
- if mode == 'allref' or mode == 'alldef' then
- checkMainReturn(uri, id, field)
- end
-
- if call then
- callStack[#callStack] = nil
- end
-
- return false
- end
-
- local function searchAnyField(uri, id, field)
- if mode == 'ref' or mode == 'allref' then
- return
- end
- local lastID = getLastID(id)
- if not lastID then
- return
- end
- local originField = ssub(id, #lastID + 1)
- if originField == TABLE_KEY
- or originField == WEAK_TABLE_KEY then
- return
- end
- local anyFieldID = lastID .. ANY_FIELD
- searchNode(uri, anyFieldID, field)
- end
-
- local function searchWeak(uri, id, field)
- local lastID = getLastID(id)
- if not lastID then
- return
- end
- local originField = ssub(id, #lastID + 1)
- if originField == WEAK_TABLE_KEY then
- local newID = lastID .. TABLE_KEY
- searchNode(uri, newID, field)
- end
- if originField == WEAK_ANY_FIELD then
- local newID = lastID .. ANY_FIELD
- searchNode(uri, newID, field)
- end
- end
-
- local stepCount = 0
- local stepMaxCount = 1e4
- if mode == 'allref' or mode == 'alldef' then
- stepMaxCount = 1e5
- end
-
- function searchStep(uri, id, field)
- stepCount = stepCount + 1
- if stepCount > stepMaxCount then
- stop(status, 'too deep!')
- return
- end
- searchSpecial(uri, id, field)
- searchNode(uri, id, field)
- if field and nodersMap[uri].skip[id] then
- return
- end
-
- local fullID = id .. (field or '')
- if not slockGlobalMap[fullID] then
- slockGlobalMap[fullID] = true
- searchGlobal(uri, id, field)
- searchClass(uri, id, field)
- slockGlobalMap[fullID] = nil
- end
-
- splitID(uri, id, field)
- searchAnyField(uri, id, field)
- searchWeak(uri, id, field)
- end
-
- search(suri, expect, nil)
- flushGeneric()
-end
-
-local function prepareSearch(source)
- if not source then
- return
- end
- if source.type == 'field'
- or source.type == 'method' then
- source = source.parent
- end
- if not source then
- return
- end
- local uri = getUri(source)
- local id = getID(source)
- -- return function
- if source.type == 'function' and source.parent.type == 'return' then
- local func = guide.getParentFunction(source)
- if func.type == 'function' then
- for index, rtn in ipairs(source.parent) do
- if rtn == source then
- id = sformat('%s%s%s'
- , getID(func)
- , RETURN_INDEX
- , index
- )
- break
- end
- end
- end
- end
- return uri, id
-end
-
-local fieldNextTypeMap = util.switch()
- : case 'getmethod'
- : case 'setmethod'
- : case 'getfield'
- : case 'setfield'
- : case 'getindex'
- : case 'setindex'
- : call(pushResult)
- : getMap()
-
-local function getField(status, source, mode)
- if source.type == 'table' then
- for i = 1, #source do
- local field = source[i]
- pushResult(status, mode, field)
- end
- return
- end
- if source.type == 'doc.type.ltable' then
- local fields = source.fields
- for i = 1, #fields do
- local field = fields[i]
- pushResult(status, mode, field)
- end
- end
- if source.type == 'doc.class.name' then
- local class = source.parent
- local fields = class.fields
- for i = 1, #fields do
- local field = fields[i]
- pushResult(status, mode, field.field)
- end
- return
- end
- local field = source.next
- if field then
- local ftype = field.type
- local pushResultOrNil = fieldNextTypeMap[ftype]
- if pushResultOrNil then
- pushResultOrNil(status, mode, field)
- end
- return
- end
-end
-
-local function searchAllGlobalByUri(status, mode, uri, fullID)
- local ast = files.getState(uri)
- if not ast then
- return
- end
- local root = ast.ast
- --compileAllNodes(root)
- local noders = getNoders(root)
- if fullID then
- for source in eachSource(noders, fullID) do
- pushResult(status, mode, source)
- end
- else
- for id in next, noders.source do
- if ssub(id, 1, 2) == 'g:'
- and not sfind(id, SPLIT_CHAR) then
- for source in eachSource(noders, id) do
- pushResult(status, mode, source)
- end
- end
- end
- end
-end
-
-local function searchAllGlobals(status, mode)
- for uri in files.eachFile() do
- searchAllGlobalByUri(status, mode, uri)
- end
-end
-
----查找全局变量
----@param uri uri
----@param mode guide.searchmode
----@param name string
----@return parser.guide.object[]
-function m.findGlobals(uri, mode, name)
- local status = m.status(nil, nil, mode)
-
- if name then
- local fullID
- if type(name) == 'string' then
- fullID = sformat('%s%s%s', 'g:', STRING_CHAR, name)
- else
- fullID = sformat('%s%s%s', 'g:', '', name)
- end
- searchAllGlobalByUri(status, mode, uri, fullID)
- else
- searchAllGlobalByUri(status, mode, uri)
- end
-
- return status.results
-end
-
----搜索对象的引用
----@param status guide.status
----@param source parser.guide.object
----@param mode guide.searchmode
-function m.searchRefs(status, source, mode)
- local uri, id = prepareSearch(source)
- if not id then
- return
- end
-
- local cached, makeCache = checkCache(status, uri, id, mode)
-
- if cached then
- return
- end
-
- if TRACE then
- log.debug('searchRefs:', id)
- end
- m.searchRefsByID(status, uri, id, mode)
- if makeCache then
- makeCache()
- end
-end
-
----搜索对象的field
----@param status guide.status
----@param source parser.guide.object
----@param mode guide.searchmode
----@param field string
-function m.searchFields(status, source, mode, field)
- local uri, id = prepareSearch(source)
- if not id then
- return
- end
- if TRACE then
- log.debug('searchFields:', id, field)
- end
- if field == '*' then
- if source.special == '_G' then
- local cached, makeCache = checkCache(status, uri, '*', mode)
- if cached then
- return
- end
- searchAllGlobals(status, mode)
- if makeCache then
- makeCache()
- end
- else
- local cached, makeCache = checkCache(status, uri, id .. '*', mode)
- if cached then
- return
- end
- local fieldMode = 'field'
- if mode == 'allref' then
- fieldMode = 'allfield'
- end
- local newStatus = m.status(source, field, fieldMode)
- m.searchRefsByID(newStatus, uri, id, fieldMode)
- local results = newStatus.results
- for i = 1, #results do
- local def = results[i]
- getField(status, def, mode)
- end
- if makeCache then
- makeCache()
- end
- end
- else
- if source.special == '_G' then
- local fullID
- if type(field) == 'string' then
- fullID = sformat('%s%s%s', 'g:', STRING_CHAR, field)
- else
- fullID = sformat('%s%s%s', 'g:', '', field)
- end
- local cahced, makeCache = checkCache(status, uri, fullID, mode)
- if cahced then
- return
- end
- m.searchRefsByID(status, uri, fullID, mode)
- if makeCache then
- makeCache()
- end
- else
- local fullID
- if type(field) == 'string' then
- fullID = sformat('%s%s%s', id, STRING_FIELD, field)
- else
- fullID = sformat('%s%s%s', id, SPLIT_CHAR, field)
- end
- local cahced, makeCache = checkCache(status, uri, fullID, mode)
- if cahced then
- return
- end
- m.searchRefsByID(status, uri, fullID, mode)
- if makeCache then
- makeCache()
- end
- end
- end
-end
-
----@class guide.status
----搜索结果
----@field results parser.guide.object[]
----@field rmark table
----@field id string
----@field source parser.guide.object
----@field field string
-
----创建搜索状态
----@param mode guide.searchmode
----@return guide.status
-function m.status(source, field, mode)
- local status = {
- callStack = {},
- results = {},
- rmark = {},
- footprint = {},
- ids = {},
- mode = mode,
- source = source,
- field = field,
- cache = setmetatable(vm.getCache('searcher:' .. mode), uriMapMT),
- }
- return status
-end
-
---- 请求对象的引用
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestReference(obj, field)
- local status = m.status(obj, field, 'ref')
-
- if field then
- m.searchFields(status, obj, 'ref', field)
- else
- m.searchRefs(status, obj, 'ref')
- end
-
- return status.results
-end
-
---- 请求对象的全部引用(深度搜索)
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestAllReference(obj, field)
- local status = m.status(obj, field, 'allref')
-
- if field then
- m.searchFields(status, obj, 'allref', field)
- else
- m.searchRefs(status, obj, 'allref')
- end
-
- return status.results
-end
-
---- 请求对象的定义
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestDefinition(obj, field)
- local status = m.status(obj, field, 'def')
-
- if field then
- m.searchFields(status, obj, 'def', field)
- else
- m.searchRefs(status, obj, 'def')
- end
-
- return status.results
-end
-
---- 请求对象的全部定义(深度搜索)
----@param obj parser.guide.object
----@param field? string
----@return parser.guide.object[]
-function m.requestAllDefinition(obj, field)
- local status = m.status(obj, field, 'alldef')
-
- if field then
- m.searchFields(status, obj, 'alldef', field)
- else
- m.searchRefs(status, obj, 'alldef')
- end
-
- return status.results
-end
-
---m.requestReference = m.requestAllReference
---m.requestDefinition = m.requestAllDefinition
-
-return m
diff --git a/script/parser/guide.lua b/script/parser/guide.lua
index eda85c97..10b2badb 100644
--- a/script/parser/guide.lua
+++ b/script/parser/guide.lua
@@ -1,66 +1,61 @@
local error = error
local type = type
----@class parser.guide.object
----@field bindDocs parser.guide.object[]
----@field bindGroup parser.guide.object[]
----@field bindSources parser.guide.object[]
----@field value parser.guide.object
----@field parent parser.guide.object
+---@class parser.object
+---@field bindDocs parser.object[]
+---@field bindGroup parser.object[]
+---@field bindSources parser.object[]
+---@field value parser.object
+---@field parent parser.object
---@field type string
---@field special string
---@field tag string
----@field args parser.guide.object[]
----@field locals parser.guide.object[]
----@field returns parser.guide.object[]
+---@field args parser.object[]
+---@field locals parser.object[]
+---@field returns parser.object[]
---@field uri uri
---@field start integer
---@field finish integer
---@field effect integer
---@field attrs string[]
----@field specials parser.guide.object[]
----@field labels parser.guide.object[]
----@field node parser.guide.object
+---@field specials parser.object[]
+---@field labels parser.object[]
+---@field node parser.object
---@field dummy boolean
----@field field parser.guide.object
----@field method parser.guide.object
----@field index parser.guide.object
----@field extends parser.guide.object[]
----@field types parser.guide.object[]
----@field fields parser.guide.object[]
----@field typeGeneric table<integer, parser.guide.object[]>
----@field tkey parser.guide.object
----@field tvalue parser.guide.object
+---@field field parser.object
+---@field method parser.object
+---@field index parser.object
+---@field extends parser.object[]
+---@field types parser.object[]
+---@field fields parser.object[]
+---@field typeGeneric table<integer, parser.object[]>
+---@field tkey parser.object
+---@field tvalue parser.object
---@field tindex integer
----@field op parser.guide.object
----@field next parser.guide.object
----@field docParam parser.guide.object
+---@field op parser.object
+---@field next parser.object
+---@field docParam parser.object
---@field sindex integer
----@field name parser.guide.object
----@field call parser.guide.object
----@field closure parser.guide.object
----@field proto parser.guide.object
----@field exp parser.guide.object
+---@field name parser.object
+---@field call parser.object
+---@field closure parser.object
+---@field proto parser.object
+---@field exp parser.object
---@field isGeneric boolean
----@field alias parser.guide.object
----@field class parser.guide.object
----@field vararg parser.guide.object
----@field param parser.guide.object
----@field overload parser.guide.object
+---@field alias parser.object
+---@field class parser.object
+---@field vararg parser.object
+---@field param parser.object
+---@field overload parser.object
---@field docParamMap table<string, integer>
---@field upvalues table<string, string[]>
----@field ref parser.guide.object[]
+---@field ref parser.object[]
---@field returnIndex integer
----@field docs parser.guide.object[]
+---@field docs parser.object[]
---@field state table
---@field comment table
---@field optional boolean
----@field _root parser.guide.object
----@field _noders noders
----@field _mnode parser.guide.object
----@field _noded boolean
----@field _initedNoders boolean
----@field _compiledGlobals boolean
+---@field _root parser.object
---@class guide
---@field debugMode boolean
@@ -147,7 +142,7 @@ local childMap = {
['doc.diagnostic'] = {'#names'},
}
----@type table<string, fun(obj: parser.guide.object, list: parser.guide.object[])>
+---@type table<string, fun(obj: parser.object, list: parser.object[])>
local compiledChildMap = setmetatable({}, {__index = function (self, name)
local defs = childMap[name]
if not defs then
@@ -227,7 +222,7 @@ local function formatNumber(n)
end
--- 是否是字面量
----@param obj parser.guide.object
+---@param obj parser.object
---@return boolean
function m.isLiteral(obj)
local tp = obj.type
@@ -241,7 +236,7 @@ function m.isLiteral(obj)
end
--- 获取字面量
----@param obj parser.guide.object
+---@param obj parser.object
---@return any
function m.getLiteral(obj)
local tp = obj.type
@@ -258,8 +253,8 @@ function m.getLiteral(obj)
end
--- 寻找父函数
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getParentFunction(obj)
for _ = 1, 10000 do
obj = obj.parent
@@ -275,8 +270,8 @@ function m.getParentFunction(obj)
end
--- 寻找所在区块
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getBlock(obj)
for _ = 1, 10000 do
if not obj then
@@ -304,8 +299,8 @@ function m.getBlock(obj)
end
--- 寻找所在父区块
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getParentBlock(obj)
for _ = 1, 10000 do
obj = obj.parent
@@ -321,8 +316,8 @@ function m.getParentBlock(obj)
end
--- 寻找所在可break的父区块
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getBreakBlock(obj)
for _ = 1, 10000 do
obj = obj.parent
@@ -341,8 +336,8 @@ function m.getBreakBlock(obj)
end
--- 寻找doc的主体
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getDocState(obj)
for _ = 1, 10000 do
local parent = obj.parent
@@ -358,8 +353,8 @@ function m.getDocState(obj)
end
--- 寻找所在父类型
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getParentType(obj, want)
for _ = 1, 10000 do
obj = obj.parent
@@ -374,8 +369,8 @@ function m.getParentType(obj, want)
end
--- 寻找根区块
----@param obj parser.guide.object
----@return parser.guide.object
+---@param obj parser.object
+---@return parser.object
function m.getRoot(obj)
local source = obj
if source._root then
@@ -399,7 +394,7 @@ function m.getRoot(obj)
error('guide.getRoot overstack')
end
----@param obj parser.guide.object
+---@param obj parser.object
---@return uri
function m.getUri(obj)
if obj.uri then
@@ -1106,7 +1101,7 @@ function m.getPath(a, b, sameFunction)
end
---是否是全局变量(包括 _G.XXX 形式)
----@param source parser.guide.object
+---@param source parser.object
---@return boolean
function m.isGlobal(source)
if source._isGlobal ~= nil then
diff --git a/script/parser/newparser.lua b/script/parser/newparser.lua
index e4884212..66df7046 100644
--- a/script/parser/newparser.lua
+++ b/script/parser/newparser.lua
@@ -2588,9 +2588,9 @@ local function skipSeps()
end
end
----@return parser.guide.object first
----@return parser.guide.object second
----@return parser.guide.object[] rest
+---@return parser.object first
+---@return parser.object second
+---@return parser.object[] rest
local function parseSetValues()
skipSpace()
local first = parseExp()
@@ -2645,8 +2645,8 @@ local function pushActionIntoCurrentChunk(action)
end
end
----@return parser.guide.object second
----@return parser.guide.object[] rest
+---@return parser.object second
+---@return parser.object[] rest
local function parseVarTails(parser, isLocal)
if Tokens[Index + 1] ~= ',' then
return
diff --git a/script/utility.lua b/script/utility.lua
index 6758a47f..0e4df627 100644
--- a/script/utility.lua
+++ b/script/utility.lua
@@ -21,9 +21,8 @@ local getupvalue = debug.getupvalue
local mathHuge = math.huge
local inf = 1 / 0
local nan = 0 / 0
-local utf8 = utf8
local error = error
-local upvalueid = debug.upvalueid
+local assert = assert
_ENV = nil
@@ -730,4 +729,32 @@ function m.defaultTable(default)
end })
end
+function m.multiTable(count, default)
+ local current
+ if default then
+ current = setmetatable({}, { __index = function (t, k)
+ local v = default(k)
+ t[k] = v
+ return v
+ end })
+ else
+ current = setmetatable({}, { __index = function (t, k)
+ local v = {}
+ t[k] = v
+ return v
+ end })
+ end
+ for _ = 3, count do
+ current = setmetatable({}, { __index = function (t, k)
+ t[k] = current
+ return current
+ end })
+ end
+ return current
+end
+
+m.MODE_K = { __mode = 'k' }
+m.MODE_V = { __mode = 'v' }
+m.MODE_KV = { __mode = 'kv' }
+
return m
diff --git a/script/vm/eachDef.lua b/script/vm/eachDef.lua
deleted file mode 100644
index ea14ed9f..00000000
--- a/script/vm/eachDef.lua
+++ /dev/null
@@ -1,11 +0,0 @@
----@class vm
-local vm = require 'vm.vm'
-local searcher = require 'core.searcher'
-
-function vm.getDefs(source, field)
- return searcher.requestDefinition(source, field)
-end
-
-function vm.getAllDefs(source, field)
- return searcher.requestAllDefinition(source, field)
-end
diff --git a/script/vm/getDef.lua b/script/vm/getDef.lua
new file mode 100644
index 00000000..df180420
--- /dev/null
+++ b/script/vm/getDef.lua
@@ -0,0 +1,175 @@
+---@class vm
+local vm = require 'vm.vm'
+local util = require 'utility'
+local compiler = require 'vm.node.compiler'
+local guide = require 'parser.guide'
+local localID = require 'vm.local-id'
+local globalNode = require 'vm.global-node'
+
+local simpleMap
+
+local function searchGetLocal(source, node, pushResult)
+ local key = guide.getKeyName(source)
+ for _, ref in ipairs(node.node.ref) do
+ if ref.type == 'getlocal'
+ and guide.isSet(ref.next)
+ and guide.getKeyName(ref.next) == key then
+ pushResult(ref.next)
+ end
+ end
+end
+
+simpleMap = util.switch()
+ : case 'local'
+ : call(function (source, pushResult)
+ pushResult(source)
+ if source.ref then
+ for _, ref in ipairs(source.ref) do
+ if ref.type == 'setlocal' then
+ pushResult(ref)
+ end
+ end
+ end
+
+ if source.dummy then
+ for _, res in ipairs(vm.getDefs(source.method.node)) do
+ pushResult(res)
+ end
+ end
+ end)
+ : case 'getlocal'
+ : case 'setlocal'
+ : call(function (source, pushResult)
+ simpleMap['local'](source.node, pushResult)
+ end)
+ : case 'field'
+ : call(function (source, pushResult)
+ local parent = source.parent
+ simpleMap[parent.type](parent, pushResult)
+ end)
+ : case 'setfield'
+ : case 'getfield'
+ : call(function (source, pushResult)
+ local node = source.node
+ if node.type == 'getlocal' then
+ searchGetLocal(source, node, pushResult)
+ return
+ end
+ end)
+ : case 'getindex'
+ : case 'setindex'
+ : call(function (source, pushResult)
+ local node = source.node
+ if node.type == 'getlocal' then
+ searchGetLocal(source, node, pushResult)
+ end
+ end)
+ : getMap()
+
+local searchFieldMap = util.switch()
+ : case 'table'
+ : call(function (node, key, pushResult)
+ for _, field in ipairs(node) do
+ if field.type == 'tablefield'
+ or field.type == 'tableindex' then
+ if guide.getKeyName(field) == key then
+ pushResult(field)
+ end
+ end
+ end
+ end)
+ : getMap()
+
+local nodeMap;nodeMap = util.switch()
+ : case 'field'
+ : call(function (source, pushResult)
+ local parent = source.parent
+ nodeMap[parent.type](parent, pushResult)
+ end)
+ : case 'getfield'
+ : case 'setfield'
+ : case 'getmethod'
+ : case 'setmethod'
+ : case 'getindex'
+ : case 'setindex'
+ : call(function (source, pushResult)
+ local node = compiler.compileNode(source.node)
+ if not node then
+ return
+ end
+ if searchFieldMap[node.type] then
+ searchFieldMap[node.type](node, guide.getKeyName(source), pushResult)
+ end
+ end)
+ : getMap()
+
+ ---@param source parser.object
+ ---@param pushResult fun(src: parser.object)
+local function searchBySimple(source, pushResult)
+ local simple = simpleMap[source.type]
+ if simple then
+ simple(source, pushResult)
+ end
+end
+
+---@param source parser.object
+---@param pushResult fun(src: parser.object)
+local function searchByGlobal(source, pushResult)
+ local global = globalNode.getNode(source)
+ if not global then
+ return
+ end
+ for _, src in ipairs(global:getSets()) do
+ pushResult(src)
+ end
+end
+
+---@param source parser.object
+---@param pushResult fun(src: parser.object)
+local function searchByID(source, pushResult)
+ local idSources = localID.getSources(source)
+ if not idSources then
+ return
+ end
+ for _, src in ipairs(idSources) do
+ if guide.isSet(src) then
+ pushResult(src)
+ end
+ end
+end
+
+---@param source parser.object
+---@param pushResult fun(src: parser.object)
+local function searchByNode(source, pushResult)
+ local node = nodeMap[source.type]
+ if node then
+ node(source, pushResult)
+ end
+end
+
+---@param source parser.object
+---@return parser.object[]
+function vm.getDefs(source)
+ local results = {}
+ local mark = {}
+
+ local function pushResult(src)
+ if not mark[src] then
+ mark[src] = true
+ results[#results+1] = src
+ end
+ end
+
+ searchBySimple(source, pushResult)
+ searchByGlobal(source, pushResult)
+ searchByID(source, pushResult)
+ searchByNode(source, pushResult)
+
+ return results
+end
+
+---@param source parser.object
+---@return parser.object[]
+function vm.getAllDefs(source)
+ return vm.getDefs(source)
+end
diff --git a/script/vm/getDocs.lua b/script/vm/getDocs.lua
index 4aeda446..4204d785 100644
--- a/script/vm/getDocs.lua
+++ b/script/vm/getDocs.lua
@@ -5,11 +5,10 @@ local vm = require 'vm.vm'
local config = require 'config'
local collector = require 'core.collector' 'searcher'
local define = require 'proto.define'
-local noder = require 'core.noder'
---获取class与alias
---@param name? string
----@return parser.guide.object[]
+---@return parser.object[]
function vm.getDocDefines(uri, name)
if type(name) ~= 'string' then
return {}
diff --git a/script/vm/getGlobals.lua b/script/vm/getGlobals.lua
index 8af21d45..2ba734ba 100644
--- a/script/vm/getGlobals.lua
+++ b/script/vm/getGlobals.lua
@@ -2,7 +2,6 @@ local collector = require 'core.collector' 'searcher'
local guide = require 'parser.guide'
---@class vm
local vm = require 'vm.vm'
-local noder = require 'core.noder'
function vm.hasGlobalSets(uri, name)
local id
diff --git a/script/vm/eachRef.lua b/script/vm/getRef.lua
index 899c04c6..ded95b61 100644
--- a/script/vm/eachRef.lua
+++ b/script/vm/getRef.lua
@@ -1,6 +1,5 @@
---@class vm
local vm = require 'vm.vm'
-local searcher = require 'core.searcher'
function vm.getRefs(source, field)
return searcher.requestReference(source, field)
diff --git a/script/vm/global-node.lua b/script/vm/global-node.lua
new file mode 100644
index 00000000..6235ff6b
--- /dev/null
+++ b/script/vm/global-node.lua
@@ -0,0 +1,142 @@
+local util = require 'utility'
+local guide = require 'parser.guide'
+local globalBuilder = require 'vm.node.global'
+
+---@class parser.object
+---@field _globalNode vm.node.global
+
+---@class vm.global-node
+local m = {}
+---@type table<string, vm.node.global>
+m.globals = util.defaultTable(globalBuilder)
+---@type table<uri, table<string, boolean>>
+m.globalSubs = util.defaultTable(function ()
+ return {}
+end)
+
+m.ID_SPLITE = '\x1F'
+
+local compilerGlobalMap = util.switch()
+ : case 'local'
+ : call(function (uri, source)
+ if source.tag ~= '_ENV' then
+ return
+ end
+ if source.ref then
+ for _, ref in ipairs(source.ref) do
+ m.compileNode(uri, ref)
+ end
+ end
+ end)
+ : case 'setglobal'
+ : call(function (uri, source)
+ local name = guide.getKeyName(source)
+ source._globalNode = m.declareGlobal(name, uri, source)
+ end)
+ : case 'getglobal'
+ : call(function (uri, source)
+ local name = guide.getKeyName(source)
+ local global = m.getGlobal(name)
+ global:addGet(uri, source)
+ source._globalNode = global
+
+ local nxt = source.next
+ if nxt then
+ m.compileNode(uri, nxt)
+ end
+ end)
+ : case 'setfield'
+ : case 'setmethod'
+ : case 'setindex'
+ ---@param uri uri
+ ---@param source parser.object
+ : call(function (uri, source)
+ local parent = source.node._globalNode
+ if not parent then
+ return
+ end
+ local name = parent:getName() .. m.ID_SPLITE .. guide.getKeyName(source)
+ source._globalNode = m.declareGlobal(name, uri, source)
+ end)
+ : case 'getfield'
+ : case 'getmethod'
+ : case 'getindex'
+ ---@param uri uri
+ ---@param source parser.object
+ : call(function (uri, source)
+ local parent = source.node._globalNode
+ if not parent then
+ return
+ end
+ local name = parent:getName() .. m.ID_SPLITE .. guide.getKeyName(source)
+ local global = m.getGlobal(name)
+ global:addGet(uri, source)
+ source._globalNode = global
+
+ local nxt = source.next
+ if nxt then
+ m.compileNode(uri, nxt)
+ end
+ end)
+ : getMap()
+
+
+---@param name string
+---@param uri uri
+---@param source parser.object
+---@return vm.node.global
+function m.declareGlobal(name, uri, source)
+ m.globalSubs[uri][name] = true
+ local node = m.globals[name]
+ node:addSet(uri, source)
+ return node
+end
+
+---@param name string
+---@param uri? uri
+---@return vm.node.global
+function m.getGlobal(name, uri)
+ if uri then
+ m.globalSubs[uri][name] = true
+ end
+ return m.globals[name]
+end
+
+---@param source parser.object
+function m.compileNode(uri, source)
+ if source._globalNode ~= nil then
+ return
+ end
+ source._globalNode = false
+ local compiler = compilerGlobalMap[source.type]
+ if compiler then
+ compiler(uri, source)
+ end
+end
+
+---@param source parser.object
+function m.compileAst(source)
+ local uri = guide.getUri(source)
+ local env = guide.getENV(source)
+ m.compileNode(uri, env)
+end
+
+---@return vm.node.global
+function m.getNode(source)
+ if source.type == 'field'
+ or source.type == 'method' then
+ source = source.parent
+ end
+ return source._globalNode
+end
+
+---@param uri uri
+function m.dropUri(uri)
+ local globalSub = m.globalSubs[uri]
+ m.globalSubs[uri] = nil
+ for name in pairs(globalSub) do
+ m.globals[name]:dropUri(uri)
+ end
+end
+
+return m
diff --git a/script/vm/init.lua b/script/vm/init.lua
index 935f39e3..5a5de99e 100644
--- a/script/vm/init.lua
+++ b/script/vm/init.lua
@@ -1,8 +1,9 @@
local vm = require 'vm.vm'
+require 'vm.state'
require 'vm.getGlobals'
require 'vm.getDocs'
require 'vm.getLibrary'
-require 'vm.eachDef'
-require 'vm.eachRef'
+require 'vm.getDef'
+require 'vm.getRef'
require 'vm.getLinks'
return vm
diff --git a/script/vm/local-id.lua b/script/vm/local-id.lua
new file mode 100644
index 00000000..8487d96a
--- /dev/null
+++ b/script/vm/local-id.lua
@@ -0,0 +1,116 @@
+local util = require 'utility'
+local guide = require 'parser.guide'
+
+---@class parser.object
+---@field _localID string
+---@field _localIDs table<string, parser.object[]>
+
+---@class vm.local-id
+local m = {}
+
+m.ID_SPLITE = '\x1F'
+
+local compileMap = util.switch()
+ : case 'local'
+ : call(function (source)
+ if not source.ref then
+ return
+ end
+ for _, ref in ipairs(source.ref) do
+ m.compileLocalID(ref)
+ end
+ end)
+ : case 'getlocal'
+ : call(function (source)
+ source._localID = ('%d'):format(source.node.start)
+ m.compileLocalID(source.next)
+ end)
+ : case 'getfield'
+ : case 'setfield'
+ : call(function (source)
+ local parentID = source.node._localID
+ if not parentID then
+ return
+ end
+ source._localID = parentID .. m.ID_SPLITE .. guide.getKeyName(source)
+ source.field._localID = source._localID
+ if source.type == 'getfield' then
+ m.compileLocalID(source.next)
+ end
+ end)
+ : getMap()
+
+local leftMap = util.switch()
+ : case 'field'
+ : call(function (source)
+ return m.getLocal(source.parent)
+ end)
+ : case 'getfield'
+ : case 'setfield'
+ : call(function (source)
+ return m.getLocal(source.node)
+ end)
+ : case 'getlocal'
+ : call(function (source)
+ return source.node
+ end)
+ : getMap()
+
+---@param source parser.object
+---@return parser.object?
+function m.getLocal(source)
+ local getLeft = leftMap[source.type]
+ if getLeft then
+ return getLeft(source)
+ end
+ return nil
+end
+
+function m.compileLocalID(source)
+ if not source then
+ return
+ end
+ source._localID = false
+ local compiler = compileMap[source.type]
+ if not compiler then
+ return
+ end
+ compiler(source)
+ local root = guide.getRoot(source)
+ if not root._localIDs then
+ root._localIDs = util.multiTable(2)
+ end
+ local sources = root._localIDs[source._localID]
+ sources[#sources+1] = source
+end
+
+---@param source parser.object
+---@return string|boolean
+function m.getID(source)
+ if source._localID ~= nil then
+ return source._localID
+ end
+ source._localID = false
+ local loc = m.getLocal(source)
+ if not loc then
+ return source._localID
+ end
+ m.compileLocalID(loc)
+ return source._localID
+end
+
+---@param source parser.object
+---@return parser.object[]?
+function m.getSources(source)
+ local id = m.getID(source)
+ if not id then
+ return nil
+ end
+ local root = guide.getRoot(source)
+ if not root._localIDs then
+ return nil
+ end
+ return root._localIDs[id]
+end
+
+return m
diff --git a/script/vm/node/class.lua b/script/vm/node/class.lua
new file mode 100644
index 00000000..ee8b1ca8
--- /dev/null
+++ b/script/vm/node/class.lua
@@ -0,0 +1,11 @@
+---@class vm.node.class
+local mt = {}
+mt.__index = mt
+mt.type = 'class'
+
+---@return vm.node.class
+return function ()
+ local class = setmetatable({
+ }, mt)
+ return class
+end
diff --git a/script/vm/node/compiler.lua b/script/vm/node/compiler.lua
new file mode 100644
index 00000000..dfe4bc0c
--- /dev/null
+++ b/script/vm/node/compiler.lua
@@ -0,0 +1,190 @@
+local guide = require 'parser.guide'
+local util = require 'utility'
+local state = require 'vm.state'
+local union = require 'vm.node.union'
+local localID = require 'vm.local-id'
+
+---@class parser.object
+---@field _compiledNodes boolean
+---@field _node vm.node
+
+---@class vm.node.compiler
+local m = {}
+
+---@class vm.node.cross
+
+---@alias vm.node parser.object | vm.node.union | vm.node.cross | vm.node.global
+
+function m.setNode(source, node)
+ if not node then
+ return
+ end
+ local me = source._node
+ if not me then
+ source._node = node
+ return
+ end
+ if me.type == 'union'
+ or me.type == 'cross' then
+ me:merge(node)
+ return
+ end
+ source._node = union(me, node)
+end
+
+function m.eachNode(node)
+ if node.type == 'union' then
+ return node:eachNode()
+ end
+ local first = true
+ return function ()
+ if first then
+ first = false
+ return node
+ end
+ return nil
+ end
+end
+
+local function getReturnOfFunction(func, index)
+ if not func._returns then
+ func._returns = util.defaultTable(function ()
+ return {
+ type = 'function.return',
+ parent = func,
+ index = index,
+ }
+ end)
+ end
+ return m.compileNode(func._returns[index])
+end
+
+local function getReturn(func, index)
+ local node = m.compileNode(func)
+ if not node then
+ return
+ end
+ for cnode in m.eachNode(node) do
+ if cnode.type == 'function' then
+ return getReturnOfFunction(cnode, index)
+ end
+ end
+end
+
+local function compileByLocalID(source)
+ local sources = localID.getSources(source)
+ if not sources then
+ return
+ end
+ for _, src in ipairs(sources) do
+ if src.value then
+ m.setNode(source, m.compileNode(src.value))
+ end
+ end
+end
+
+local searchFieldMap = util.switch()
+ : case 'table'
+ : call(function (node, key, pushResult)
+ for _, field in ipairs(node) do
+ if field.type == 'tablefield'
+ or field.type == 'tableindex' then
+ if guide.getKeyName(field) == key then
+ pushResult(field)
+ end
+ end
+ end
+ end)
+ : getMap()
+
+local function compileByParentNode(source)
+ local parentNode = m.compileNode(source.node)
+ if not parentNode then
+ return
+ end
+ local key = guide.getKeyName(source)
+ local f = searchFieldMap[parentNode.type]
+ if f then
+ f(parentNode, key, function (field)
+ m.setNode(source, m.compileNode(field.value))
+ end)
+ end
+end
+
+local compilerMap = util.switch()
+ : case 'boolean'
+ : case 'table'
+ : case 'integer'
+ : case 'number'
+ : case 'string'
+ : case 'function'
+ : call(function (source)
+ m.setNode(source, state.declareLiteral(source))
+ end)
+ : case 'local'
+ : call(function (source)
+ if source.value then
+ m.setNode(source, m.compileNode(source.value))
+ end
+ if source.ref then
+ for _, ref in ipairs(source.ref) do
+ if ref.type == 'setlocal' then
+ m.setNode(source, m.compileNode(ref.value))
+ end
+ end
+ end
+ end)
+ : case 'getlocal'
+ : call(function (source)
+ m.setNode(source, m.compileNode(source.node))
+ end)
+ : case 'setfield'
+ : case 'setmethod'
+ : case 'setindex'
+ : call(function (source)
+ compileByLocalID(source)
+ end)
+ : case 'getfield'
+ : case 'getmethod'
+ : case 'getindex'
+ : call(function (source)
+ compileByLocalID(source)
+ compileByParentNode(source)
+ end)
+ : case 'function.return'
+ : call(function (source)
+ local func = source.parent
+ local index = source.index
+ if func.returns then
+ for _, rtn in ipairs(func.returns) do
+ if rtn[index] then
+ m.setNode(source, m.compileNode(rtn[index]))
+ end
+ end
+ end
+ end)
+ : case 'select'
+ : call(function (source, value)
+ local vararg = value.vararg
+ if vararg.type == 'call' then
+ m.setNode(source, getReturn(vararg.node, value.sindex))
+ end
+ end)
+ : getMap()
+
+---@param source parser.object
+---@return vm.node
+function m.compileNode(source)
+ if source._node then
+ return source._node
+ end
+ source._node = false
+ local compiler = compilerMap[source.type]
+ if compiler then
+ compiler(source)
+ end
+ state.subscribeLiteral(source, source._node)
+ return source._node
+end
+
+return m
diff --git a/script/vm/node/global.lua b/script/vm/node/global.lua
new file mode 100644
index 00000000..499e526b
--- /dev/null
+++ b/script/vm/node/global.lua
@@ -0,0 +1,79 @@
+local util = require 'utility'
+
+---@class vm.node.global.link
+---@field gets parser.object[]
+---@field sets parser.object[]
+
+---@class vm.node.global
+---@field links table<uri, vm.node.global.link>
+---@field setsCache parser.object[]
+---@field getsCache parser.object[]
+local mt = {}
+mt.__index = mt
+mt.type = 'global'
+mt.name = ''
+
+---@param uri uri
+---@param source parser.object
+function mt:addSet(uri, source)
+ local link = self.links[uri]
+ link.sets[#link.sets+1] = source
+end
+
+---@param uri uri
+---@param source parser.object
+function mt:addGet(uri, source)
+ local link = self.links[uri]
+ link.gets[#link.gets+1] = source
+end
+
+---@return parser.object[]
+function mt:getSets()
+ if not self.setsCache then
+ self.setsCache = {}
+ for _, link in pairs(self.links) do
+ for _, source in ipairs(link.sets) do
+ self.setsCache[#self.setsCache+1] = source
+ end
+ end
+ end
+ return self.setsCache
+end
+
+---@return parser.object[]
+function mt:getGets()
+ if not self.getsCache then
+ self.getsCache = {}
+ for _, link in pairs(self.links) do
+ for _, source in ipairs(link.gets) do
+ self.getsCache[#self.getsCache+1] = source
+ end
+ end
+ end
+ return self.getsCache
+end
+
+---@param uri uri
+function mt:dropUri(uri)
+ self.links[uri] = nil
+ self.setsCache = nil
+ self.getsCache = nil
+end
+
+---@return string
+function mt:getName()
+ return self.name
+end
+
+---@return vm.node.global
+return function (name)
+ return setmetatable({
+ name = name,
+ links = util.defaultTable(function ()
+ return {
+ sets = {},
+ gets = {},
+ }
+ end),
+ }, mt)
+end
diff --git a/script/vm/node/union.lua b/script/vm/node/union.lua
new file mode 100644
index 00000000..a8c917d9
--- /dev/null
+++ b/script/vm/node/union.lua
@@ -0,0 +1,49 @@
+local state = require 'vm.state'
+
+---@class vm.node.union
+local mt = {}
+mt.__index = mt
+mt.type = 'union'
+
+---@param node vm.node
+function mt:merge(node)
+ if not node then
+ return
+ end
+ if node.type == 'union' then
+ for _, c in ipairs(node) do
+ self[#self+1] = c
+ end
+ else
+ self[#self+1] = node
+ end
+end
+
+---@param source parser.object
+function mt:subscribeLiteral(source)
+ for _, c in ipairs(self) do
+ state.subscribeLiteral(source, c)
+ if c.type == 'cross' then
+ c:subscribeLiteral(source)
+ end
+ end
+end
+
+function mt:eachNode()
+ local i = 0
+ return function ()
+ i = i + 1
+ return self[i]
+ end
+end
+
+---@param me parser.object
+---@param node vm.node
+---@return vm.node.union
+return function (me, node)
+ local union = setmetatable({
+ [1] = me,
+ }, mt)
+ union:merge(node)
+ return union
+end
diff --git a/script/vm/state.lua b/script/vm/state.lua
new file mode 100644
index 00000000..b0689384
--- /dev/null
+++ b/script/vm/state.lua
@@ -0,0 +1,80 @@
+local util = require 'utility'
+local files = require 'files'
+local globalNode = require 'vm.global-node'
+local guide = require 'parser.guide'
+
+---@class vm.state
+local m = {}
+---@type table<uri, parser.object[]>
+m.literals = util.multiTable(2)
+---@type table<parser.object, table<parser.object, boolean>>
+m.literalSubs = util.multiTable(2, function ()
+ return setmetatable({}, util.MODE_K)
+end)
+---@type table<parser.object, boolean>
+m.allLiterals = {}
+
+---@param source parser.object
+function m.declareLiteral(source)
+ if m.allLiterals[source] then
+ return
+ end
+ m.allLiterals[source] = true
+ local uri = guide.getUri(source)
+ local literals = m.literals[uri]
+ literals[#literals+1] = source
+end
+
+---@param source parser.object
+---@param node vm.node
+function m.subscribeLiteral(source, node)
+ if not node then
+ return
+ end
+ if node.type == 'union'
+ or node.type == 'cross' then
+ node:subscribeLiteral(source)
+ return
+ end
+ if not m.allLiterals[source] then
+ return
+ end
+ m.literalSubs[node][source] = true
+end
+
+---@param uri uri
+function m.dropUri(uri)
+ local literals = m.literals[uri]
+ m.literals[uri] = nil
+ for _, literal in ipairs(literals) do
+ m.allLiterals[literal] = nil
+ local literalSubs = m.literalSubs[literal]
+ m.literalSubs[literal] = nil
+ for source in pairs(literalSubs) do
+ source._node = nil
+ end
+ end
+end
+
+for uri in files.eachFile() do
+ local state = files.getState(uri)
+ if state then
+ globalNode.compileAst(state.ast)
+ end
+end
+
+files.watch(function (ev, uri)
+ if ev == 'update' then
+ local state = files.getState(uri)
+ if state then
+ globalNode.compileAst(state.ast)
+ end
+ end
+ if ev == 'remove' then
+ m.dropUri(uri)
+ globalNode.dropUri(uri)
+ end
+end)
+
+
+return m
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
index aa18ea73..ff893f24 100644
--- a/script/vm/vm.lua
+++ b/script/vm/vm.lua
@@ -1,11 +1,8 @@
local guide = require 'parser.guide'
-local util = require 'utility'
local files = require 'files'
local timer = require 'timer'
local setmetatable = setmetatable
-local running = coroutine.running
-local ipairs = ipairs
local log = log
local xpcall = xpcall
local mathHuge = math.huge
diff --git a/test.lua b/test.lua
index 42b0d129..8a343f70 100644
--- a/test.lua
+++ b/test.lua
@@ -51,8 +51,8 @@ end
local function testAll()
test 'basic'
- test 'references'
test 'definition'
+ test 'references'
test 'type_inference'
test 'hover'
test 'completion'
diff --git a/test/basic/init.lua b/test/basic/init.lua
index 1b698493..8490d51c 100644
--- a/test/basic/init.lua
+++ b/test/basic/init.lua
@@ -1,2 +1 @@
require 'basic.textmerger'
-require 'basic.noder'
diff --git a/test/basic/linker.txt b/test/basic/linker.txt
deleted file mode 100644
index ea3ba180..00000000
--- a/test/basic/linker.txt
+++ /dev/null
@@ -1,141 +0,0 @@
-ast -> linkers = {
- ['g|"X"|"Y"|"Z"'] = {src1, src2, src3},
- ['g|"X"|"Y"'] = {src4, src5, src6},
- ['g|"X"'] = {src7, src8, src9},
- ['l|7'] = {src10},
- ['l|7|"x"'] = {src11},
- ['l|11|"k"'] = {src12},
-}
-
-```lua
-x.y.<?z?> = <!f!>
-
-<?g?> = x.y.z
-
-t.<!z!> = 1
-x.y = t
-
-x = {
- y = {
- <!z!> = 1
- }
-}
-```
-
-expect: 'l|x|y|z'
-forward: 'l|x|y|z' -> f
-backward: 'l|x|y|z' -> g
-last: 'l|x|y' + 'z'
-
-expect: 'l|x|y' + '|z'
-forward: 'l|t' + '|z' -> 'l|t|z' -> t.z
-backward: nil
-last: 'l|x' + '|y|z'
-
-expect: 'l|x' + '|y|z'
-forward: 'l|0' + '|y|z' -> 'l|0|y|z'
-backward: nil
-last: nil
-
-expect: 'l|0|y|z'
-forward: nil
-backward: nil
-last: 'l|0|y' + '|z'
-
-expect: 'l|0|y' + '|z'
-forward: 'l|1'+ '|z' -> 'l|1|z' -> field z
-backward: nil
-last: 'l|0' + '|y|z'
-
-
-```lua
-a = {
- b = {
- <?c?> = 1,
- }
-}
-
-print(a.b.<!c!>)
-```
-
-expect: 't|3|c'
-forward: nil
-backward: nil
-last: 't|3' + '|c'
-
-expect: 't|3' + '|c'
-forward: nil
-backward: 't|2|b' + '|c'
-last: nil
-
-expect: 't|2|b|c'
-forward: nil
-backward: 't|2|b' + '|c'
-last: nil
-
-```lua
----@return <?A?>
-local function f()
-end
-
-local <!x!> = f()
-```
-
-'d|A'
-'f|1|#1'
-'f|1' + '|#1'
-'l|1' + '|#1'
-'s|1' + '|#1'
-
-```lua
----@generic T
----@param a T
----@return T
-local function f(a) end
-
-local <?c?>
-
-local <!v!> = f(c)
-```
-
-'l1'
-'l2|@1'
-'f|1|@1'
-'f|1|#1'
-
-```
----@generic T
----@param p T
----@return T
-local function f(p) end
-
-local <?r?> = f(<!k!>)
-```
-
-l:r
-s:1#1 call
-l:f#1 call
-f:1#1 call -> f:1&T = l:k
-l:f@1 --> 从保存的call信息里找到 f:1&T = l:k
-l:k
-
-
-
-```
----@generic T, V
----@param p T
----@return fun(V):T, V
-local function f(p) end
-
-local f2 = f(<!k!>)
-local <?r?> = f2()
-```
-
-l:r
-s:2|#1 call1
-l:f2|#1 call1
-f:2|#1 call1
-s:1#1|#1 call2
-f:1#1|#1 call2 -> f:1&T = l:k
-dfun:1|#1
-dn:V -> f:1&T = l:k
diff --git a/test/basic/noder.lua b/test/basic/noder.lua
deleted file mode 100644
index 49585ee8..00000000
--- a/test/basic/noder.lua
+++ /dev/null
@@ -1,143 +0,0 @@
-local noder = require 'core.noder'
-local files = require 'files'
-local util = require 'utility'
-local guide = require 'parser.guide'
-local catch = require 'catch'
-
-local function getSource(pos)
- local ast = files.getState('')
- return guide.eachSourceContain(ast.ast, pos, function (source)
- if source.type == 'local'
- or source.type == 'getlocal'
- or source.type == 'setlocal'
- or source.type == 'setglobal'
- or source.type == 'getglobal'
- or source.type == 'setfield'
- or source.type == 'getfield'
- or source.type == 'setmethod'
- or source.type == 'getmethod'
- or source.type == 'tablefield'
- or source.type == 'setindex'
- or source.type == 'getindex'
- or source.type == 'tableindex'
- or source.type == 'label'
- or source.type == 'goto' then
- return source
- end
- end)
-end
-
-local CARE = {}
-local function TEST(script)
- return function (expect)
- local newScript, catched = catch(script, '?')
- files.setText('', newScript)
- local source = getSource(catched['?'][1][1])
- assert(source)
- local result = {
- id = noder.getID(source),
- }
-
- expect['id'] = expect['id']:gsub('|', '\x1F')
-
- for key in pairs(CARE) do
- assert(result[key] == expect[key])
- end
- files.remove('')
- end
-end
-
-CARE['id'] = true
-TEST [[
-local <?x?>
-]] {
- id = 'l:6',
-}
-
-TEST [[
-local x
-print(<?x?>)
-]] {
- id = 'l:6',
-}
-
-TEST [[
-local x
-<?x?> = 1
-]] {
- id = 'l:6',
-}
-
-TEST [[
-print(<?X?>)
-]] {
- id = 'g:.X',
-}
-
-TEST [[
-print(<?X?>)
-]] {
- id = 'g:.X',
-}
-
-TEST [[
-local x
-print(x.y.<?z?>)
-]] {
- id = 'l:6|.y|.z',
-}
-
-TEST [[
-local x
-function x:<?f?>() end
-]] {
- id = 'l:6|.f',
-}
-
-TEST [[
-print(X.Y.<?Z?>)
-]] {
- id = 'g:.X|.Y|.Z',
-}
-
-TEST [[
-function x:<?f?>() end
-]] {
- id = 'g:.x|.f',
-}
-
-TEST [[
-{
- <?x?> = 1,
-}
-]] {
- id = 't:0|.x',
-}
-
-TEST [[
-return <?X?>
-]] {
- id = 'g:.X',
-}
-
-TEST [[
-function f()
- return <?X?>
-end
-]] {
- id = 'g:.X',
-}
-
-TEST [[
-::<?label?>::
-goto label
-]] {
- id = 'l:2',
-}
-
-TEST [[
-::label::
-goto <?label?>
-]] {
- id = 'l:2',
-}
diff --git a/test/definition/field.lua b/test/definition/field.lua
new file mode 100644
index 00000000..5b5b67f9
--- /dev/null
+++ b/test/definition/field.lua
@@ -0,0 +1,23 @@
+TEST [[
+X.<!y!> = 1
+
+local t = X
+
+print(t.<?y?>)
+]]
+
+TEST [[
+X.x.<!y!> = 1
+
+local t = X.x
+
+print(t.<?y?>)
+]]
+
+TEST [[
+X.x.<!y!> = 1
+
+local t = X
+
+print(t.x.<?y?>)
+]]
diff --git a/test/definition/init.lua b/test/definition/init.lua
index fc2903c1..08b97442 100644
--- a/test/definition/init.lua
+++ b/test/definition/init.lua
@@ -44,6 +44,7 @@ end
require 'definition.local'
require 'definition.set'
+require 'definition.field'
require 'definition.arg'
require 'definition.function'
require 'definition.table'
diff --git a/test/definition/table.lua b/test/definition/table.lua
index 61e8746d..66e71b0c 100644
--- a/test/definition/table.lua
+++ b/test/definition/table.lua
@@ -134,32 +134,44 @@ local y = {
t.<?insert?>()
]]
+
+TEST [[
+local x
+x.y.<!z!> = 1
+print(x.y.<?z?>)
+]]
+
+
+TEST [[
+local x
+x.y = {
+ <!z!> = 1
+}
+print(x.y.<?z?>)
+]]
+
+TEST [[
+local x = {
+ y = {
+ <!z!> = 1
+ }
+}
+print(x.y.<?z?>)
+]]
+
TEST [[
local function f()
- local t = {}
- t.field1 = {
+ local t = {
<!x!> = 1,
- y = 1,
- z = 1,
- }
- t.field2 = {
- x = 1,
- y = 1,
- z = 1,
- }
- t.field3 = {
- x = 1,
- y = 1,
- z = 1,
}
return t
end
local t = f()
-t.field1.<?x?>
+t.<?x?>
]]
-TEST [[
-local t = { <!a!> }
-
-print(t[<?1?>])
-]]
+--TEST [[
+--local t = { <!a!> }
+--
+--print(t[<?1?>])
+--]]