minetest_x_bows/bin/lua-language-server-3.5.6-l.../script/vm/sign.lua
2022-11-03 11:37:33 -04:00

209 lines
7.1 KiB
Lua

local guide = require 'parser.guide'
---@class vm
local vm = require 'vm.vm'
---@class vm.sign
---@field parent parser.object
---@field signList vm.node[]
local mt = {}
mt.__index = mt
mt.type = 'sign'
---@param node vm.node
function mt:addSign(node)
self.signList[#self.signList+1] = node
end
---@param uri uri
---@param args parser.object
---@param removeGeneric true?
---@return table<string, vm.node>?
function mt:resolve(uri, args, removeGeneric)
if not args then
return nil
end
local resolved = {}
---@param object vm.node.object
---@param node vm.node
local function resolve(object, node)
if object.type == 'doc.generic.name' then
local key = object[1]
if object.literal then
-- 'number' -> `T`
for n in node:eachObject() do
if n.type == 'string' then
---@cast n parser.object
local type = vm.declareGlobal('type', n[1], guide.getUri(n))
resolved[key] = vm.createNode(type, resolved[key])
end
end
else
-- number -> T
resolved[key] = vm.createNode(node, resolved[key])
end
end
if object.type == 'doc.type.array' then
for n in node:eachObject() do
if n.type == 'doc.type.array' then
-- number[] -> T[]
resolve(object.node, vm.compileNode(n.node))
end
if n.type == 'doc.type.table' then
-- { [integer]: number } -> T[]
local tvalueNode = vm.getTableValue(uri, node, 'integer', true)
if tvalueNode then
resolve(object.node, tvalueNode)
end
end
if n.type == 'global' and n.cate == 'type' then
-- ---@field [integer]: number -> T[]
---@cast n vm.global
vm.getClassFields(uri, n, vm.declareGlobal('type', 'integer'), false, function (field)
resolve(object.node, vm.compileNode(field.extends))
end)
end
if n.type == 'table' and #n >= 1 then
-- { x } / { ... } -> T[]
resolve(object.node, vm.compileNode(n[1]))
end
end
end
if object.type == 'doc.type.table' then
for _, ufield in ipairs(object.fields) do
local ufieldNode = vm.compileNode(ufield.name)
local uvalueNode = vm.compileNode(ufield.extends)
local firstField = ufieldNode:get(1)
local firstValue = uvalueNode:get(1)
if not firstField or not firstValue then
goto CONTINUE
end
if firstField.type == 'doc.generic.name' and firstValue.type == 'doc.generic.name' then
-- { [number]: number} -> { [K]: V }
local tfieldNode = vm.getTableKey(uri, node, 'any', true)
local tvalueNode = vm.getTableValue(uri, node, 'any', true)
if tfieldNode then
resolve(firstField, tfieldNode)
end
if tvalueNode then
resolve(firstValue, tvalueNode)
end
else
if ufieldNode:get(1).type == 'doc.generic.name' then
-- { [number]: number}|number[] -> { [K]: number }
local tnode = vm.getTableKey(uri, node, uvalueNode, true)
if tnode then
resolve(firstField, tnode)
end
elseif uvalueNode:get(1).type == 'doc.generic.name' then
-- { [number]: number}|number[] -> { [number]: V }
local tnode = vm.getTableValue(uri, node, ufieldNode, true)
if tnode then
resolve(firstValue, tnode)
end
end
end
::CONTINUE::
end
end
end
---@param sign vm.node
---@return table<string, true>
---@return table<string, true>
local function getSignInfo(sign)
local knownTypes = {}
local genericsNames = {}
for obj in sign:eachObject() do
if obj.type == 'doc.generic.name' then
genericsNames[obj[1]] = true
goto CONTINUE
end
if obj.type == 'doc.type.table'
or obj.type == 'doc.type.function'
or obj.type == 'doc.type.array' then
---@cast obj parser.object
local hasGeneric
guide.eachSourceType(obj, 'doc.generic.name', function (src)
hasGeneric = true
genericsNames[src[1]] = true
end)
if hasGeneric then
goto CONTINUE
end
end
local view = vm.viewObject(obj, uri)
if view then
knownTypes[view] = true
end
::CONTINUE::
end
return knownTypes, genericsNames
end
-- remove un-generic type
---@param argNode vm.node
---@param sign vm.node
---@param knownTypes table<string, true>
---@return vm.node
local function buildArgNode(argNode, sign, knownTypes)
local newArgNode = vm.createNode()
local needRemoveNil = sign:hasFalsy()
for n in argNode:eachObject() do
if needRemoveNil then
if n.type == 'nil' then
goto CONTINUE
end
if n.type == 'global' and n.cate == 'type' and n.name == 'nil' then
goto CONTINUE
end
end
local view = vm.viewObject(n, uri)
if knownTypes[view] then
goto CONTINUE
end
newArgNode:merge(n)
::CONTINUE::
end
if not needRemoveNil and argNode:isOptional() then
newArgNode:addOptional()
end
return newArgNode
end
---@param genericNames table<string, true>
local function isAllResolved(genericNames)
for n in pairs(genericNames) do
if not resolved[n] then
return false
end
end
return true
end
for i, arg in ipairs(args) do
local sign = self.signList[i]
if not sign then
break
end
local argNode = vm.compileNode(arg)
local knownTypes, genericNames = getSignInfo(sign)
if not isAllResolved(genericNames) then
local newArgNode = buildArgNode(argNode,sign, knownTypes)
for n in sign:eachObject() do
resolve(n, newArgNode)
end
end
end
return resolved
end
---@return vm.sign
function vm.createSign()
local genericMgr = setmetatable({
signList = {},
}, mt)
return genericMgr
end