Документация
-- правильно поставить пробелы в десятичном целом
function FormatDecimalInteger(digits)
	return mw.getCurrentFrame():callParserFunction('formatnum', digits)
end

-- возвращает true для включений в основном пространстве
function InvokedFromArticle()
	return mw.title.getCurrentTitle():inNamespace(0)
end

-- возвращает true, если статья существует
function ArticleExists(link)
	return mw.title.new(link, 0).exists
end

-- отв. за форматирование последовательности в викитекст
function SequenceFormatter(formatNumberMode, itemPrefix, itemSuffix, usualSeparator, lastSeparator)
	-- instance data
	local formatter = {
		_formatNumberMode = formatNumberMode,
		_itemPrefix       = itemPrefix,
		_itemSuffix       = itemSuffix,
		_usualSeparator   = usualSeparator,
		_lastSeparator    = lastSeparator,
		_linkCount = 0,
		_items = {}
	}

	-- вернуть внутреннюю ссылку и посчитать её
	-- изменяет self._linkCount
	function formatter:_WikiLink(link, text)
		self._linkCount = self._linkCount + 1
		return '[[' .. link .. '|' .. text .. ']]'
	end

	-- возвращает метод объекта formatter, форматирующий целое число
	function formatter:_FormatNumberFunc()
		function mode0(self, text)
			return self._itemPrefix .. text .. self._itemSuffix
		end
		function mode1(self, text)
			local link = text .. ' (число)'
			return self._itemPrefix .. self:_WikiLink(link, text) .. self._itemSuffix
		end
		function mode2(self, text)
			local link = text .. ' (число)'
			if ArticleExists(link) then
				return self._itemPrefix .. self:_WikiLink(link, text) .. self._itemSuffix
			end
			return self._itemPrefix .. text .. self._itemSuffix
		end
		local modes = {
			['no'] = mode0,
			['нет'] = mode0,
			['0'] = mode0,
			['yes'] = mode1,
			['да'] = mode1,
			['1'] = mode1,
			['nrl'] = mode2,
			['бкс'] = mode2,
			['2'] = mode2
		}
		return modes[self._formatNumberMode] or mode0
	end

	-- форматирует целое число
	formatter._FormatNumber = formatter:_FormatNumberFunc()

	-- форматирует элемент последовательности
	-- изменяет self._linkCount
	function formatter:_FormatItem(item)
		-- целое число в десятичной системе счисления?
		if mw.ustring.match(item, '^[%s]*[%-]?[%s]*[%d]+[%d%s]*$') then
			local digits = mw.ustring.gsub(item, '%s', '')  -- без пробелов
			local decimal = FormatDecimalInteger(digits)    -- с пробелами
			local number = self:_FormatNumber(decimal)      -- с оформлением
			return number
		end
		-- здесь могли быть другие варианты
		-- ничего не совпало, выводим без изменений
		mw.log(item)
		return item
	end

	-- форматирует и запоминает элемент последовательности
	-- изменяет self._linkCount, self._items
	function formatter:AddItem(item)
		table.insert(self._items, self:_FormatItem(item))
	end

	-- последовательность в викитекст для вывода
	function formatter:Sequence()
		local num_of_items = #self._items
		if num_of_items > 2 then
			return table.concat(self._items, self._usualSeparator,
				1, num_of_items - 1) .. self._lastSeparator .. self._items[num_of_items]
		else
			return table.concat(self._items, self._lastSeparator)
		end
	end

	-- категории в викитекст для вывода
	function formatter:Categories()
		local c = ''
		if (self._linkCount > 0) and InvokedFromArticle() then
			c = c .. '[[Категория:Википедия:Статьи со ссылками на статьи об отдельных числах]]'
		end
		return c
	end

	return formatter
end

-- возвращает первый из параметров в keys, указанных в frame, либо default
function SelectSeparator(frame, keys, default)
	local before = {
		['x'] = ' ',
		['x!'] = '',
		['xx'] = ' ',
		['xx!'] = ''
	}
	local after = {
		['x'] = ' ',
		['x!'] = '',
		['xx'] = ' ',
		['xx!'] = ''
	}
	for i, key in ipairs(keys) do
		local sep = frame.args[key]
		if sep then
			return before[key] .. sep .. after[key]
		end
	end
	return default
end

local p = {}

-- реализация для шаблона
-- код шаблона: {{#invoke:имя_модуля|numsImpl}}
function p.numsImpl(_frame)
	-- доступ к параметрам, переданным в шаблон (а не из шаблона в модуль)
	local frame = _frame:getParent()
	-- настройка по параметрам
	local usep = SelectSeparator(frame, {'x', 'x!'}, ', ')
	local lsep = SelectSeparator(frame, {'xx', 'xx!'}, usep)
	local formatter = SequenceFormatter(
		frame.args['link'] or 'no',
		frame.args['prefix'] or '',
		frame.args['suffix'] or '',
		usep,
		lsep)
	-- получение элементов последовательности
	for i, v in ipairs(frame.args) do
		formatter:AddItem(v)
	end
	--mw.log(formatter._linkCount .. ' links / ' .. #formatter._items .. ' items')
	-- форматирование, автокатегоризация, вывод
	return formatter:Sequence() .. formatter:Categories()
end

return p