summaryrefslogtreecommitdiff
path: root/server/src/matcher
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2018-12-28 15:36:56 +0800
committer最萌小汐 <sumneko@hotmail.com>2018-12-28 15:36:56 +0800
commita080b2516d2ce2efb65ad0fd26126cdfa6f6e4f8 (patch)
treeb5672e03d11a43a56b63fe02d7b685eacd21ace4 /server/src/matcher
parent549f6dd7ce8c733f9b51f5e93b3f14c1e2a44aca (diff)
downloadlua-language-server-a080b2516d2ce2efb65ad0fd26126cdfa6f6e4f8.zip
matcher -> core
Diffstat (limited to 'server/src/matcher')
-rw-r--r--server/src/matcher/completion.lua501
-rw-r--r--server/src/matcher/definition.lua88
-rw-r--r--server/src/matcher/diagnostics.lua269
-rw-r--r--server/src/matcher/document_symbol.lua77
-rw-r--r--server/src/matcher/env.lua139
-rw-r--r--server/src/matcher/find_lib.lua47
-rw-r--r--server/src/matcher/find_result.lua47
-rw-r--r--server/src/matcher/hover.lua482
-rw-r--r--server/src/matcher/implementation.lua88
-rw-r--r--server/src/matcher/init.lua16
-rw-r--r--server/src/matcher/library.lua177
-rw-r--r--server/src/matcher/references.lua38
-rw-r--r--server/src/matcher/rename.lua51
-rw-r--r--server/src/matcher/signature.lua66
-rw-r--r--server/src/matcher/vm.lua1501
15 files changed, 0 insertions, 3587 deletions
diff --git a/server/src/matcher/completion.lua b/server/src/matcher/completion.lua
deleted file mode 100644
index 508c0320..00000000
--- a/server/src/matcher/completion.lua
+++ /dev/null
@@ -1,501 +0,0 @@
-local findResult = require 'matcher.find_result'
-local hover = require 'matcher.hover'
-
-local CompletionItemKind = {
- Text = 1,
- Method = 2,
- Function = 3,
- Constructor = 4,
- Field = 5,
- Variable = 6,
- Class = 7,
- Interface = 8,
- Module = 9,
- Property = 10,
- Unit = 11,
- Value = 12,
- Enum = 13,
- Keyword = 14,
- Snippet = 15,
- Color = 16,
- File = 17,
- Reference = 18,
- Folder = 19,
- EnumMember = 20,
- Constant = 21,
- Struct = 22,
- Event = 23,
- Operator = 24,
- TypeParameter = 25,
-}
-
-local function matchKey(me, other)
- if me == other then
- return false
- end
- if me == '' then
- return true
- end
- if #me > #other then
- return false
- end
- local lMe = me:lower()
- local lOther = other:lower()
- if lMe:sub(1, 1) ~= lOther:sub(1, 1) then
- return false
- end
- if lMe == lOther:sub(1, #lMe) then
- return true
- end
- local used = {
- [1] = true,
- }
- local cur = 2
- local lookup
- local researched
- for i = 2, #lMe do
- local c = lMe:sub(i, i)
- -- 1. 看当前字符是否匹配
- if c == lOther:sub(cur, cur) then
- used[cur] = true
- goto NEXT
- end
- -- 2. 看前一个字符是否匹配
- if not used[cur-1] then
- if c == lOther:sub(cur-1, cur-1) then
- used[cur-1] = true
- goto NEXT
- end
- end
- -- 3. 向后找这个字
- lookup = lOther:find(c, cur+1, true)
- if lookup then
- cur = lookup
- used[cur] = true
- goto NEXT
- end
-
- -- 4. 重新搜索整个字符串,但是只允许1次,否则失败.如果找不到也失败
- if researched then
- return false
- else
- researched = true
- for j = 2, cur - 2 do
- if c == lOther:sub(j, j) then
- used[j] = true
- goto NEXT
- end
- end
- return false
- end
- -- 5. 找到下一个可用的字,如果超出长度就算成功
- ::NEXT::
- repeat
- cur = cur + 1
- until not used[cur]
- if cur > #lOther then
- break
- end
- end
- return true
-end
-
-local function searchLocals(vm, pos, name, callback)
- for _, loc in ipairs(vm.results.locals) do
- if loc.source.start == 0 then
- goto CONTINUE
- end
- if loc.source.start <= pos and loc.close >= pos then
- if matchKey(name, loc.key) then
- callback(loc)
- end
- end
- ::CONTINUE::
- end
-end
-
-local function searchFields(name, parent, object, callback)
- if not parent or not parent.value or not parent.value.child then
- return
- end
- for key, field in pairs(parent.value.child) do
- if type(key) ~= 'string' then
- goto CONTINUE
- end
- if object then
- if not field.value or field.value.type ~= 'function' then
- goto CONTINUE
- end
- end
- if type(name) == 'string' and matchKey(name, key) then
- callback(field)
- end
- ::CONTINUE::
- end
-end
-
-local KEYS = {'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while', 'toclose'}
-local function searchKeyWords(name, callback)
- for _, key in ipairs(KEYS) do
- if matchKey(name, key) then
- callback(key)
- end
- end
-end
-
-local function getKind(var, default)
- local value = var.value
- if default == CompletionItemKind.Variable then
- if value.type == 'function' then
- return CompletionItemKind.Function
- end
- end
- if default == CompletionItemKind.Field then
- local tp = type(value.value)
- if tp == 'number' or tp == 'integer' or tp == 'string' then
- return CompletionItemKind.Enum
- end
- if value.type == 'function' then
- if var.parent and var.parent.value and var.parent.value.ENV ~= true then
- return CompletionItemKind.Method
- else
- return CompletionItemKind.Function
- end
- end
- end
- return default
-end
-
-local function getDetail(var)
- local tp = type(var.value.value)
- if tp == 'boolean' then
- return ('= %q'):format(var.value.value)
- elseif tp == 'number' then
- if math.type(var.value.value) == 'integer' then
- return ('= %q'):format(var.value.value)
- else
- local str = ('= %.10f'):format(var.value.value)
- local dot = str:find('.', 1, true)
- local suffix = str:find('[0]+$', dot+2)
- if suffix then
- return str:sub(1, suffix-1)
- else
- return str
- end
- end
- elseif tp == 'string' then
- return ('= %q'):format(var.value.value)
- end
- return nil
-end
-
-local function getDocument(var, source)
- if var.value.type == 'function' then
- return {
- kind = 'markdown',
- value = hover(var, source),
- }
- end
- return nil
-end
-
-local function searchAsLocal(vm, pos, result, callback)
- searchFields(result.key, vm.results.locals[1], nil, function (var)
- callback(var, CompletionItemKind.Variable)
- end)
-
- -- 支持 local function
- if matchKey(result.key, 'function') then
- callback('function', CompletionItemKind.Keyword)
- end
-end
-
-local function searchAsArg(vm, pos, result, callback)
- searchFields(result.key, vm.results.locals[1], nil, function (var)
- if var.value.lib then
- return
- end
- callback(var, CompletionItemKind.Variable)
- end)
-end
-
-local function searchAsGlobal(vm, pos, result, callback)
- if result.key == '' then
- return
- end
- searchLocals(vm, pos, result.key, function (var)
- callback(var, CompletionItemKind.Variable)
- end)
- searchFields(result.key, vm.results.locals[1], nil, function (var)
- callback(var, CompletionItemKind.Field)
- end)
- searchKeyWords(result.key, function (name)
- callback(name, CompletionItemKind.Keyword)
- end)
-end
-
-local function searchAsSuffix(result, callback)
- searchFields(result.key, result.parent, result.source.object, function (var)
- callback(var, CompletionItemKind.Field)
- end)
-end
-
-local function searchInArg(vm, inCall, inString, callback)
- local lib = inCall.func.lib
- if not lib then
- return
- end
-
- -- require列举出可以引用到的文件
- if lib.special == 'require' then
- if not vm.lsp or not vm.lsp.workspace then
- return
- end
- local results = vm.lsp.workspace:matchPath(vm.uri, inString[1])
- if not results then
- return
- end
- for _, v in ipairs(results) do
- if v ~= inString[1] then
- callback(v, CompletionItemKind.File)
- end
- end
- end
-
- -- 其他库函数,根据参数位置找枚举值
- if lib.args and lib.enums then
- local arg = lib.args[inCall.select]
- local name = arg and arg.name
- for _, enum in ipairs(lib.enums) do
- if enum.name == name and enum.enum then
- if inString then
- callback(enum.enum, CompletionItemKind.EnumMember, {
- documentation = enum.description
- })
- else
- callback(('%q'):format(enum.enum), CompletionItemKind.EnumMember, {
- documentation = enum.description
- })
- end
- end
- end
- end
-end
-
-local function searchAsIndex(vm, pos, result, callback)
- searchLocals(vm, pos, result.key, function (var)
- callback(var, CompletionItemKind.Variable)
- end)
- for _, index in ipairs(vm.results.indexs) do
- if matchKey(result.key, index) then
- callback(index, CompletionItemKind.Property)
- end
- end
- searchFields(result.key, vm.results.locals[1], nil, function (var)
- callback(var, CompletionItemKind.Field)
- end)
-end
-
-local function findClosePos(vm, pos)
- local curDis = math.maxinteger
- local parent = nil
- local function found(object, source)
- local dis = pos - source.finish
- if dis > 1 and dis < curDis then
- curDis = dis
- parent = object
- end
- end
- for sources, object in pairs(vm.results.sources) do
- if sources.type == 'multi-source' then
- for _, source in ipairs(sources) do
- if source.type ~= 'simple' then
- found(object, source)
- end
- end
- else
- found(object, sources)
- end
- end
- if not parent then
- return nil
- end
- if parent.type ~= 'local' and parent.type ~= 'field' then
- return nil
- end
- -- 造个假的 DirtyName
- local source = {
- type = 'name',
- start = pos,
- finish = pos,
- [1] = '',
- }
- local result = {
- type = 'field',
- parent = parent,
- key = '',
- source = source,
- }
- return result, source
-end
-
-local function isContainPos(obj, pos)
- if obj.start <= pos and obj.finish + 1 >= pos then
- return true
- end
- return false
-end
-
-local function findString(vm, pos)
- for _, source in ipairs(vm.results.strings) do
- if isContainPos(source, pos) then
- return source
- end
- end
- return nil
-end
-
-local function findArgCount(args, pos)
- for i, arg in ipairs(args) do
- if isContainPos(arg, pos) then
- return i
- end
- end
- return #args + 1
-end
-
--- 找出范围包含pos的call
-local function findCall(vm, pos)
- local results = {}
- for _, call in ipairs(vm.results.calls) do
- if isContainPos(call.args, pos) then
- local n = findArgCount(call.args, pos)
- local var = vm.results.sources[call.lastObj]
- if var then
- results[#results+1] = {
- func = call.func,
- var = var,
- source = call.lastObj,
- select = n,
- args = call.args,
- }
- end
- end
- end
- if #results == 0 then
- return nil
- end
- -- 可能处于 'func1(func2(' 的嵌套中,因此距离越远的函数层级越低
- table.sort(results, function (a, b)
- return a.args.start < b.args.start
- end)
- return results[#results]
-end
-
-local function makeList(source)
- local list = {}
- local mark = {}
- local function callback(var, defualt, data)
- local key
- if type(var) == 'string' then
- key = var
- else
- key = var.key
- end
- if mark[key] then
- return
- end
- mark[key] = true
- data = data or {}
- list[#list+1] = data
- if var == key then
- data.label = var
- data.kind = defualt
- else
- data.label = var.key
- data.kind = getKind(var, defualt)
- data.detail = data.detail or getDetail(var)
- data.documentation = data.documentation or getDocument(var, source)
- end
- end
- return list, callback
-end
-
-local function searchInResult(result, source, vm, pos, callback)
- if result.type == 'local' then
- if source.isArg then
- searchAsArg(vm, pos, result, callback)
- elseif source.isLocal then
- searchAsLocal(vm, pos, result, callback)
- else
- searchAsGlobal(vm, pos, result, callback)
- end
- elseif result.type == 'field' then
- if source.isIndex then
- searchAsIndex(vm, pos, result, callback)
- elseif result.parent and result.parent.value and result.parent.value.ENV == true then
- searchAsGlobal(vm, pos, result, callback)
- else
- searchAsSuffix(result, callback)
- end
- end
-end
-
-local function searchSpecial(vm, pos, callback)
- -- 尝试 #
- local result, source = findResult(vm, pos, 2)
- if source and source.type == 'index'
- and result.source and result.source.op == '#'
- then
- local name = {}
- local var = result
- while true do
- var = var.parent
- if not var then
- break
- end
- if var.value and var.value.ENV then
- break
- end
- local key = var.key
- if type(key) ~= 'string' or key == '' then
- return
- end
- table.insert(name, 1, key)
- end
- local label = table.concat(name, '.') .. '+1'
- callback(label, CompletionItemKind.Snippet, {
- textEdit = {
- start = result.source.start + 1,
- finish = source.finish,
- newText = ('%s] = '):format(label),
- }
- })
- end
-end
-
-return function (vm, pos)
- local result, source = findResult(vm, pos)
- if not result then
- result, source = findClosePos(vm, pos)
- end
-
- if not result then
- return nil
- end
-
- local list, callback = makeList(source)
- local inCall = findCall(vm, pos)
- local inString = findString(vm, pos)
- if inCall then
- searchInArg(vm, inCall, inString, callback)
- end
- searchSpecial(vm, pos, callback)
- if not inString then
- searchInResult(result, source, vm, pos, callback)
- end
- if #list == 0 then
- return nil
- end
- return list
-end
diff --git a/server/src/matcher/definition.lua b/server/src/matcher/definition.lua
deleted file mode 100644
index 3ca27b89..00000000
--- a/server/src/matcher/definition.lua
+++ /dev/null
@@ -1,88 +0,0 @@
-local function parseResultAcrossUri(positions, vm, result)
- -- 跨越文件时,遍历的是值的绑定信息
- for _, info in ipairs(result.value) do
- if info.type == 'set' and info.source.uri == result.value.uri then
- positions[1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- if #positions == 0 then
- for _, info in ipairs(result.value) do
- if info.type == 'return' and info.source.uri == result.value.uri then
- positions[1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- if #positions == 0 then
- positions[1] = {
- 0, 0,
- result.value.uri,
- }
- end
-end
-
-local function parseResult(vm, result)
- local positions = {}
- local tp = result.type
- if tp == 'local' then
- if result.value.uri ~= vm.uri then
- parseResultAcrossUri(positions, vm, result)
- else
- for _, info in ipairs(result) do
- if info.type == 'local' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- elseif tp == 'field' then
- if result.value.uri ~= vm.uri then
- parseResultAcrossUri(positions, vm, result)
- else
- for _, info in ipairs(result) do
- if info.type == 'set' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- elseif tp == 'label' then
- for _, info in ipairs(result) do
- if info.type == 'set' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- }
- end
- end
- elseif tp == 'string' then
- -- require 'XXX' 专用
- positions[#positions+1] = {
- 0,
- 0,
- result.uri,
- }
- end
- return positions
-end
-
-return function (vm, result)
- if not result then
- return nil
- end
- local positions = parseResult(vm, result)
- return positions
-end
diff --git a/server/src/matcher/diagnostics.lua b/server/src/matcher/diagnostics.lua
deleted file mode 100644
index a6672c0b..00000000
--- a/server/src/matcher/diagnostics.lua
+++ /dev/null
@@ -1,269 +0,0 @@
-local lang = require 'language'
-
-local DiagnosticSeverity = {
- Error = 1,
- Warning = 2,
- Information = 3,
- Hint = 4,
-}
-
-local function searchUnusedLocals(results, callback)
- for _, var in ipairs(results.locals) do
- if var.key == 'self'
- or var.key == '_'
- or var.key == '_ENV'
- or var.key == ''
- then
- goto NEXT_VAR
- end
- for _, info in ipairs(var) do
- if info.type == 'get' then
- goto NEXT_VAR
- end
- if info.type == 'local' then
- if info.source.start == 0 then
- goto NEXT_VAR
- end
- end
- end
- callback(var.source.start, var.source.finish, var.key)
- ::NEXT_VAR::
- end
-end
-
-local function searchUndefinedGlobal(results, callback)
- local env = results.locals[1]
- for index, field in pairs(env.value.child) do
- if field.value.lib then
- goto NEXT_VAR
- end
- if type(index) ~= 'string' then
- goto NEXT_VAR
- end
- local lIndex = index:lower()
- if lIndex == 'log' or lIndex == 'arg' or lIndex == '' then
- goto NEXT_VAR
- end
- if not index:find '%l' then
- goto NEXT_VAR
- end
- for _, info in ipairs(field) do
- if info.type == 'set' then
- goto NEXT_VAR
- end
- end
- for _, info in ipairs(field) do
- if info.type == 'get' then
- callback(info.source.start, info.source.finish, tostring(index))
- end
- end
- ::NEXT_VAR::
- end
-end
-
-local function searchUnusedLabel(results, callback)
- for _, label in ipairs(results.labels) do
- for _, info in ipairs(label) do
- if info.type == 'goto' then
- goto NEXT_LABEL
- end
- end
- for _, info in ipairs(label) do
- if info.type == 'set' then
- callback(info.source.start, info.source.finish, label.key)
- end
- end
- ::NEXT_LABEL::
- end
-end
-
-local function isContainPos(obj, start, finish)
- if obj.start <= start and obj.finish + 1 >= finish then
- return true
- end
- return false
-end
-
-local function isInString(vm, start, finish)
- for _, source in ipairs(vm.results.strings) do
- if isContainPos(source, start, finish) then
- return true
- end
- end
- return false
-end
-
-local function searchSpaces(vm, lines, callback)
- for i = 1, #lines do
- local line = lines:line(i)
-
- if line:find '^[ \t]+$' then
- local start, finish = lines:range(i)
- if isInString(vm, start, finish) then
- goto NEXT_LINE
- end
- callback(start, finish, lang.script.DIAG_LINE_ONLY_SPACE)
- goto NEXT_LINE
- end
-
- local pos = line:find '[ \t]+$'
- if pos then
- local start, finish = lines:range(i)
- start = start + pos - 1
- if isInString(vm, start, finish) then
- goto NEXT_LINE
- end
- callback(start, finish, lang.script.DIAG_LINE_POST_SPACE)
- goto NEXT_LINE
- end
- ::NEXT_LINE::
- end
-end
-
-local function searchRedefinition(results, uri, callback)
- local used = {}
- for _, var in ipairs(results.locals) do
- if var.key == '_'
- or var.key == '_ENV'
- or var.key == ''
- then
- goto NEXT_VAR
- end
- local shadow = var.shadow
- if not shadow then
- goto NEXT_VAR
- end
- if used[shadow] then
- goto NEXT_VAR
- end
- used[shadow] = true
- -- 如果多次重定义,则不再警告
- if #shadow >= 4 then
- goto NEXT_VAR
- end
- local related = {}
- for i = 1, #shadow do
- related[i] = {
- start = shadow[i].source.start,
- finish = shadow[i].source.finish,
- uri = uri,
- }
- end
- for i = 2, #shadow do
- callback(shadow[i].source.start, shadow[i].source.finish, shadow[i].key, related)
- end
- ::NEXT_VAR::
- end
-end
-
-local function searchNewLineCall(results, lines, callback)
- for _, call in ipairs(results.calls) do
- if not call.nextObj then
- goto NEXT_CALL
- end
- if not call.lastObj.start then
- goto NEXT_CALL
- end
- local callline = lines:rowcol(call.args.start)
- local lastline = lines:rowcol(call.lastObj.finish)
- if callline > lastline then
- callback(call.args.start, call.args.finish)
- end
- ::NEXT_CALL::
- end
-end
-
-local function searchRedundantParameters(results, callback)
- for _, call in ipairs(results.calls) do
- if not call.func.built then
- goto NEXT_CALL
- end
- if call.func.hasDots then
- goto NEXT_CALL
- end
- if not call.func.args then
- return
- end
- local max = #call.func.args
- local passed = #call.args
- for i = max + 1, passed do
- local source = call.args[i]
- if source.start then
- callback(source.start, source.finish, max, passed)
- else
- log.error('No start: ', table.dump(source))
- end
- end
- ::NEXT_CALL::
- end
-end
-
-return function (vm, lines, uri)
- local datas = {}
- local results = vm.results
- -- 未使用的局部变量
- searchUnusedLocals(results, function (start, finish, key)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Information,
- message = lang.script('DIAG_UNUSED_LOCAL', key),
- }
- end)
- -- 读取未定义全局变量
- searchUndefinedGlobal(results, function (start, finish, key)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Warning,
- message = lang.script('DIAG_UNDEFINED_GLOBAL', key),
- }
- end)
- -- 未使用的Label
- searchUnusedLabel(results, function (start, finish, key)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level =DiagnosticSeverity.Information,
- message = lang.script('DIAG_UNUSED_LABEL', key)
- }
- end)
- -- 只有空格与制表符的行,以及后置空格
- searchSpaces(vm, lines, function (start, finish, message)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Information,
- message = message,
- }
- end)
- -- 重定义局部变量
- searchRedefinition(results, uri, function (start, finish, key, related)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Information,
- message = lang.script('DIAG_REDEFINED_LOCAL', key),
- related = related,
- }
- end)
- -- 以括号开始的一行(可能被误解析为了上一行的call)
- searchNewLineCall(results, lines, function (start, finish)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Information,
- message = lang.script.DIAG_PREVIOUS_CALL,
- }
- end)
- -- 调用函数时的参数数量是否超过函数的接收数量
- searchRedundantParameters(results, function (start, finish, max, passed)
- datas[#datas+1] = {
- start = start,
- finish = finish,
- level = DiagnosticSeverity.Warning,
- message = lang.script('DIAG_OVER_MAX_ARGS', max, passed),
- }
- end)
- return datas
-end
diff --git a/server/src/matcher/document_symbol.lua b/server/src/matcher/document_symbol.lua
deleted file mode 100644
index b6802c93..00000000
--- a/server/src/matcher/document_symbol.lua
+++ /dev/null
@@ -1,77 +0,0 @@
-local hover = require 'matcher.hover'
-
-local SymbolKind = {
- File = 1,
- Module = 2,
- Namespace = 3,
- Package = 4,
- Class = 5,
- Method = 6,
- Property = 7,
- Field = 8,
- Constructor = 9,
- Enum = 10,
- Interface = 11,
- Function = 12,
- Variable = 13,
- Constant = 14,
- String = 15,
- Number = 16,
- Boolean = 17,
- Array = 18,
- Object = 19,
- Key = 20,
- Null = 21,
- EnumMember = 22,
- Struct = 23,
- Event = 24,
- Operator = 25,
- TypeParameter = 26,
-}
-
-local function buildFunc(vm, func, nextFunction, nextFinish)
- local source = func.source
- local var = vm.results.sources[source.name]
- if not var then
- return
- end
- local hvr = hover(var, source.name)
- if not hvr then
- return
- end
- return {
- name = hvr.name,
- detail = hvr.label,
- kind = SymbolKind.Function,
- range = { source.start, source.finish },
- selectionRange = { source.name.start, source.name.finish },
- }
-end
-
-return function (vm)
- local i = 0
- local function nextFunction()
- i = i + 1
- local func = vm.results.funcs[i]
- return func
- end
-
- local function nextFinish()
- local func = vm.results.funcs[i+1]
- if not func then
- return 0
- end
- return func.source.finish
- end
-
- local symbols = {}
- while true do
- local func = nextFunction()
- if not func then
- break
- end
- symbols[1] = buildFunc(vm, func, nextFunction, nextFinish)
- end
-
- return symbols
-end
diff --git a/server/src/matcher/env.lua b/server/src/matcher/env.lua
deleted file mode 100644
index ada26145..00000000
--- a/server/src/matcher/env.lua
+++ /dev/null
@@ -1,139 +0,0 @@
-local setmetatable = setmetatable
-local pairs = pairs
-local type = type
-local table_sort = table.sort
-
-return function (root)
- local env = {root}
- local is_table = {}
- for key, value in pairs(root) do
- if type(value) == 'table' then
- is_table[key] = true
- end
- end
- root._next = nil
- root._cut = {}
-
- local mt = { _env = env }
- function mt:push()
- env[#env+1] = { _next = env[#env], _cut = {} }
- end
- function mt:pop()
- env[#env] = nil
- end
- function mt:cut(key)
- env[#env]._cut[key] = true
- end
- function mt:__index(key)
- local origin = env[#env]
- if is_table[key] then
- return setmetatable({}, {
- __index = function (_, ckey)
- local o = origin
- while o do
- local t = o[key]
- if t and t[ckey] ~= nil then
- return t[ckey]
- end
- o = not o._cut[key] and o._next
- end
- end,
- __newindex = function (_, ckey, value)
- local o = origin
- if not o[key] then
- o[key] = {}
- end
- o[key][ckey] = value
- end,
- __pairs = function ()
- local o = origin
- local tbl = {}
- while o do
- local t = o[key]
- if t then
- for k, v in pairs(t) do
- if tbl[k] == nil then
- tbl[k] = v
- end
- end
- end
- o = not o._cut[key] and o._next
- end
- return next, tbl
- end,
- })
- else
- local o = origin
- while o do
- if o[key] ~= nil then
- return o[key]
- end
- o = not o._cut[key] and o._next
- end
- end
- end
- function mt:__newindex(key, value)
- local o = env[#env]
- if is_table[key] then
- if type(o[key]) ~= 'table' then
- o[key] = {}
- end
- if type(value) == 'table' then
- for k, v in pairs(value) do
- o[key][k] = v
- end
- else
- error(('[env.%s] should be table, got [%s]'):format(key, value))
- end
- else
- o[key] = value
- end
- end
- function mt:__pairs()
- local keys = {}
- local cuted = {}
- local result = {}
- local o = env[#env]
- while true do
- for key in pairs(o._cut) do
- cuted[key] = true
- end
- for key, value in pairs(o) do
- if key == '_cut' or key == '_next' then
- goto CONTINUE
- end
- if cuted[key] then
- goto CONTINUE
- end
- if result[key] == nil then
- keys[#keys+1] = key
- if is_table[key] then
- result[key] = {}
- else
- result[key] = value
- end
- end
- if is_table[key] then
- for k, v in pairs(value) do
- if result[key][k] == nil then
- result[key][k] = v
- end
- end
- end
- ::CONTINUE::
- end
- o = o._next
- if not o then
- break
- end
- end
- table_sort(keys)
- local i = 0
- return function ()
- i = i + 1
- local k = keys[i]
- return k, result[k]
- end
- end
- return setmetatable(mt, mt)
-end
diff --git a/server/src/matcher/find_lib.lua b/server/src/matcher/find_lib.lua
deleted file mode 100644
index c4d5bff3..00000000
--- a/server/src/matcher/find_lib.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-local function findLib(var)
- if var.type ~= 'local' and var.type ~= 'field' then
- return nil
- end
- local value = var.value
- local lib = value.lib
- if not lib then
- return nil
- end
- if lib.parent then
- local res
- for _, parent in ipairs(lib.parent) do
- if parent.type == value.parentType then
- res = parent
- end
- end
- if not res then
- res = lib.parent[1]
- end
- if res.type == 'object' then
- local fullKey = ('*%s:%s'):format(res.nick or res.name, lib.name)
- return lib, fullKey, true
- else
- local fullKey = ('%s.%s'):format(res.nick or res.name, lib.name)
- return lib, fullKey, false
- end
- else
- local res
- if not lib.source then
- return lib, lib.nick or lib.name, false
- end
- for _, source in ipairs(lib.source) do
- if source.type == value.parentType then
- res = source
- end
- end
- if not res then
- return lib, lib.nick or lib.name, false
- end
- return lib, res.nick or res.name or lib.nick or lib.name, false
- end
-end
-
-return function (var)
- local lib, fullKey, oo = findLib(var)
- return lib, fullKey, oo
-end
diff --git a/server/src/matcher/find_result.lua b/server/src/matcher/find_result.lua
deleted file mode 100644
index ac8dc3f4..00000000
--- a/server/src/matcher/find_result.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-local function isContainPos(obj, pos)
- if obj.start <= pos and obj.finish + 1 >= pos then
- return true
- end
- return false
-end
-
-local function findAtPos(results, pos, level)
- local res = {}
- for sources, object in pairs(results.sources) do
- if sources.type == 'multi-source' then
- for _, source in ipairs(sources) do
- if source.type ~= 'simple' and isContainPos(source, pos) then
- res[#res+1] = {
- object = object,
- source = source,
- range = source.finish - source.start,
- }
- end
- end
- else
- local source = sources
- if source.type ~= 'simple' and isContainPos(source, pos) then
- res[#res+1] = {
- object = object,
- source = source,
- range = source.finish - source.start,
- }
- end
- end
- end
- if #res == 0 then
- return nil
- end
- table.sort(res, function (a, b)
- return a.range < b.range
- end)
- local data = res[level or 1]
- if not data then
- return nil
- end
- return data.object, data.source
-end
-
-return function (vm, pos, level)
- return findAtPos(vm.results, pos, level)
-end
diff --git a/server/src/matcher/hover.lua b/server/src/matcher/hover.lua
deleted file mode 100644
index 5239f3b8..00000000
--- a/server/src/matcher/hover.lua
+++ /dev/null
@@ -1,482 +0,0 @@
-local findLib = require 'matcher.find_lib'
-
-local OriginTypes = {
- ['any'] = true,
- ['nil'] = true,
- ['integer'] = true,
- ['number'] = true,
- ['boolean'] = true,
- ['string'] = true,
- ['thread'] = true,
- ['userdata'] = true,
- ['table'] = true,
- ['function'] = true,
-}
-
-local function buildLibArgs(lib, oo, select)
- if not lib.args then
- return ''
- end
- local start
- if oo then
- start = 2
- if select then
- select = select + 1
- end
- else
- start = 1
- end
- local strs = {}
- local argLabel
- for i = start, #lib.args do
- local arg = lib.args[i]
- if arg.optional then
- if i > start then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > start then
- strs[#strs+1] = ', '
- end
-
- local argStr = {}
- if arg.name then
- argStr[#argStr+1] = ('%s: '):format(arg.name)
- end
- if type(arg.type) == 'table' then
- argStr[#argStr+1] = table.concat(arg.type, '/')
- else
- argStr[#argStr+1] = arg.type or 'any'
- end
- if arg.default then
- argStr[#argStr+1] = ('(%q)'):format(arg.default)
- end
-
- for _, str in ipairs(argStr) do
- strs[#strs+1] = str
- end
- if arg.optional == 'self' then
- strs[#strs+1] = ']'
- end
- if i == select then
- argLabel = table.concat(argStr)
- end
- end
- for _, arg in ipairs(lib.args) do
- if arg.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- return table.concat(strs), argLabel
-end
-
-local function buildLibReturns(lib)
- if not lib.returns then
- return ''
- end
- local strs = {}
- for i, rtn in ipairs(lib.returns) do
- if rtn.optional then
- if i > 1 then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > 1 then
- strs[#strs+1] = ', '
- end
- if rtn.name then
- strs[#strs+1] = ('%s: '):format(rtn.name)
- end
- if type(rtn.type) == 'table' then
- strs[#strs+1] = table.concat(rtn.type, '/')
- else
- strs[#strs+1] = rtn.type or 'any'
- end
- if rtn.default then
- strs[#strs+1] = ('(%q)'):format(rtn.default)
- end
- if rtn.optional == 'self' then
- strs[#strs+1] = ']'
- end
- end
- for _, rtn in ipairs(lib.returns) do
- if rtn.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- return '\n -> ' .. table.concat(strs)
-end
-
-local function buildEnum(lib)
- if not lib.enums then
- return ''
- end
- local container = table.container()
- for _, enum in ipairs(lib.enums) do
- if not enum.name or (not enum.enum and not enum.code) then
- goto NEXT_ENUM
- end
- if not container[enum.name] then
- container[enum.name] = {}
- if lib.args then
- for _, arg in ipairs(lib.args) do
- if arg.name == enum.name then
- container[enum.name].type = arg.type
- break
- end
- end
- end
- if lib.returns then
- for _, rtn in ipairs(lib.returns) do
- if rtn.name == enum.name then
- container[enum.name].type = rtn.type
- break
- end
- end
- end
- end
- table.insert(container[enum.name], enum)
- ::NEXT_ENUM::
- end
- local strs = {}
- for name, enums in pairs(container) do
- local tp
- if type(enums.type) == 'table' then
- tp = table.concat(enums.type, '/')
- else
- tp = enums.type
- end
- strs[#strs+1] = ('\n%s: %s'):format(name, tp or 'any')
- for _, enum in ipairs(enums) do
- if enum.default then
- strs[#strs+1] = '\n -> '
- else
- strs[#strs+1] = '\n | '
- end
- if enum.code then
- strs[#strs+1] = tostring(enum.code)
- else
- strs[#strs+1] = ('%q'):format(enum.enum)
- end
- if enum.description then
- strs[#strs+1] = ' -- ' .. enum.description
- end
- end
- end
- return table.concat(strs)
-end
-
-local function buildValueName(result, source)
- local func = result.value
- local declarat = func.declarat or source
- if declarat then
- local key
- if declarat.type == 'name' then
- key = declarat[1]
- elseif declarat.type == 'string' then
- key = ('%q'):format(declarat[1])
- elseif declarat.type == 'number' or declarat.type == 'boolean' then
- key = tostring(declarat[1])
- elseif func.type == 'function' then
- key = ''
- elseif type(result.key) == 'string' then
- key = result.key
- else
- key = ''
- end
-
- local parentName = declarat.parentName
-
- if not parentName then
- return key or ''
- end
-
- if parentName == '?' then
- local parentType = result.parentValue and result.parentValue.type
- if parentType == 'table' then
- else
- parentName = '*' .. parentType
- end
- end
- if source.object then
- return parentName .. ':' .. key
- else
- if parentName then
- if declarat.index then
- return parentName .. '[' .. key .. ']'
- else
- return parentName .. '.' .. key
- end
- else
- return key
- end
- end
- end
- return result.key or ''
-end
-
-local function buildValueArgs(result, source, select)
- local func = result.value
- local names = {}
- local values = {}
- if func.args then
- for i, arg in ipairs(func.args) do
- names[i] = arg.key
- end
- end
- if func.argValues then
- for i, value in ipairs(func.argValues) do
- values[i] = value.type
- end
- end
- local strs = {}
- local argLabel
- local start = 1
- if source.object then
- start = 2
- if select then
- select = select + 1
- end
- end
- local max
- if func.built then
- max = #names
- else
- max = math.max(#names, #values)
- end
- for i = start, max do
- local name = names[i]
- local value = values[i] or 'any'
- if name then
- strs[#strs+1] = name .. ': ' .. value
- else
- strs[#strs+1] = value
- end
- if i == select then
- argLabel = strs[#strs]
- end
- end
- if func.hasDots then
- strs[#strs+1] = '...'
- end
- return table.concat(strs, ', '), argLabel
-end
-
-local function buildValueReturns(result)
- local func = result.value
- if not func.hasReturn then
- return ''
- end
- local strs = {}
- if func.returns then
- for i, rtn in ipairs(func.returns) do
- strs[i] = rtn.type
- end
- end
- if #strs == 0 then
- strs[1] = 'any'
- end
- return '\n -> ' .. table.concat(strs, ', ')
-end
-
-local function getFunctionHover(name, result, source, lib, oo, select)
- local args = ''
- local returns
- local enum
- local tip
- local argLabel
- if lib then
- args, argLabel = buildLibArgs(lib, oo, select)
- returns = buildLibReturns(lib)
- enum = buildEnum(lib)
- tip = lib.description
- else
- args, argLabel = buildValueArgs(result, source, select)
- returns = buildValueReturns(result)
- end
- local title = ('function %s(%s)%s'):format(name, args, returns)
- return {
- label = title,
- description = tip,
- enum = enum,
- argLabel = argLabel,
- }
-end
-
-local function findClass(result)
- -- 根据部分字段尝试找出自定义类型
- local metatable = result.value.metatable
- if not metatable or not metatable.child then
- return nil
- end
- -- 查找meta表的 __name 字段
- local name = metatable.child['__name']
- -- 值必须是字符串
- if name and name.value and type(name.value.value) == 'string' then
- return name.value.value
- end
- -- 查找meta表 __index 里的字段
- local index = metatable.child['__index']
- if index and index.value and index.value.child then
- for key, field in pairs(index.value.child) do
- -- 键值类型必须均为字符串
- if type(key) ~= 'string' then
- goto CONTINUE
- end
- if not field.value or type(field.value.value) ~= 'string' then
- goto CONTINUE
- end
- local lKey = key:lower()
- if lKey == 'type' or lKey == 'name' or lKey == 'class' then
- -- 必须只有过一次赋值
- local hasSet = false
- for _, info in ipairs(field) do
- if info.type == 'set' then
- if hasSet then
- goto CONTINUE
- end
- hasSet = true
- end
- end
- return field.value.value
- end
- ::CONTINUE::
- end
- end
- return nil
-end
-
-local function unpackTable(result)
- local child = result.value and result.value.child
- if not child then
- return '{}'
- end
- local lines = {'{'}
- for key, field in pairs(child) do
- local kType = type(key)
- if kType == 'table' then
- key = ('[*%s]'):format(key.type)
- else
- if kType ~= 'string' then
- key = ('[%s]'):format(key)
- end
- end
-
- local value = field.value
- if not value then
- lines[#lines+1] = (' %s: %s,'):format(key, 'any')
- goto CONTINUE
- end
-
- local vType = type(value.value)
- if vType == 'boolean' or vType == 'integer' or vType == 'number' or vType == 'string' then
- lines[#lines+1] = (' %s: %s = %q,'):format(key, value.type, value.value)
- else
- lines[#lines+1] = (' %s: %s,'):format(key, value.type)
- end
- ::CONTINUE::
- end
- lines[#lines+1] = '}'
- return table.concat(lines, '\r\n')
-end
-
-local function getValueHover(name, valueType, result, source, lib)
- if not lib then
- local class = findClass(result)
- if class then
- valueType = class
- end
- end
-
- if not OriginTypes[valueType] then
- valueType = '*' .. valueType
- end
-
- local value
- local tip
- if lib then
- value = lib.code or (lib.value and ('%q'):format(lib.value))
- tip = lib.description
- else
- value = result.value.value and ('%q'):format(result.value.value)
- end
-
- local tp = result.type
- if tp == 'field' then
- if result.parent and result.parent.value and result.parent.value.ENV then
- tp = 'global'
- end
- end
-
- local text
- if valueType == 'table' then
- text = ('%s %s: %s'):format(tp, name, unpackTable(result))
- else
- if value == nil then
- text = ('%s %s: %s'):format(tp, name, valueType)
- else
- text = ('%s %s: %s = %s'):format(tp, name, valueType, value)
- end
- end
- return {
- label = text,
- description = tip,
- }
-end
-
-local function getStringHover(result, lsp)
- if not result.uri then
- return nil
- end
- if not lsp or not lsp.workspace then
- return nil
- end
- local path = lsp.workspace:relativePathByUri(result.uri)
- return {
- description = ('[%s](%s)'):format(path:string(), result.uri),
- }
-end
-
-return function (result, source, lsp, select)
- if not result.value then
- return
- end
-
- if result.key == '' then
- return
- end
-
- if result.type == 'string' then
- return getStringHover(result, lsp)
- end
-
- if result.type ~= 'local' and result.type ~= 'field' then
- return
- end
-
- local lib, fullKey, oo = findLib(result)
- local valueType = lib and lib.type
- if valueType then
- if type(valueType) == 'table' then
- valueType = valueType[1]
- end
- else
- valueType = result.value.type or 'nil'
- end
- local name = fullKey or buildValueName(result, source)
- local hover
- if valueType == 'function' then
- hover = getFunctionHover(name, result, source, lib, oo, select)
- else
- hover = getValueHover(name, valueType, result, source, lib)
- end
- if not hover then
- return
- end
- hover.name = name
- return hover
-end
diff --git a/server/src/matcher/implementation.lua b/server/src/matcher/implementation.lua
deleted file mode 100644
index 9ff3fdca..00000000
--- a/server/src/matcher/implementation.lua
+++ /dev/null
@@ -1,88 +0,0 @@
-local function parseResultAcrossUri(positions, vm, result)
- -- 跨越文件时,遍历的是值的绑定信息
- for _, info in ipairs(result.value) do
- if info.type == 'set' and info.source.uri == result.value.uri then
- positions[1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- if #positions == 0 then
- for _, info in ipairs(result.value) do
- if info.type == 'return' and info.source.uri == result.value.uri then
- positions[1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- if #positions == 0 then
- positions[1] = {
- 0, 0,
- result.value.uri,
- }
- end
-end
-
-local function parseResult(vm, result)
- local positions = {}
- local tp = result.type
- if tp == 'local' then
- if result.value.uri ~= vm.uri then
- parseResultAcrossUri(positions, vm, result)
- else
- for _, info in ipairs(result) do
- if info.type == 'set' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- elseif tp == 'field' then
- if result.value.uri ~= vm.uri then
- parseResultAcrossUri(positions, vm, result)
- else
- for _, info in ipairs(result) do
- if info.type == 'set' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- info.source.uri,
- }
- end
- end
- end
- elseif tp == 'label' then
- for _, info in ipairs(result) do
- if info.type == 'set' then
- positions[#positions+1] = {
- info.source.start,
- info.source.finish,
- }
- end
- end
- elseif tp == 'string' then
- -- require 'XXX' 专用
- positions[#positions+1] = {
- 0,
- 0,
- result.uri,
- }
- end
- return positions
-end
-
-return function (vm, result)
- if not result then
- return nil
- end
- local positions = parseResult(vm, result)
- return positions
-end
diff --git a/server/src/matcher/init.lua b/server/src/matcher/init.lua
deleted file mode 100644
index cf33359a..00000000
--- a/server/src/matcher/init.lua
+++ /dev/null
@@ -1,16 +0,0 @@
-local api = {
- definition = require 'matcher.definition',
- implementation = require 'matcher.implementation',
- references = require 'matcher.references',
- rename = require 'matcher.rename',
- hover = require 'matcher.hover',
- diagnostics = require 'matcher.diagnostics',
- findResult = require 'matcher.find_result',
- findLib = require 'matcher.find_lib',
- completion = require 'matcher.completion',
- signature = require 'matcher.signature',
- documentSymbol = require 'matcher.document_symbol',
- vm = require 'matcher.vm',
-}
-
-return api
diff --git a/server/src/matcher/library.lua b/server/src/matcher/library.lua
deleted file mode 100644
index b268b368..00000000
--- a/server/src/matcher/library.lua
+++ /dev/null
@@ -1,177 +0,0 @@
-local lni = require 'lni'
-local fs = require 'bee.filesystem'
-
-local function mergeEnum(lib, locale)
- if not lib or not locale then
- return
- end
- local pack = {}
- for _, enum in ipairs(lib) do
- if enum.enum then
- pack[enum.enum] = enum
- end
- if enum.code then
- pack[enum.code] = enum
- end
- end
- for _, enum in ipairs(locale) do
- if pack[enum.enum] then
- if enum.description then
- pack[enum.enum].description = enum.description
- end
- end
- if pack[enum.code] then
- if enum.description then
- pack[enum.code].description = enum.description
- end
- end
- end
-end
-
-local function mergeField(lib, locale)
- if not lib or not locale then
- return
- end
- local pack = {}
- for _, field in ipairs(lib) do
- if field.field then
- pack[field.field] = field
- end
- end
- for _, field in ipairs(locale) do
- if pack[field.field] then
- if field.description then
- pack[field.field].description = field.description
- end
- end
- end
-end
-
-local function mergeLocale(libs, locale)
- if not libs or not locale then
- return
- end
- for name in pairs(locale) do
- if libs[name] then
- if locale[name].description then
- libs[name].description = locale[name].description
- end
- mergeEnum(libs[name].enums, locale[name].enums)
- mergeField(libs[name].fields, locale[name].fields)
- end
- end
-end
-
-local function mergeSource(alllibs, name, lib)
- if not lib.source then
- alllibs.global[name] = lib
- return
- end
- for _, source in ipairs(lib.source) do
- local sourceName = source.name or name
- if source.type == 'global' then
- alllibs.global[sourceName] = lib
- elseif source.type == 'library' then
- alllibs.library[sourceName] = lib
- elseif source.type == 'object' then
- alllibs.object[sourceName] = lib
- end
- end
-end
-
-local function copy(t)
- local new = {}
- for k, v in pairs(t) do
- new[k] = v
- end
- return new
-end
-
-local function insert(tbl, name, key, value)
- if not name or not key then
- return
- end
- if not tbl[name] then
- tbl[name] = {
- type = name,
- name = name,
- child = {},
- }
- end
- tbl[name].child[key] = copy(value)
-end
-
-local function mergeParent(alllibs, name, lib)
- for _, parent in ipairs(lib.parent) do
- if parent.type == 'global' then
- insert(alllibs.global, parent.name, name, lib)
- elseif parent.type == 'library' then
- insert(alllibs.library, parent.name, name, lib)
- elseif parent.type == 'object' then
- insert(alllibs.object, parent.name, name, lib)
- end
- end
-end
-
-local function mergeLibs(alllibs, libs)
- if not libs then
- return
- end
- for _, lib in pairs(libs) do
- if lib.parent then
- mergeParent(alllibs, lib.name, lib)
- else
- mergeSource(alllibs, lib.name, lib)
- end
- end
-end
-
-local function loadLocale(language, relative)
- local localePath = ROOT / 'locale' / language / relative
- local localeBuf = io.load(localePath)
- if localeBuf then
- local locale = table.container()
- xpcall(lni.classics, log.error, localeBuf, localePath:string(), {locale})
- return locale
- end
- return nil
-end
-
-local function fix(libs)
- for name, lib in pairs(libs) do
- lib.name = lib.name or name
- lib.child = {}
- end
-end
-
-local function init()
- local lang = require 'language'
- local id = lang.id
- local alllibs = {
- global = table.container(),
- library = table.container(),
- object = table.container(),
- }
- for path in io.scan(ROOT / 'libs') do
- local libs
- local buf = io.load(path)
- if buf then
- libs = table.container()
- xpcall(lni.classics, log.error, buf, path:string(), {libs})
- fix(libs)
- end
- local relative = fs.relative(path, ROOT)
-
- local locale = loadLocale('en-US', relative)
- mergeLocale(libs, locale)
- if id ~= 'en-US' then
- locale = loadLocale(id, relative)
- mergeLocale(libs, locale)
- end
- mergeLibs(alllibs, libs)
- end
-
- return alllibs
-end
-
-return init()
diff --git a/server/src/matcher/references.lua b/server/src/matcher/references.lua
deleted file mode 100644
index 71b7eb77..00000000
--- a/server/src/matcher/references.lua
+++ /dev/null
@@ -1,38 +0,0 @@
-local findResult = require 'matcher.find_result'
-
-local function parseResult(result, declarat)
- local positions = {}
- local tp = result.type
- if tp == 'local' then
- for _, info in ipairs(result) do
- if declarat or info.type == 'get' then
- positions[#positions+1] = {info.source.start, info.source.finish}
- end
- end
- elseif tp == 'field' then
- for _, info in ipairs(result) do
- if declarat or info.type == 'get' then
- positions[#positions+1] = {info.source.start, info.source.finish}
- end
- end
- elseif tp == 'label' then
- for _, info in ipairs(result) do
- if declarat or info.type == 'goto' then
- positions[#positions+1] = {info.source.start, info.source.finish}
- end
- end
- elseif tp == 'string' then
- else
- error('Unknow result type:' .. result.type)
- end
- return positions
-end
-
-return function (vm, pos, declarat)
- local result = findResult(vm, pos)
- if not result then
- return nil
- end
- local positions = parseResult(result, declarat)
- return positions
-end
diff --git a/server/src/matcher/rename.lua b/server/src/matcher/rename.lua
deleted file mode 100644
index 1033d331..00000000
--- a/server/src/matcher/rename.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local findResult = require 'matcher.find_result'
-local parser = require 'parser'
-
-local function parseResult(result, source, newName)
- local positions = {}
- local tp = result.type
- if tp == 'local' or tp == 'field' then
- local key = source[1]
- if result.disableRename then
- return positions
- end
- if source.index then
- if not parser.grammar(newName, 'Exp') then
- return positions
- end
- else
- if not parser.grammar(newName, 'Name') then
- return positions
- end
- end
- local mark = {}
- for _, info in ipairs(result) do
- if not mark[info.source] then
- mark[info.source] = info
- if info.source[1] == key then
- positions[#positions+1] = {info.source.start, info.source.finish}
- end
- end
- end
- elseif tp == 'label' then
- if not parser.grammar(newName, 'Name') then
- return positions
- end
- local label = result.label
- for _, info in ipairs(label) do
- positions[#positions+1] = {info.source.start, info.source.finish}
- end
- else
- error('Unknow result type:' .. result.type)
- end
- return positions
-end
-
-return function (vm, pos, newName)
- local result, source = findResult(vm, pos)
- if not result then
- return nil
- end
- local positions = parseResult(result, source, newName)
- return positions
-end
diff --git a/server/src/matcher/signature.lua b/server/src/matcher/signature.lua
deleted file mode 100644
index dc6af15e..00000000
--- a/server/src/matcher/signature.lua
+++ /dev/null
@@ -1,66 +0,0 @@
-local hover = require 'matcher.hover'
-
-local function isContainPos(obj, pos)
- if obj.start <= pos and obj.finish + 1 >= pos then
- return true
- end
- return false
-end
-
-local function findArgCount(args, pos)
- for i, arg in ipairs(args) do
- if isContainPos(arg, pos) then
- return i, arg
- end
- end
- return #args + 1, nil
-end
-
--- 找出范围包含pos的call
-local function findCall(vm, pos)
- local results = {}
- for _, call in ipairs(vm.results.calls) do
- if isContainPos(call.args, pos) then
- local n, arg = findArgCount(call.args, pos)
- if arg and arg.type == 'string' then
- return nil
- end
- local var = vm.results.sources[call.lastObj]
- if var then
- results[#results+1] = {
- func = call.func,
- var = var,
- source = call.lastObj,
- select = n,
- args = call.args,
- }
- end
- end
- end
- -- 可能处于 'func1(func2(' 的嵌套中,因此距离越远的函数层级越低
- table.sort(results, function (a, b)
- return a.args.start < b.args.start
- end)
- return results
-end
-
-return function (vm, pos)
- local calls = findCall(vm, pos)
- if not calls or #calls == 0 then
- return nil
- end
-
- local hovers = {}
- for _, call in ipairs(calls) do
- local hvr = hover(call.var, call.source, nil, call.select)
- if hvr and hvr.argLabel then
- hovers[#hovers+1] = hvr
- end
- end
-
- if #hovers == 0 then
- return nil
- end
-
- return hovers
-end
diff --git a/server/src/matcher/vm.lua b/server/src/matcher/vm.lua
deleted file mode 100644
index 6bd7e7c3..00000000
--- a/server/src/matcher/vm.lua
+++ /dev/null
@@ -1,1501 +0,0 @@
-local env = require 'matcher.env'
-local library = require 'matcher.library'
-
-local DefaultSource = { start = 0, finish = 0 }
-
--- 根据赋值顺序决定遍历顺序的表
-local function orderTable()
- local t = {}
- local list = {}
- local mark = {}
- return setmetatable(t, {
- __newindex = function (self, k, v)
- if not mark[k] then
- mark[k] = true
- list[#list+1] = k
- end
- rawset(self, k, v)
- end,
- __pairs = function (self)
- local i = 0
- return function ()
- while true do
- i = i + 1
- local k = list[i]
- if not k then
- return nil, nil
- end
- local v = t[k]
- if v ~= nil then
- return k, v
- end
- end
- end
- end,
- })
-end
-
-local function readOnly(t)
- local keys
- return setmetatable({}, {
- __index = function (self, k)
- if k == nil then
- return nil
- end
- local v = t[k]
- if type(v) == 'table' then
- v = readOnly(v)
- end
- self[k] = v
- return v
- end,
- __len = function (self)
- return #t
- end,
- __pairs = function (self)
- if not keys then
- keys = {}
- for k in pairs(t) do
- keys[#keys+1] = k
- end
- end
- local i = 0
- return function ()
- i = i + 1
- local k = keys[i]
- return k, self[k]
- end
- end,
- __source = t,
- })
-end
-
-local function insertOnce(tbl, key)
- if tbl[key] then
- return
- end
- tbl[key] = true
- tbl[#tbl+1] = key
-end
-
-local mt = {}
-mt.__index = mt
-
-function mt:createDummyVar(source, value)
- local loc = {
- type = 'local',
- key = '',
- source = source or DefaultSource,
- }
- self:setValue(loc, value, source)
- return loc
-end
-
-function mt:createLocal(key, source, value)
- local loc = {
- type = 'local',
- key = key,
- source = source or DefaultSource,
- close = self.scope.block.finish,
- }
-
- if source then
- source.isLocal = true
- end
-
- local shadow = self.scope.locals[key]
- if shadow then
- shadow.close = source and (source.start-1)
- local group
- if shadow.shadow then
- group = shadow.shadow
- else
- group = { shadow }
- shadow.shadow = group
- end
- group[#group+1] = loc
- loc.shadow = group
- end
-
- self.scope.locals[key] = loc
- self.results.locals[#self.results.locals+1] = loc
-
- self:addInfo(loc, 'local', source)
- self:setValue(loc, value, source)
- return loc
-end
-
-function mt:createArg(key, source, value)
- local loc = self:createLocal(key, source, value)
- if source then
- source.isArg = true
- end
- return loc
-end
-
-function mt:scopePush(block)
- if not block.start then
- error('Scope push without start!')
- end
- self.scope:push()
- self.scope.block = block
-end
-
-function mt:scopePop()
- self.scope:pop()
-end
-
-function mt:addInfo(obj, type, source)
- if source and not source.start then
- error('Miss start: ' .. table.dump(source))
- end
- obj[#obj+1] = {
- type = type,
- source = source or DefaultSource,
- }
- if source then
- source.uri = self.uri
- local other = self.results.sources[source]
- if other then
- if other.type == 'multi-source' then
- other[#other+1] = obj
- else
- other = {
- type = 'multi-source',
- [1] = other,
- [2] = obj,
- }
- end
- else
- self.results.sources[source] = obj
- end
- if type == 'set' or type == 'return' then
- if not obj.declarat then
- obj.declarat = source
- end
- end
- end
- return obj
-end
-
-function mt:createDots(index, source)
- local dots = {
- type = 'dots',
- source = source or DefaultSource,
- func = self:getCurrentFunction(),
- index = index,
- }
- self.chunk.dots = dots
- return dots
-end
-
-function mt:buildTable(source)
- local tbl = self:createValue('table', source)
- if not source then
- return tbl
- end
- local n = 0
- for index, obj in ipairs(source) do
- if obj.type == 'pair' then
- local value = self:getExp(obj[2])
- local key = obj[1]
- if key.index then
- local index = self:getIndex(key)
- local field = self:createField(tbl, index, key)
- if value.type == 'list' then
- self:setValue(field, value[1], key)
- else
- self:setValue(field, value, key)
- end
- else
- if key.type == 'name' then
- local index = key[1]
- insertOnce(self.results.indexs, index)
- local field = self:createField(tbl, index, key)
- key.isIndex = true
- if value.type == 'list' then
- self:setValue(field, value[1], key)
- else
- self:setValue(field, value, key)
- end
- end
- end
- else
- local value = self:getExp(obj)
- if value.type == 'list' then
- if index == #source then
- for i, v in ipairs(value) do
- local field = self:createField(tbl, n + i)
- self:setValue(field, v)
- end
- else
- n = n + 1
- local field = self:createField(tbl, n)
- self:setValue(field, value[1])
- end
- else
- n = n + 1
- local field = self:createField(tbl, n)
- self:setValue(field, value)
- end
- -- 处理写了一半的 key = value,把name类的数组元素视为哈希键
- if obj.type == 'name' then
- obj.isIndex = true
- end
- end
- end
- return tbl
-end
-
-function mt:mergeValue(a, b, mark)
- if a == b then
- return
- end
- if not mark then
- mark = {}
- end
- if mark[a] or mark[b] then
- return
- end
- if a.uri ~= self.uri then
- return
- end
- mark[a] = true
- mark[b] = true
- self:mergeChild(a, b, mark)
- for k in pairs(a) do
- a[k] = nil
- end
- for k, v in pairs(b) do
- a[k] = v
- end
-end
-
-function mt:mergeField(a, b, mark)
- if a == b then
- return
- end
- if not mark then
- mark = {}
- end
- for i, info in ipairs(a) do
- a[i] = nil
- b[#b+1] = info
- end
- for i, v in ipairs(b) do
- a[i] = v
- end
- self:mergeValue(a.value, b.value, mark)
-end
-
-function mt:mergeChild(a, b, mark)
- if a == b then
- return
- end
- if not a.child and not b.child then
- return
- end
- if not mark then
- mark = {}
- end
- if a.uri ~= self.uri then
- return
- end
- if b.uri == self.uri then
- local child = a.child or orderTable()
- local other = b.child or orderTable()
- a.child = nil
- b.child = nil
- for k, v in pairs(other) do
- if child[k] then
- self:mergeField(child[k], v, mark)
- else
- child[k] = v
- end
- end
- a.child = child
- b.child = child
- else
- local child = a.child or orderTable()
- local other = b.child
- if not other then
- return
- end
- a.child = nil
- for k, v in pairs(other) do
- child[k] = v
- end
- a.child = child
- end
-end
-
-function mt:setValue(var, value, source)
- if value and value.type == 'list' then
- error('Cant set value list')
- end
- value = value or self:createValue('any', source)
- if var.value then
- if value.type == 'any' then
- self:mergeChild(var.value, value)
- else
- self:mergeValue(var.value, value)
- end
- value = var.value
- else
- var.value = value
- end
- if source and source.start then
- self:addInfo(var, 'set', source)
- self:addInfo(value, 'set', source)
- end
- return value
-end
-
-function mt:getValue(var)
- if not var.value then
- var.value = self:createValue('any')
- end
- return var.value
-end
-
-function mt:createField(pValue, name, source)
- if pValue.type == 'local' or pValue.type == 'field' then
- error('Only value can create field')
- end
- local field = {
- type = 'field',
- key = name,
- source = source or DefaultSource,
- }
-
- if not pValue.child then
- pValue.child = orderTable()
- end
- pValue.child[name] = field
- self:inference(pValue, 'table')
-
- return field
-end
-
-function mt:getField(pValue, name, source)
- local field = (pValue.child and pValue.child[name])
- or self:createField(pValue, name, source)
-
- return field
-end
-
-function mt:buildFunction(exp, object)
- local func = self:createValue('function', exp)
- func.args = {}
- func.argValues = {}
-
- if not exp then
- return func
- end
-
- func.built = true
-
- self:scopePush(exp)
- self.chunk:push()
- self.chunk:cut 'dots'
- self.chunk:cut 'labels'
- self.chunk.func = func
-
- if object then
- local var = self:createArg('self', object.source, self:getValue(object))
- var.disableRename = true
- func.args[1] = var
- end
-
- local stop
- self:forList(exp.arg, function (arg)
- if stop then
- return
- end
- if arg.type == 'name' then
- local var = self:createArg(arg[1], arg)
- arg.isArg = true
- func.args[#func.args+1] = var
- func.argValues[#func.args] = self:getValue(var)
- elseif arg.type == '...' then
- self:createDots(#func.args+1, arg)
- func.hasDots = true
- for _ = 1, 10 do
- func.argValues[#func.argValues+1] = self:createValue('any', arg)
- end
- stop = true
- end
- end)
-
- self:doActions(exp)
-
- self.results.funcs[#self.results.funcs+1] = func
-
- self.chunk:pop()
- self:scopePop()
-
- return func
-end
-
-function mt:forList(list, callback)
- if not list then
- return
- end
- if list.type == 'list' then
- for i = 1, #list do
- callback(list[i])
- end
- else
- callback(list)
- end
-end
-
-function mt:countList(list)
- if not list then
- return 0
- end
- if list.type == 'list' then
- return #list
- end
- return 1
-end
-
-function mt:updateFunctionArgs(func)
- if not func.argValues then
- return
- end
- if not func.args then
- return
- end
-
- local values = func.argValues
- for i, var in ipairs(func.args) do
- if var.type == 'dots' then
- local list = {
- type = 'list',
- }
- for n = i, #values do
- list[n-i+1] = values[n]
- end
- self:setValue(var, list)
- break
- else
- self:setValue(var, values[i])
- end
- end
-end
-
-function mt:setFunctionArg(func, values)
- if func.uri ~= self.uri then
- return
- end
- if not func.argValues then
- func.argValues = {}
- end
- for i = 1, #values do
- if not func.argValues[i] then
- func.argValues[i] = values[i]
- end
- self:inference(values[i], func.argValues[i].type)
- self:inference(func.argValues[i], values[i].type)
- end
-
- self:updateFunctionArgs(func)
-end
-
-function mt:getFunctionArg(func, i)
- if not func.argValues then
- func.argValues = {}
- end
- if not func.argValues[i] then
- for n = #func.argValues+1, i do
- func.argValues[n] = self:createValue('any')
- end
- end
- return func.argValues[i]
-end
-
-function mt:checkMetaIndex(value, meta)
- local index = self:getField(meta, '__index')
- if not index then
- return
- end
- local indexValue = self:getValue(index)
- -- TODO 支持function
- self:mergeChild(value, indexValue)
-end
-
-function mt:callSetMetaTable(func, values)
- if not values[1] then
- values[1] = self:createValue('any')
- end
- if not values[2] then
- values[2] = self:createValue('any')
- end
- self:setFunctionReturn(func, 1, values[1])
-
- values[1].metatable = values[2]
- -- 检查 __index
- self:checkMetaIndex(values[1], values[2])
-end
-
-function mt:getRequire(strValue, destVM)
- -- 取出对方的主函数
- local main = destVM.results.main
- -- 获取主函数返回值,注意不能修改对方的环境
- local mainValue
- if main.returns then
- mainValue = readOnly(main.returns[1])
- else
- mainValue = self:createValue('boolean', nil, true)
- mainValue.uri = destVM.uri
- end
-
- return mainValue
-end
-
-function mt:getLoadFile(strValue, destVM)
- -- 取出对方的主函数
- local main = destVM.results.main
- -- loadfile 的返回值就是对方的主函数
- local mainValue = readOnly(main)
-
- return mainValue
-end
-
-function mt:tryRequireOne(strValue, mode)
- if not self.lsp or not self.lsp.workspace then
- return nil
- end
- local str = strValue.value
- if type(str) == 'string' then
- -- 支持 require 'xxx' 的转到定义
- local strSource = strValue.source
- self.results.sources[strSource] = strValue
- strValue.isRequire = true
-
- local uri
- if mode == 'require' then
- uri = self.lsp.workspace:searchPath(self.uri, str)
- elseif mode == 'loadfile' then
- uri = self.lsp.workspace:loadPath(self.uri, str)
- elseif mode == 'dofile' then
- uri = self.lsp.workspace:loadPath(self.uri, str)
- end
- if not uri then
- return nil
- end
-
- strValue.uri = uri
- -- 如果取不到VM(不编译),则做个标记,之后再取一次
- local destVM = self.lsp:getVM(uri)
- self.lsp:compileChain(self.uri, uri)
- if destVM then
- if mode == 'require' then
- return self:getRequire(strValue, destVM)
- elseif mode == 'loadfile' then
- return self:getLoadFile(strValue, destVM)
- elseif mode == 'dofile' then
- return self:getRequire(strValue, destVM)
- end
- end
- end
- return nil
-end
-
-function mt:callRequire(func, values)
- if not values[1] then
- values[1] = self:createValue('any')
- end
- local str = values[1].value
- if type(str) ~= 'string' then
- return
- end
- local lib = library.library[str]
- if lib then
- local value = self:getLibValue(lib, 'library')
- self:setFunctionReturn(func, 1, value)
- return
- else
- local requireValue = self:tryRequireOne(values[1], 'require')
- if not requireValue then
- requireValue = self:createValue('boolean')
- requireValue.isRequire = true
- end
- self:setFunctionReturn(func, 1, requireValue)
- end
-end
-
-function mt:callLoadFile(func, values)
- if not values[1] then
- values[1] = self:createValue('any')
- end
- local str = values[1].value
- if type(str) ~= 'string' then
- return
- end
- local requireValue = self:tryRequireOne(values[1], 'loadfile')
- if not requireValue then
- requireValue = self:createValue('any')
- requireValue.isRequire = true
- end
- self:setFunctionReturn(func, 1, requireValue)
-end
-
-function mt:callDoFile(func, values)
- if not values[1] then
- values[1] = self:createValue('any')
- end
- local str = values[1].value
- if type(str) ~= 'string' then
- return
- end
- local requireValue = self:tryRequireOne(values[1], 'dofile')
- if not requireValue then
- requireValue = self:createValue('any')
- requireValue.isRequire = true
- end
- self:setFunctionReturn(func, 1, requireValue)
-end
-
-function mt:call(func, values)
- self:inference(func, 'function')
- local lib = func.lib
- if lib then
- if lib.args then
- for i, arg in ipairs(lib.args) do
- if arg.type == '...' then
- self:inference(self:getFunctionArg(func, i), 'any')
- else
- self:inference(self:getFunctionArg(func, i), arg.type or 'any')
- end
- end
- end
- if lib.returns then
- for i, rtn in ipairs(lib.returns) do
- if rtn.type == '...' then
- self:inference(self:getFunctionReturns(func, i), 'any')
- else
- self:inference(self:getFunctionReturns(func, i), rtn.type or 'any')
- end
- end
- end
- if lib.special then
- if lib.special == 'setmetatable' then
- self:callSetMetaTable(func, values)
- elseif lib.special == 'require' then
- self:callRequire(func, values)
- elseif lib.special == 'loadfile' then
- self:callLoadFile(func, values)
- elseif lib.special == 'dofile' then
- self:callDoFile(func, values)
- end
- end
- end
-
- self:setFunctionArg(func, values)
-
- return self:getFunctionReturns(func)
-end
-
-function mt:getCurrentFunction()
- return self.chunk.func
-end
-
-function mt:mergeFunctionReturn(func, index, value)
- if not func.returns[index] then
- func.returns[index] = value
- return
- end
- if value.type == 'nil' then
- return
- end
- if value == 'any' and func.returns[index] ~= 'nil' then
- return
- end
- func.returns[index] = value
-end
-
-function mt:setFunctionReturn(func, index, value)
- func.hasReturn = true
- if not func.returns then
- func.returns = {
- type = 'list',
- }
- end
- if value then
- if value.type == 'list' then
- for i, v in ipairs(value) do
- self:mergeFunctionReturn(func, index+i-1, v)
- end
- else
- self:mergeFunctionReturn(func, index, value)
- end
- else
- self:mergeFunctionReturn(func, index, self:createValue('any'))
- end
-end
-
-function mt:getFunctionReturns(func, i)
- if func.maxReturns and i and func.maxReturns < i then
- return self:createValue('nil')
- end
- if not func.returns then
- func.returns = {
- type = 'list',
- }
- end
- if i then
- if not func.returns[i] then
- for n = #func.returns+1, i do
- func.returns[n] = self:createValue('any')
- end
- end
- return func.returns[i]
- else
- return func.returns
- end
-end
-
-function mt:inference(value, type)
- if type == '...' then
- error('Value type cant be ...')
- end
- if value.type == 'any' and type ~= 'nil' then
- value.type = type
- end
-end
-
-function mt:createValue(tp, source, v)
- if tp == '...' then
- error('Value type cant be ...')
- end
- -- TODO lib里的多类型
- if type(tp) == 'table' then
- tp = tp[1]
- end
- local value = {
- type = tp,
- source = source or DefaultSource,
- value = v,
- uri = self.uri,
- }
- local lib = library.object[tp]
- if lib then
- self:getLibChild(value, lib, 'object')
- end
- return value
-end
-
-function mt:getLibChild(value, lib, parentType)
- if lib.child then
- if self.libraryChild[lib] then
- value.child = self.libraryChild[lib]
- return
- end
- self.libraryChild[lib] = {}
- for fName, fLib in pairs(lib.child) do
- local fField = self:createField(value, fName)
- local fValue = self:getLibValue(fLib, parentType)
- self:setValue(fField, fValue)
- end
- if value.child then
- for k, v in pairs(value.child) do
- self.libraryChild[lib][k] = v
- end
- end
- value.child = self.libraryChild[lib]
- end
-end
-
-function mt:getLibValue(lib, parentType, v)
- if self.libraryValue[lib] then
- return self.libraryValue[lib]
- end
- local tp = lib.type
- local value
- if tp == 'table' then
- value = self:createValue('table')
- elseif tp == 'function' then
- value = self:createValue('function')
- if lib.returns then
- local dots
- for i, rtn in ipairs(lib.returns) do
- self:setFunctionReturn(value, i, self:getLibValue(rtn, parentType))
- if rtn.type == '...' then
- dots = true
- end
- end
- if not dots then
- value.maxReturns = #lib.returns
- end
- else
- value.maxReturns = 0
- end
- if lib.args then
- local values = {}
- for i, arg in ipairs(lib.args) do
- values[i] = self:getLibValue(arg, parentType) or self:createValue('any')
- end
- self:setFunctionArg(value, values)
- end
- elseif tp == 'string' then
- value = self:createValue('string', nil, v or lib.value)
- elseif tp == 'boolean' then
- value = self:createValue('boolean', nil, v or lib.value)
- elseif tp == 'number' then
- value = self:createValue('number', nil, v or lib.value)
- elseif tp == 'integer' then
- value = self:createValue('integer', nil, v or lib.value)
- elseif tp == 'nil' then
- value = self:createValue('nil')
- elseif tp == '...' then
- value = self:createValue('any')
- else
- value = self:createValue(tp or 'any')
- end
- self.libraryValue[lib] = value
- value.lib = lib
- value.parentType = parentType
-
- self:getLibChild(value, lib, parentType)
-
- return value
-end
-
-function mt:getName(name, source)
- local loc = self.scope.locals[name]
- if loc then
- return loc
- end
- local ENV = self.scope.locals._ENV
- local global = self:getField(self:getValue(ENV), name, source)
- global.parent = ENV
- return global
-end
-
-function mt:getIndex(obj)
- local tp = obj.type
- if tp == 'name' then
- local var = self:getName(obj[1])
- local value = self:getValue(var)
- self:addInfo(var, 'get', obj)
- return value
- elseif (tp == 'string' or tp == 'number' or tp == 'boolean') then
- return obj[1]
- else
- return self:getExp(obj)
- end
-end
-
--- expect表示遇到 ... 时,期待的返回数量
-function mt:unpackDots(res, expect)
- local dots = self:getDots(1)
- local func = dots.func
- local start = dots.index
- if expect then
- local finish = start + expect - 1
- for i = start, finish do
- res[#res+1] = self:getFunctionArg(func, i)
- end
- else
- if not func.argValues then
- return
- end
- for i = start, #func.argValues do
- res[#res+1] = func.argValues[i]
- end
- end
-end
-
-function mt:unpackList(list, expect)
- local res = {
- type = 'list',
- }
- if not list then
- return res
- end
- if list.type == 'list' or list.type == 'call' then
- for i, exp in ipairs(list) do
- if exp.type == '...' then
- self:unpackDots(res, expect)
- break
- end
- local value = self:getExp(exp)
- if value.type == 'list' then
- if i == #list then
- for _, v in ipairs(value) do
- res[#res+1] = v
- end
- else
- res[#res+1] = value[1]
- end
- else
- res[#res+1] = value
- end
- end
- elseif list.type == '...' then
- self:unpackDots(res, expect)
- else
- local value = self:getExp(list)
- if value.type == 'list' then
- for i, v in ipairs(value) do
- res[i] = v
- end
- else
- res[1] = value
- end
- end
- for _, v in ipairs(res) do
- if v.type == 'list' then
- error('Unpack list')
- end
- end
- return res
-end
-
-function mt:getSimple(simple, mode)
- local value = self:getExp(simple[1])
- local field
- local parentName
- local tp = simple[1].type
- if tp == 'name' then
- field = self:getName(simple[1][1])
- parentName = field.key
- elseif tp == 'string' or tp == 'number' or tp == 'nil' or tp == 'boolean' then
- local v = self:createValue(tp, simple[1], simple[1][1])
- field = self:createDummyVar(simple[1], v)
- parentName = '*' .. tp
- else
- local v = self:createValue('any', simple[1])
- field = self:createDummyVar(simple[1], v)
- parentName = '?'
- end
- local object
- local lastField = field
- for i = 2, #simple do
- local obj = simple[i]
- local tp = obj.type
-
- if tp == 'call' then
- local args = self:unpackList(obj)
- if object then
- table.insert(args, 1, self:getValue(object))
- end
- local func = value
- -- 函数的返回值一定是list
- value = self:call(func, args)
- if i < #simple then
- value = value[1] or self:createValue('any')
- end
- self.results.calls[#self.results.calls+1] = {
- args = obj,
- lastObj = simple[i-1],
- nextObj = simple[i+1],
- func = func,
- }
- parentName = parentName .. '(...)'
- elseif tp == 'index' then
- local child = obj[1]
- local index = self:getIndex(child)
- field = self:getField(value, index, child)
- field.parentValue = value
- value = self:getValue(field)
- if mode == 'value' or i < #simple then
- self:addInfo(field, 'get', obj)
- end
- field.parent = lastField
- lastField = field
- obj.object = object
- obj.parentName = parentName
- if obj[1].type == 'string' then
- parentName = ('%s[%q]'):format(parentName, index)
- elseif obj[1].type == 'number' or obj[1].type == 'boolean' then
- parentName = ('%s[%s]'):format(parentName, index)
- else
- parentName = ('%s[?]'):format(parentName)
- end
- else
- if tp == 'name' then
- field = self:getField(value, obj[1], obj)
- field.parentValue = value
- value = self:getValue(field)
- if mode == 'value' or i < #simple then
- self:addInfo(field, 'get', obj)
- end
- field.parent = lastField
- lastField = field
- obj.object = object
- obj.parentName = parentName
- parentName = parentName .. '.' .. field.key
- elseif tp == ':' then
- object = field
- end
- end
- end
- if mode == 'value' then
- return value, object
- elseif mode == 'field' then
- return field, object
- end
- error('Unknow simple mode: ' .. mode)
-end
-
-function mt:isTrue(v)
- if v.type == 'nil' then
- return false
- end
- if v.type == 'boolean' and not v.value then
- return false
- end
- return true
-end
-
-function mt:getBinary(exp)
- local v1 = self:getExp(exp[1])
- local v2 = self:getExp(exp[2])
- local op = exp.op
- -- TODO 搜索元方法
- if op == 'or' then
- if self:isTrue(v1) then
- return v1
- else
- return v2
- end
- elseif op == 'and' then
- if self:isTrue(v1) then
- return v2
- else
- return v1
- end
- elseif op == '<='
- or op == '>='
- or op == '<'
- or op == '>'
- then
- self:inference(v1, 'number')
- self:inference(v2, 'number')
- return self:createValue('boolean')
- elseif op == '~='
- or op == '=='
- then
- return self:createValue('boolean')
- elseif op == '|'
- or op == '~'
- or op == '&'
- or op == '<<'
- or op == '>>'
- then
- self:inference(v1, 'integer')
- self:inference(v2, 'integer')
- if math.type(v1.value) == 'integer' and math.type(v2.value) == 'integer' then
- if op == '|' then
- return self:createValue('integer', v1.value | v2.value)
- elseif op == '~' then
- return self:createValue('integer', v1.value ~ v2.value)
- elseif op == '&' then
- return self:createValue('integer', v1.value &v2.value)
- elseif op == '<<' then
- return self:createValue('integer', v1.value << v2.value)
- elseif op == '>>' then
- return self:createValue('integer', v1.value >> v2.value)
- end
- end
- return self:createValue('integer')
- elseif op == '..' then
- self:inference(v1, 'string')
- self:inference(v2, 'string')
- if type(v1.value) == 'string' and type(v2.value) == 'string' then
- return self:createValue('string', nil, v1.value .. v2.value)
- end
- return self:createValue('string')
- elseif op == '+'
- or op == '-'
- or op == '*'
- or op == '/'
- or op == '^'
- or op == '%'
- or op == '//'
- then
- self:inference(v1, 'number')
- self:inference(v2, 'number')
- if type(v1.value) == 'number' and type(v2.value) == 'number' then
- if op == '+' then
- return self:createValue('number', nil, v1.value + v2.value)
- elseif op == '-' then
- return self:createValue('number', nil, v1.value - v2.value)
- elseif op == '*' then
- return self:createValue('number', nil, v1.value * v2.value)
- elseif op == '/' then
- return self:createValue('number', nil, v1.value / v2.value)
- elseif op == '^' then
- return self:createValue('number', nil, v1.value ^ v2.value)
- elseif op == '%' then
- return self:createValue('number', nil, v1.value % v2.value)
- elseif op == '//' then
- return self:createValue('number', nil, v1.value // v2.value)
- end
- end
- return self:createValue('number')
- end
- return nil
-end
-
-function mt:getUnary(exp)
- local v1 = self:getExp(exp[1])
- local op = exp.op
- -- TODO 搜索元方法
- if op == 'not' then
- return self:createValue('boolean')
- elseif op == '#' then
- self:inference(v1, 'table')
- if type(v1.value) == 'string' then
- return self:createValue('integer', nil, #v1.value)
- end
- return self:createValue('integer')
- elseif op == '-' then
- self:inference(v1, 'number')
- if type(v1.value) == 'number' then
- return self:createValue('number', nil, -v1.value)
- end
- return self:createValue('number')
- elseif op == '~' then
- self:inference(v1, 'integer')
- if math.type(v1.value) == 'integer' then
- return self:createValue('integer', nil, ~v1.value)
- end
- return self:createValue('integer')
- end
- return nil
-end
-
-function mt:getDots()
- if not self.chunk.dots then
- self:createDots(1)
- end
- return self.chunk.dots
-end
-
-function mt:getExp(exp)
- local tp = exp.type
- if tp == 'nil' then
- return self:createValue('nil', exp)
- elseif tp == 'string' then
- self.results.strings[#self.results.strings+1] = exp
- return self:createValue('string', exp, exp[1])
- elseif tp == 'boolean' then
- return self:createValue('boolean', exp, exp[1])
- elseif tp == 'number' then
- return self:createValue('number', exp, exp[1])
- elseif tp == 'name' then
- local var = self:getName(exp[1], exp)
- local value = self:getValue(var)
- self:addInfo(var, 'get', exp)
- return value
- elseif tp == 'simple' then
- return self:getSimple(exp, 'value')
- elseif tp == 'binary' then
- return self:getBinary(exp)
- elseif tp == 'unary' then
- return self:getUnary(exp)
- elseif tp == 'function' then
- return self:buildFunction(exp)
- elseif tp == 'table' then
- return self:buildTable(exp)
- elseif tp == '...' then
- local value = { type = 'list' }
- self:unpackDots(value)
- return value
- end
- error('Unkown exp type: ' .. tostring(tp))
-end
-
-function mt:doDo(action)
- self:scopePush(action)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doReturn(action)
- self:getCurrentFunction().hasReturn = true
- for i, exp in ipairs(action) do
- local value = self:getExp(exp)
- if value.type == 'list' then
- if i == #action then
- if #value == 0 then
- value[1] = self:createValue('any', exp)
- end
- for x, v in ipairs(value) do
- self:addInfo(v, 'return', exp)
- self:setFunctionReturn(self:getCurrentFunction(), i + x - 1, v)
- end
- break
- else
- local v = value[1] or self:createValue('nil', exp)
- self:addInfo(v, 'return', exp)
- self:setFunctionReturn(self:getCurrentFunction(), i, v)
- end
- else
- self:addInfo(value, 'return', exp)
- self:setFunctionReturn(self:getCurrentFunction(), i, value)
- end
- end
-end
-
-function mt:createLabel(action)
- local name = action[1]
- if not self.chunk.labels[name] then
- local label = {
- type = 'label',
- key = name,
- }
- self.chunk.labels[name] = label
- self.results.labels[#self.results.labels+1] = label
- end
- return self.chunk.labels[name]
-end
-
-function mt:doSet(action)
- if not action[2] then
- return
- end
- local n = self:countList(action[1])
- -- 要先计算值
- local values = self:unpackList(action[2], n)
- self:forList(action[1], function (key)
- local value = table.remove(values, 1)
- if key.type == 'name' then
- local var = self:getName(key[1], key)
- self:setValue(var, value, key)
- elseif key.type == 'simple' then
- local field = self:getSimple(key, 'field')
- self:setValue(field, value, key[#key])
- end
- end)
-end
-
-function mt:doLocal(action)
- local n = self:countList(action[1])
- local values
- if action[2] then
- values = self:unpackList(action[2], n)
- end
- self:forList(action[1], function (key)
- local value
- if values then
- value = table.remove(values, 1)
- end
- self:createLocal(key[1], key, value)
- end)
-end
-
-function mt:doIf(action)
- for _, block in ipairs(action) do
- if block.filter then
- self:getExp(block.filter)
- end
-
- self:scopePush(block)
- self:doActions(block)
- self:scopePop()
- end
-end
-
-function mt:doLoop(action)
-
- local min = self:getExp(action.min)
- self:getExp(action.max)
- if action.step then
- self:getExp(action.step)
- end
-
- self:scopePush(action)
- self:createLocal(action.arg[1], action.arg, min)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doIn(action)
- local args = self:unpackList(action.exp)
-
- self:scopePush(action)
- local func = table.remove(args, 1) or self:createValue('any')
- local values = self:call(func, args)
- self:forList(action.arg, function (arg)
- local value = table.remove(values, 1)
- self:createLocal(arg[1], arg, value)
- end)
-
- self:doActions(action)
-
- self:scopePop()
-end
-
-function mt:doWhile(action)
-
- self:getExp(action.filter)
-
- self:scopePush(action)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doRepeat(action)
- self:scopePush(action)
- self:doActions(action)
- self:getExp(action.filter)
- self:scopePop()
-end
-
-function mt:doFunction(action)
- local name = action.name
- local var, object
- local source
- if name then
- if name.type == 'simple' then
- var, object = self:getSimple(name, 'field')
- source = name[#name]
- else
- var = self:getName(name[1], name)
- source = name
- end
- end
- local func = self:buildFunction(action, object)
- if var then
- self:setValue(var, func, source)
- end
-end
-
-function mt:doLocalFunction(action)
- local name = action.name
- local var, object
- local source
- if name then
- if name.type == 'simple' then
- var, object = self:getSimple(name, 'field')
- source = name[#name]
- else
- var = self:createLocal(name[1], name)
- source = name
- end
- end
- local func = self:buildFunction(action, object)
- if var then
- self:setValue(var, func, source)
- end
-end
-
-function mt:doAction(action)
- local tp = action.type
- if tp == 'do' then
- self:doDo(action)
- elseif tp == 'break' then
- elseif tp == 'return' then
- self:doReturn(action)
- elseif tp == 'label' then
- local label = self:createLabel(action)
- self:addInfo(label, 'set', action)
- elseif tp == 'goto' then
- local label = self:createLabel(action)
- self:addInfo(label, 'goto', action)
- elseif tp == 'set' then
- self:doSet(action)
- elseif tp == 'local' then
- self:doLocal(action)
- elseif tp == 'simple' then
- -- call
- self:getSimple(action, 'value')
- elseif tp == 'if' then
- self:doIf(action)
- elseif tp == 'loop' then
- self:doLoop(action)
- elseif tp == 'in' then
- self:doIn(action)
- elseif tp == 'while' then
- self:doWhile(action)
- elseif tp == 'repeat' then
- self:doRepeat(action)
- elseif tp == 'function' then
- self:doFunction(action)
- elseif tp == 'localfunction' then
- self:doLocalFunction(action)
- else
- self:getExp(action)
- end
-end
-
-function mt:doActions(actions)
- for _, action in ipairs(actions) do
- self:doAction(action)
- end
-end
-
-function mt:createEnvironment()
- self.scope.block = { start = 0, finish = math.maxinteger }
- -- 整个文件是一个函数
- self.chunk.func = self:buildFunction()
- self.results.main = self.chunk.func
- -- 隐藏的上值`_ENV`
- local parent = self:createLocal('_ENV')
- local envValue = self:setValue(parent, self:buildTable())
- -- _ENV 有个特殊标记
- envValue.ENV = true
- -- 隐藏的参数`...`
- self:createDots(1)
-
- -- 设置全局变量
- for name, lib in pairs(library.global) do
- local field = self:createField(envValue, name)
- local value = self:getLibValue(lib, 'global')
- value = self:setValue(field, value)
- end
-
- -- 设置 _G 使用 _ENV 的child
- local g = self:getField(envValue, '_G')
- local gValue = self:getValue(g)
- gValue.child = envValue.child
-end
-
-local function compile(ast, lsp, uri)
- local vm = setmetatable({
- scope = env {
- locals = {},
- },
- chunk = env {
- labels = {},
- },
- results = {
- locals = {},
- labels = {},
- funcs = {},
- calls = {},
- sources= {},
- strings= {},
- indexs = {},
- main = nil,
- },
- libraryValue = {},
- libraryChild = {},
- lsp = lsp,
- uri = uri,
- }, mt)
-
- -- 创建初始环境
- vm:createEnvironment()
-
- -- 执行代码
- vm:doActions(ast)
-
- return vm
-end
-
-return function (ast, lsp, uri)
- if not ast then
- return nil
- end
- local suc, res = xpcall(compile, log.error, ast, lsp, uri)
- if not suc then
- return nil
- end
- return res
-end