246 lines
6.8 KiB
Lua
246 lines
6.8 KiB
Lua
|
---@class vm
|
||
|
local vm = require 'vm.vm'
|
||
|
|
||
|
---@param arg parser.object
|
||
|
---@return parser.object?
|
||
|
local function getDocParam(arg)
|
||
|
if not arg.bindDocs then
|
||
|
return nil
|
||
|
end
|
||
|
for _, doc in ipairs(arg.bindDocs) do
|
||
|
if doc.type == 'doc.param'
|
||
|
and doc.param[1] == arg[1] then
|
||
|
return doc
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
---@param func parser.object
|
||
|
---@return integer min
|
||
|
---@return number max
|
||
|
---@return integer def
|
||
|
function vm.countParamsOfFunction(func)
|
||
|
local min = 0
|
||
|
local max = 0
|
||
|
local def = 0
|
||
|
if func.type == 'function' then
|
||
|
if func.args then
|
||
|
max = #func.args
|
||
|
def = max
|
||
|
for i = #func.args, 1, -1 do
|
||
|
local arg = func.args[i]
|
||
|
if arg.type == '...' then
|
||
|
max = math.huge
|
||
|
elseif arg.type == 'self'
|
||
|
and i == 1 then
|
||
|
min = i
|
||
|
break
|
||
|
elseif getDocParam(arg)
|
||
|
and not vm.compileNode(arg):isNullable() then
|
||
|
min = i
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if func.type == 'doc.type.function' then
|
||
|
if func.args then
|
||
|
max = #func.args
|
||
|
def = max
|
||
|
for i = #func.args, 1, -1 do
|
||
|
local arg = func.args[i]
|
||
|
if arg.name and arg.name[1] =='...' then
|
||
|
max = math.huge
|
||
|
elseif not vm.compileNode(arg):isNullable() then
|
||
|
min = i
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return min, max, def
|
||
|
end
|
||
|
|
||
|
---@param node vm.node
|
||
|
---@return integer min
|
||
|
---@return number max
|
||
|
---@return integer def
|
||
|
function vm.countParamsOfNode(node)
|
||
|
local min, max, def
|
||
|
for n in node:eachObject() do
|
||
|
if n.type == 'function'
|
||
|
or n.type == 'doc.type.function' then
|
||
|
---@cast n parser.object
|
||
|
local fmin, fmax, fdef = vm.countParamsOfFunction(n)
|
||
|
if not min or fmin < min then
|
||
|
min = fmin
|
||
|
end
|
||
|
if not max or fmax > max then
|
||
|
max = fmax
|
||
|
end
|
||
|
if not def or fdef > def then
|
||
|
def = fdef
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return min or 0, max or math.huge, def or 0
|
||
|
end
|
||
|
|
||
|
---@param func parser.object
|
||
|
---@param onlyDoc? boolean
|
||
|
---@param mark? table
|
||
|
---@return integer min
|
||
|
---@return number max
|
||
|
---@return integer def
|
||
|
function vm.countReturnsOfFunction(func, onlyDoc, mark)
|
||
|
if func.type == 'function' then
|
||
|
---@type integer?, number?, integer?
|
||
|
local min, max, def
|
||
|
local hasDocReturn
|
||
|
if func.bindDocs then
|
||
|
local lastReturn
|
||
|
local n = 0
|
||
|
---@type integer?, number?, integer?
|
||
|
local dmin, dmax, ddef
|
||
|
for _, doc in ipairs(func.bindDocs) do
|
||
|
if doc.type == 'doc.return' then
|
||
|
hasDocReturn = true
|
||
|
for _, ret in ipairs(doc.returns) do
|
||
|
n = n + 1
|
||
|
lastReturn = ret
|
||
|
dmax = n
|
||
|
ddef = n
|
||
|
if (not ret.name or ret.name[1] ~= '...')
|
||
|
and not vm.compileNode(ret):isNullable() then
|
||
|
dmin = n
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if lastReturn then
|
||
|
if lastReturn.name and lastReturn.name[1] == '...' then
|
||
|
dmax = math.huge
|
||
|
end
|
||
|
end
|
||
|
if dmin and (not min or (dmin < min)) then
|
||
|
min = dmin
|
||
|
end
|
||
|
if dmax and (not max or (dmax > max)) then
|
||
|
max = dmax
|
||
|
end
|
||
|
if ddef and (not def or (ddef > def)) then
|
||
|
def = ddef
|
||
|
end
|
||
|
end
|
||
|
if not onlyDoc and not hasDocReturn and func.returns then
|
||
|
for _, ret in ipairs(func.returns) do
|
||
|
local rmin, rmax, ddef = vm.countList(ret, mark)
|
||
|
if not min or rmin < min then
|
||
|
min = rmin
|
||
|
end
|
||
|
if not max or rmax > max then
|
||
|
max = rmax
|
||
|
end
|
||
|
if not def or ddef > def then
|
||
|
def = ddef
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return min or 0, max or math.huge, def or 0
|
||
|
end
|
||
|
if func.type == 'doc.type.function' then
|
||
|
return vm.countList(func.returns)
|
||
|
end
|
||
|
error('not a function')
|
||
|
end
|
||
|
|
||
|
---@param func parser.object
|
||
|
---@param mark? table
|
||
|
---@return integer min
|
||
|
---@return number max
|
||
|
---@return integer def
|
||
|
function vm.countReturnsOfCall(func, args, mark)
|
||
|
local funcs = vm.getMatchedFunctions(func, args, mark)
|
||
|
---@type integer?, number?, integer?
|
||
|
local min, max, def
|
||
|
for _, f in ipairs(funcs) do
|
||
|
local rmin, rmax, rdef = vm.countReturnsOfFunction(f, false, mark)
|
||
|
if not min or rmin < min then
|
||
|
min = rmin
|
||
|
end
|
||
|
if not max or rmax > max then
|
||
|
max = rmax
|
||
|
end
|
||
|
if not def or rdef > def then
|
||
|
def = rdef
|
||
|
end
|
||
|
end
|
||
|
return min or 0, max or math.huge, def or 0
|
||
|
end
|
||
|
|
||
|
---@param list parser.object[]?
|
||
|
---@param mark? table
|
||
|
---@return integer min
|
||
|
---@return number max
|
||
|
---@return integer def
|
||
|
function vm.countList(list, mark)
|
||
|
if not list then
|
||
|
return 0, 0, 0
|
||
|
end
|
||
|
local lastArg = list[#list]
|
||
|
if not lastArg then
|
||
|
return 0, 0, 0
|
||
|
end
|
||
|
if lastArg.type == '...'
|
||
|
or lastArg.type == 'varargs' then
|
||
|
return #list - 1, math.huge, #list
|
||
|
end
|
||
|
if lastArg.type == 'call' then
|
||
|
if not mark then
|
||
|
mark = {}
|
||
|
end
|
||
|
if mark[lastArg] then
|
||
|
return #list - 1, math.huge, #list
|
||
|
end
|
||
|
mark[lastArg] = true
|
||
|
local rmin, rmax, rdef = vm.countReturnsOfCall(lastArg.node, lastArg.args, mark)
|
||
|
return #list - 1 + rmin, #list - 1 + rmax, #list - 1 + rdef
|
||
|
end
|
||
|
return #list, #list, #list
|
||
|
end
|
||
|
|
||
|
---@param func parser.object
|
||
|
---@param args parser.object[]?
|
||
|
---@param mark? table
|
||
|
---@return parser.object[]
|
||
|
function vm.getMatchedFunctions(func, args, mark)
|
||
|
local funcs = {}
|
||
|
local node = vm.compileNode(func)
|
||
|
for n in node:eachObject() do
|
||
|
if n.type == 'function'
|
||
|
or n.type == 'doc.type.function' then
|
||
|
funcs[#funcs+1] = n
|
||
|
end
|
||
|
end
|
||
|
if #funcs <= 1 then
|
||
|
return funcs
|
||
|
end
|
||
|
|
||
|
local amin, amax = vm.countList(args, mark)
|
||
|
|
||
|
local matched = {}
|
||
|
for _, n in ipairs(funcs) do
|
||
|
local min, max = vm.countParamsOfFunction(n)
|
||
|
if amin >= min and amax <= max then
|
||
|
matched[#matched+1] = n
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if #matched == 0 then
|
||
|
return funcs
|
||
|
else
|
||
|
return matched
|
||
|
end
|
||
|
end
|