diff options
Diffstat (limited to 'server-beta/src')
-rw-r--r-- | server-beta/src/parser/guide.lua | 19 | ||||
-rw-r--r-- | server-beta/src/searcher/init.lua | 2 | ||||
-rw-r--r-- | server-beta/src/searcher/isTrue.lua | 52 | ||||
-rw-r--r-- | server-beta/src/searcher/searcher.lua | 4 | ||||
-rw-r--r-- | server-beta/src/searcher/typeInference.lua | 253 |
5 files changed, 324 insertions, 6 deletions
diff --git a/server-beta/src/parser/guide.lua b/server-beta/src/parser/guide.lua index 80f9d160..f91c677e 100644 --- a/server-beta/src/parser/guide.lua +++ b/server-beta/src/parser/guide.lua @@ -299,7 +299,10 @@ function m.eachSourceContain(ast, offset, callback) list[len] = nil if m.isInRange(obj, offset) then if m.isContain(obj, offset) then - callback(obj) + local res = callback(obj) + if res ~= nil then + return res + end end m.addChilds(list, obj, m.childMap) end @@ -476,19 +479,23 @@ function m.getKeyName(obj) elseif tp == 'getfield' or tp == 'setfield' or tp == 'tablefield' then - return 's|' .. obj.field[1] + if obj.field then + return 's|' .. obj.field[1] + end elseif tp == 'getmethod' or tp == 'setmethod' then - return 's|' .. obj.method[1] + if obj.method then + return 's|' .. obj.method[1] + end elseif tp == 'getindex' or tp == 'setindex' or tp == 'tableindex' then - return m.getKeyName(obj.index) + if obj.index then + return m.getKeyName(obj.index) + end elseif tp == 'field' or tp == 'method' then return 's|' .. obj[1] - elseif tp == 'index' then - return m.getKeyName(obj.index) elseif tp == 'string' then local s = obj[1] if s then diff --git a/server-beta/src/searcher/init.lua b/server-beta/src/searcher/init.lua index 6ec7bc99..1734c2ea 100644 --- a/server-beta/src/searcher/init.lua +++ b/server-beta/src/searcher/init.lua @@ -6,4 +6,6 @@ require 'searcher.getGlobals' require 'searcher.getLinks' require 'searcher.getGlobal' require 'searcher.getLibrary' +require 'searcher.typeInference' +require 'searcher.isTrue' return searcher diff --git a/server-beta/src/searcher/isTrue.lua b/server-beta/src/searcher/isTrue.lua new file mode 100644 index 00000000..ba825d43 --- /dev/null +++ b/server-beta/src/searcher/isTrue.lua @@ -0,0 +1,52 @@ +local searcher = require 'searcher.searcher' + +local function checkLiteral(source) + if source.type == 'boolean' then + if source[1] == true then + return 'true' + else + return 'false' + end + end + if source.type == 'string' then + return 'true' + end + if source.type == 'number' then + return 'true' + end + if source.type == 'table' then + return 'true' + end + if source.type == 'function' then + return 'true' + end + if source.type == 'nil' then + return 'false' + end +end + +local function isTrue(source) + local res = checkLiteral(source) + if res then + return res + end + return 'unknown' +end + +function searcher.isTrue(source) + if not source then + return + end + local cache = searcher.cache.isTrue[source] + if cache ~= nil then + return cache + end + local unlock = searcher.lock('isTrue', source) + if not unlock then + return + end + cache = isTrue(source) or false + searcher.cache.isTrue[source] = cache + unlock() + return cache +end diff --git a/server-beta/src/searcher/searcher.lua b/server-beta/src/searcher/searcher.lua index 19e114d6..3e12430b 100644 --- a/server-beta/src/searcher/searcher.lua +++ b/server-beta/src/searcher/searcher.lua @@ -64,6 +64,8 @@ function m.refreshCache() getGlobal = {}, specialName = {}, getLibrary = {}, + typeInfer = {}, + isTrue = {}, specials = nil, } m.locked = { @@ -72,6 +74,8 @@ function m.refreshCache() getGlobals = {}, getLinks = {}, getLibrary = {}, + typeInfer = {}, + isTrue = {}, } m.cacheTracker[m.cache] = true end diff --git a/server-beta/src/searcher/typeInference.lua b/server-beta/src/searcher/typeInference.lua new file mode 100644 index 00000000..56f4cc98 --- /dev/null +++ b/server-beta/src/searcher/typeInference.lua @@ -0,0 +1,253 @@ +local searcher = require 'searcher.searcher' +local guide = require 'parser.guide' +local config = require 'config' + +local typeSort = { + ['boolean'] = 1, + ['string'] = 2, + ['integer'] = 3, + ['number'] = 4, + ['table'] = 5, + ['function'] = 6, + ['nil'] = math.maxinteger, +} + +local function merge(result, tp) + if result[tp] then + return + end + if tp:find('|', 1, true) then + for sub in tp:gmatch '[^|]+' do + if not result[sub] then + result[sub] = true + result[#result+1] = sub + end + end + else + result[tp] = true + result[#result+1] = tp + end +end + +local function hasType(tp, target) + if not target then + return false + end + for sub in target:gmatch '[^|]+' do + if sub == tp then + return true + end + end + return false +end + +local function dump(result) + if #result <= 1 then + return result[1] + end + table.sort(result, function (a, b) + local sa = typeSort[a] + local sb = typeSort[b] + if sa and sb then + return sa < sb + end + if not sa and not sb then + return a < b + end + if sa and not sb then + return true + end + if not sa and sb then + return false + end + return false + end) + return table.concat(result, '|') +end + +local function checkLiteral(source) + if source.type == 'string' then + return 'string' + elseif source.type == 'nil' then + return 'nil' + elseif source.type == 'boolean' then + return 'boolean' + elseif source.type == 'number' then + if math.type(source[1]) == 'integer' then + return 'integer' + else + return 'number' + end + elseif source.type == 'table' then + return 'table' + elseif source.type == 'function' then + return 'function' + end +end + +local function checkUnary(source) + if source.type ~= 'unary' then + return + end + local op = source.op + if op.type == 'not' then + return 'boolean' + elseif op.type == '#' + or op.type == '~' then + return 'integer' + elseif op.type == '-' then + if hasType('integer', searcher.typeInference(source[1])) then + return 'integer' + else + return 'number' + end + end +end + +local function checkBinary(source) + if source.type ~= 'binary' then + return + end + local op = source.op + if op.type == 'and' then + local isTrue = searcher.isTrue(source[1]) + if isTrue == 'true' then + return searcher.typeInference(source[2]) + elseif isTrue == 'false' then + return searcher.typeInference(source[1]) + else + local result = {} + merge(result, searcher.typeInference(source[1])) + merge(result, searcher.typeInference(source[2])) + return dump(result) + end + end + if op.type == 'or' then + local isTrue = searcher.isTrue(source[1]) + if isTrue == 'true' then + return searcher.typeInference(source[1]) + elseif isTrue == 'false' then + return searcher.typeInference(source[2]) + else + local result = {} + merge(result, searcher.typeInference(source[1])) + merge(result, searcher.typeInference(source[2])) + return dump(result) + end + end + if op.type == '==' + or op.type == '~=' + or op.type == '<=' + or op.type == '>=' + or op.type == '<' + or op.type == '>' then + return 'boolean' + end + if op.type == '|' + or op.type == '~' + or op.type == '&' + or op.type == '<<' + or op.type == '>>' then + return 'integer' + end + if op.type == '..' then + return 'string' + end + if op.type == '^' + or op.type == '/' then + return 'number' + end + -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数 + if op.type == '+' + or op.type == '-' + or op.type == '*' + or op.type == '%' + or op.type == '//' then + if hasType('integer', searcher.typeInference(source[1])) + and hasType('integer', searcher.typeInference(source[2])) then + return 'integer' + else + return 'number' + end + end +end + +local function checkValue(source) + if source.value then + return searcher.typeInference(source.value) + end +end + +local function checkCall(result, source) + if not source.parent then + return + end + if source.parent.type ~= 'call' then + return + end + if source.parent.node == source then + merge(result, 'function') + return + end +end + +local function checkNext(result, source) + local next = source.next + if not next then + return + end + if next.type == 'getfield' + or next.type == 'getindex' + or next.type == 'getmethod' + or next.type == 'setfield' + or next.type == 'setindex' + or next.type == 'setmethod' then + merge(result, 'table') + end +end + +local function checkDef(result, source) + searcher.eachDef(source, function (info) + local src = info.source + local tp = searcher.typeInference(src) + if tp then + merge(result, tp) + end + end) +end + +local function typeInference(source) + local tp = checkLiteral(source) + or checkValue(source) + or checkUnary(source) + or checkBinary(source) + if tp then + return tp + end + + local result = {} + + checkCall(result, source) + checkNext(result, source) + checkDef(result, source) + + return dump(result) +end + +function searcher.typeInference(source) + if not source then + return + end + local cache = searcher.cache.typeInfer[source] + if cache ~= nil then + return cache + end + local unlock = searcher.lock('typeInfer', source) + if not unlock then + return + end + cache = typeInference(source) or false + searcher.cache.typeInfer[source] = cache + unlock() + return cache +end |