---@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' and not vm.isVarargFunctionWithOverloads(n)) 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 ---@param func table ---@return boolean function vm.isVarargFunctionWithOverloads(func) if func.type ~= 'function' then return false end if not func.args then return false end if func.args[1].type ~= '...' then return false end if not func.bindDocs then return false end for _, doc in ipairs(func.bindDocs) do if doc.type == 'doc.overload' then return true end end return false end