1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
---@class vm
local vm = require 'vm.vm'
local util = require 'utility'
local guide = require 'parser.guide'
local simpleSwitch
simpleSwitch = util.switch()
: case 'goto'
: call(function (source, pushResult)
if source.node then
pushResult(source.node)
end
end)
: case 'doc.cast.name'
: call(function (source, pushResult)
local loc = guide.getLocal(source, source[1], source.start)
if loc then
pushResult(loc)
end
end)
: case 'doc.field'
: call(function (source, pushResult)
pushResult(source)
end)
---@param source parser.object
---@param pushResult fun(src: parser.object)
local function searchBySimple(source, pushResult)
simpleSwitch(source.type, source, pushResult)
end
---@param source parser.object
---@param pushResult fun(src: parser.object)
local function searchByLocalID(source, pushResult)
local idSources = vm.getVariableSets(source)
if not idSources then
return
end
for _, src in ipairs(idSources) do
pushResult(src)
end
end
local function searchByNode(source, pushResult)
local node = vm.compileNode(source)
local suri = guide.getUri(source)
for n in node:eachObject() do
if n.type == 'global' then
for _, set in ipairs(n:getSets(suri)) do
pushResult(set)
end
else
pushResult(n)
end
end
end
---@param source parser.object
---@return parser.object[]
function vm.getDefs(source)
local results = {}
local mark = {}
local hasLocal
local function pushResult(src)
if src.type == 'local' then
if hasLocal then
return
end
hasLocal = true
if source.type ~= 'local'
and source.type ~= 'getlocal'
and source.type ~= 'setlocal'
and source.type ~= 'doc.cast.name' then
return
end
end
if not mark[src] then
mark[src] = true
if guide.isAssign(src)
or guide.isLiteral(src) then
results[#results+1] = src
end
end
end
searchBySimple(source, pushResult)
searchByLocalID(source, pushResult)
vm.compileByNodeChain(source, pushResult)
searchByNode(source, pushResult)
return results
end
local HAS_DEF_ERR = {'<HAS_DEF_ERR>'} -- the error object for comparing
local function checkHasDef(checkFunc, source, pushResult)
local _, err = pcall(checkFunc, source, pushResult)
return err == HAS_DEF_ERR
end
---@param source parser.object
function vm.hasDef(source)
local mark = {}
local hasLocal
local function pushResult(src)
if src.type == 'local' then
if hasLocal then
return
end
hasLocal = true
if source.type ~= 'local'
and source.type ~= 'getlocal'
and source.type ~= 'setlocal'
and source.type ~= 'doc.cast.name' then
return
end
end
if not mark[src] then
mark[src] = true
if guide.isAssign(src)
or guide.isLiteral(src) then
-- break out on 1st result using error() with a unique error object
error(HAS_DEF_ERR)
end
end
end
return checkHasDef(searchBySimple, source, pushResult)
or checkHasDef(searchByLocalID, source, pushResult)
or checkHasDef(vm.compileByNodeChain, source, pushResult)
or checkHasDef(searchByNode, source, pushResult)
end
|