summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/src/emmy/generic.lua27
-rw-r--r--server/src/emmy/manager.lua36
-rw-r--r--server/src/emmy/param.lua8
-rw-r--r--server/src/emmy/return.lua8
-rw-r--r--server/src/utility.lua50
-rw-r--r--server/src/vm/emmy.lua53
-rw-r--r--server/src/vm/function.lua24
-rw-r--r--server/test/hover/init.lua13
8 files changed, 197 insertions, 22 deletions
diff --git a/server/src/emmy/generic.lua b/server/src/emmy/generic.lua
new file mode 100644
index 00000000..d47ee585
--- /dev/null
+++ b/server/src/emmy/generic.lua
@@ -0,0 +1,27 @@
+local listMgr = require 'vm.list'
+
+---@class EmmyGeneric
+local mt = {}
+mt.__index = mt
+mt.type = 'emmy.generic'
+
+function mt:getName()
+ return self.name
+end
+
+function mt:setValue(value)
+ self._value = value
+end
+
+function mt:getValue()
+ return self._value
+end
+
+return function (manager, defs)
+ for _, def in ipairs(defs) do
+ setmetatable(def, mt)
+ def._manager = manager
+ def._binds = {}
+ end
+ return defs
+end
diff --git a/server/src/emmy/manager.lua b/server/src/emmy/manager.lua
index 7875e322..5213e2f6 100644
--- a/server/src/emmy/manager.lua
+++ b/server/src/emmy/manager.lua
@@ -1,11 +1,12 @@
-local listMgr = require 'vm.list'
-local newClass = require 'emmy.class'
-local newType = require 'emmy.type'
+local listMgr = require 'vm.list'
+local newClass = require 'emmy.class'
+local newType = require 'emmy.type'
local newTypeUnit = require 'emmy.typeUnit'
-local newAlias = require 'emmy.alias'
-local newParam = require 'emmy.param'
-local newReturn = require 'emmy.return'
-local newField = require 'emmy.field'
+local newAlias = require 'emmy.alias'
+local newParam = require 'emmy.param'
+local newReturn = require 'emmy.return'
+local newField = require 'emmy.field'
+local newGeneric = require 'emmy.generic'
local mt = {}
mt.__index = mt
@@ -107,15 +108,23 @@ function mt:addAlias(source, typeObj)
return aliasObj
end
-function mt:addParam(source, typeObj)
+function mt:addParam(source, bind)
local paramObj = newParam(self, source)
- paramObj:bindType(typeObj)
+ if bind.type == 'emmy.type' then
+ paramObj:bindType(bind)
+ elseif bind.type == 'emmy.generic' then
+ paramObj:bindGeneric(bind)
+ end
return paramObj
end
-function mt:addReturn(source, typeObj)
+function mt:addReturn(source, bind)
local returnObj = newReturn(self, source)
- returnObj:bindType(typeObj)
+ if bind.type == 'emmy.type' then
+ returnObj:bindType(bind)
+ elseif bind.type == 'emmy.generic' then
+ returnObj:bindGeneric(bind)
+ end
return returnObj
end
@@ -126,6 +135,11 @@ function mt:addField(source, typeObj, value)
return fieldObj
end
+function mt:addGeneric(defs)
+ local genericObj = newGeneric(self, defs)
+ return genericObj
+end
+
function mt:remove()
end
diff --git a/server/src/emmy/param.lua b/server/src/emmy/param.lua
index 9c18bfec..84474db6 100644
--- a/server/src/emmy/param.lua
+++ b/server/src/emmy/param.lua
@@ -21,6 +21,14 @@ function mt:bindType(type)
end
end
+function mt:bindGeneric(generic)
+ if generic then
+ self._bindGeneric = generic
+ else
+ return self._bindGeneric
+ end
+end
+
return function (manager, source)
local self = setmetatable({
name = source[1][1],
diff --git a/server/src/emmy/return.lua b/server/src/emmy/return.lua
index 49f6e260..a347267c 100644
--- a/server/src/emmy/return.lua
+++ b/server/src/emmy/return.lua
@@ -17,6 +17,14 @@ function mt:bindType(type)
end
end
+function mt:bindGeneric(generic)
+ if generic then
+ self._bindGeneric = generic
+ else
+ return self._bindGeneric
+ end
+end
+
return function (manager, source)
local self = setmetatable({
source = source.id,
diff --git a/server/src/utility.lua b/server/src/utility.lua
index 4e390597..a56a78ad 100644
--- a/server/src/utility.lua
+++ b/server/src/utility.lua
@@ -19,29 +19,64 @@ local TAB = setmetatable({}, { __index = function (self, n)
end})
local KEY = {}
+local RESERVED = {
+ ['and'] = true,
+ ['break'] = true,
+ ['do'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['end'] = true,
+ ['false'] = true,
+ ['for'] = true,
+ ['function'] = true,
+ ['goto'] = true,
+ ['if'] = true,
+ ['in'] = true,
+ ['local'] = true,
+ ['nil'] = true,
+ ['not'] = true,
+ ['or'] = true,
+ ['repeat'] = true,
+ ['return'] = true,
+ ['then'] = true,
+ ['true'] = true,
+ ['until'] = true,
+ ['while'] = true,
+}
function table.dump(tbl)
if type(tbl) ~= 'table' then
return ('%q'):format(tbl)
end
local lines = {}
+ local mark = {}
lines[#lines+1] = '{'
local function unpack(tbl, tab)
- if tab > 10 then
- return '<Deep Table>'
+ if tab > 10 and mark[tbl] then
+ lines[#lines+1] = TAB[tab+1] .. '"<Loop>"'
+ return
end
+ mark[tbl] = true
local keys = {}
+ local integerFormat = '[%d]'
+ if #tbl >= 10 then
+ local width = math.log(#tbl, 10)
+ integerFormat = ('[%%0%dd]'):format(math.ceil(width))
+ end
for key in pairs(tbl) do
if type(key) == 'string' then
- if key == '' or key:find('[^%w_]') then
+ if not key:match('^[%a_][%w_]*$')
+ or #key >= 32
+ or RESERVED[key]
+ then
KEY[key] = ('[%q]'):format(key)
else
KEY[key] = key
end
elseif mathType(key) == 'integer' then
- KEY[key] = ('[%03d]'):format(key)
+ KEY[key] = integerFormat:format(key)
else
- KEY[key] = ('<%s>'):format(key)
+ KEY[key] = ('["<%s>"]'):format(key)
end
keys[#keys+1] = key
end
@@ -60,14 +95,15 @@ function table.dump(tbl)
lines[#lines+1] = ('%s},'):format(TAB[tab+1])
elseif tp == 'string' or tp == 'number' or tp == 'boolean' then
lines[#lines+1] = ('%s%s = %q,'):format(TAB[tab+1], KEY[key], value)
+ elseif tp == 'nil' then
else
- lines[#lines+1] = ('%s%s = <%s>,'):format(TAB[tab+1], KEY[key], value)
+ lines[#lines+1] = ('%s%s = %s,'):format(TAB[tab+1], KEY[key], tostring(value))
end
end
end
unpack(tbl, 0)
lines[#lines+1] = '}'
- return table.concat(lines, '\n')
+ return table.concat(lines, '\r\n')
end
local function sort_table(tbl)
diff --git a/server/src/vm/emmy.lua b/server/src/vm/emmy.lua
index 6c93a116..500c4711 100644
--- a/server/src/vm/emmy.lua
+++ b/server/src/vm/emmy.lua
@@ -4,6 +4,7 @@ function mt:clearEmmy()
self._emmy = nil
self._emmyParams = nil
self._emmyReturns = nil
+ self._emmyGeneric = nil
end
function mt:doEmmy(action)
@@ -21,6 +22,7 @@ function mt:doEmmy(action)
elseif tp == 'emmyField' then
self:doEmmyField(action)
elseif tp == 'emmyGeneric' then
+ self:doEmmyGeneric(action)
elseif tp == 'emmyVararg' then
elseif tp == 'emmyLanguage' then
elseif tp == 'emmyArrayType' then
@@ -64,6 +66,12 @@ function mt:getEmmyReturns()
return returns
end
+function mt:getEmmyGeneric()
+ local generic = self._emmyGeneric
+ self._emmyGeneric = nil
+ return generic
+end
+
function mt:doEmmyClass(action)
---@type emmyMgr
local emmyMgr = self.emmyMgr
@@ -120,25 +128,48 @@ function mt:doEmmyAlias(action)
end
end
+function mt:getGenericByType(type)
+ local generics = self._emmyGeneric
+ if not generics then
+ return
+ end
+ if #type > 1 then
+ return
+ end
+ local name = type[1][1]
+ for _, generic in ipairs(generics) do
+ if generic:getName() == name then
+ return generic
+ end
+ end
+ return nil
+end
+
function mt:doEmmyParam(action)
---@type emmyMgr
local emmyMgr = self.emmyMgr
self:instantSource(action)
self:instantSource(action[1])
- local type = self:buildEmmyType(action[2])
+ local type = self:getGenericByType(action[2]) or self:buildEmmyType(action[2])
local param = emmyMgr:addParam(action, type)
action:set('emmy.param', param)
self:addEmmyParam(param)
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
end
function mt:doEmmyReturn(action)
---@type emmyMgr
local emmyMgr = self.emmyMgr
self:instantSource(action)
- local type = self:buildEmmyType(action[1])
+ local type = self:getGenericByType(action[1]) or self:buildEmmyType(action[1])
local rtn = emmyMgr:addReturn(action, type)
action:set('emmy.return', rtn)
self:addEmmyReturn(rtn)
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
end
function mt:doEmmyField(action)
@@ -160,6 +191,24 @@ function mt:doEmmyField(action)
action:set('target class', class)
end
+function mt:doEmmyGeneric(action)
+ ---@type emmyMgr
+ local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+
+ local defs = {}
+ for i, obj in ipairs(action) do
+ defs[i] = {}
+ defs[i].name = self:instantSource(obj[1])
+ if obj[2] then
+ defs[i].type = self:buildEmmyType(obj[2])
+ end
+ end
+
+ local generic = emmyMgr:addGeneric(defs)
+ self._emmyGeneric = generic
+end
+
function mt:doEmmyIncomplete(action)
self:instantSource(action)
end
diff --git a/server/src/vm/function.lua b/server/src/vm/function.lua
index 4c2f0be5..5c9019aa 100644
--- a/server/src/vm/function.lua
+++ b/server/src/vm/function.lua
@@ -259,6 +259,13 @@ function mt:run(vm)
-- 向局部变量中填充参数
for i, loc in ipairs(self.args) do
loc:setValue(self.argValues[i])
+ local emmyParam = self:findEmmyParamByName(loc:getName())
+ if emmyParam then
+ local genericObj = emmyParam:bindGeneric()
+ if genericObj then
+ genericObj:setValue(loc:getValue())
+ end
+ end
end
if self._dots then
self._dots = createMulti()
@@ -271,7 +278,17 @@ function mt:run(vm)
if self._emmyReturns then
for i, rtn in ipairs(self._emmyReturns) do
local value = vm:createValue('nil', rtn:getSource())
- value:setEmmy(rtn:bindType())
+ local typeObj = rtn:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
+ local genericObj = rtn:bindGeneric()
+ if genericObj then
+ local destValue = genericObj:getValue()
+ if destValue then
+ value:mergeType(destValue)
+ end
+ end
self:setReturn(i, value)
end
end
@@ -307,7 +324,10 @@ function mt:createArg(vm, arg)
local emmyParam = self:findEmmyParamByName(arg[1])
local value = valueMgr.create('nil', arg)
if emmyParam then
- value:setEmmy(emmyParam:bindType())
+ local typeObj = emmyParam:bindType()
+ if typeObj then
+ value:setEmmy(typeObj)
+ end
end
local loc = localMgr.create(arg[1], arg, value)
self:saveUpvalue(arg[1], loc)
diff --git a/server/test/hover/init.lua b/server/test/hover/init.lua
index 4b4d0c25..24cf8789 100644
--- a/server/test/hover/init.lua
+++ b/server/test/hover/init.lua
@@ -608,3 +608,16 @@ end
function f()
-> A|B, C
]]
+
+--TEST [[
+-----@generic T
+-----@param x T
+-----@return T
+--local function f(x)
+--end
+--
+--local <?r?> = f(1)
+--]]
+--[[
+--local r: number = 1
+--]]