---@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 integer max function vm.countParamsOfFunction(func) local min = 0 local max = 0 if func.type == 'function' then if func.args then max = #func.args 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 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 end ---@param node vm.node ---@return integer min ---@return integer max function vm.countParamsOfNode(node) local min, max for n in node:eachObject() do if n.type == 'function' or n.type == 'doc.type.function' then ---@cast n parser.object local fmin, fmax = vm.countParamsOfFunction(n) if not min or fmin < min then min = fmin end if not max or fmax > max then max = fmax end end end return min or 0, max or math.huge end ---@param func parser.object ---@param mark? table ---@return integer min ---@return integer max function vm.countReturnsOfFunction(func, mark) if func.type == 'function' then local min, max local hasDocReturn if func.bindDocs then local lastReturn local n = 0 local dmin, dmax 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 if not vm.compileNode(ret):isNullable() then dmin = n end end end end if lastReturn then if lastReturn.types[1][1] == '...' then dmax = math.huge end 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 end if not hasDocReturn and func.returns then for _, ret in ipairs(func.returns) do local rmin, rmax = vm.countList(ret, mark) if not min or rmin < min then min = rmin end if not max or rmax > max then max = rmax end end end return min or 0, max or math.huge 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 integer max function vm.countReturnsOfCall(func, args, mark) local funcs = vm.getMatchedFunctions(func, args) local min local max for _, f in ipairs(funcs) do local rmin, rmax = vm.countReturnsOfFunction(f, mark) if not min or rmin < min then min = rmin end if not max or rmax > max then max = rmax end end return min or 0, max or math.huge end ---@param list parser.object[]? ---@param mark? table ---@return integer min ---@return integer max function vm.countList(list, mark) if not list then return 0, 0 end local lastArg = list[#list] if not lastArg then return 0, 0 end if lastArg.type == '...' or lastArg.type == 'varargs' then return #list - 1, math.huge end if lastArg.type == 'call' then if not mark then mark = {} end if mark[lastArg] then return #list - 1, math.huge end mark[lastArg] = true local rmin, rmax = vm.countReturnsOfCall(lastArg.node, lastArg.args, mark) return #list - 1 + rmin, #list - 1 + rmax end return #list, #list end ---@param func parser.object ---@param args parser.object[]? ---@return parser.object[] function vm.getMatchedFunctions(func, args) 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) 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