模块:生日分类

来自理想城计划

可在模块:生日分类/doc创建此模块的帮助文档

--[=[
供[[模板:生日]]使用,亦可以在其他模块中调用`from_text`、`from_month_day`两个函数

测试例:
{{生日|1年2月3日}} -> 1年[[:分类:2月3日|2月3日]][[分类:2月3日]]
{{生日|2月3日}} / {{生日|2|3}} -> [[:分类:2月3日|2月3日]][[分类:2月3日]]
{{生日|1年<ref>……</ref>2月3日}} -> 1年<ref>……</ref>[[:分类:2月3日|2月3日]][[分类:2月3日]]
{{生日|2月3日<ref>……</ref>}} -> [[:分类:2月3日|2月3日]]<ref>……</ref>[[分类:2月3日]]
{{生日|2月30日}} -> 2月30日[[分类:错误生日]]
{{生日|2月3.0日}} -> 2月3.0日[[分类:错误生日]]
{{生日|2}} -> 2[[分类:错误生日]]
{{生日|阿巴阿巴}} -> 阿巴阿巴[[分类:错误生日]]
]=]

local p = {}

local MAX_DAY_OF_MONTH = {
--  1   2   3   4   5   6   7   8   9  10  11  12
	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
}
local REF_MARKER_PATTERN = '\127\'"`UNIQ%-%-[rR][eE][fF]%-%x%x%x%x%x%x%x%x%-QINU`"\'\127'


local function ns0_category(category)
	return (mw.title.getCurrentTitle().namespace == 0) and ('[[分类:'..category..']]') or ''
end


local function for_hacking_template(date)
	return mw.getCurrentFrame():callParserFunction('#vardefine', '生日日期', date)
end

---@param month string | integer
---@param day string | integer
---@return string? # 错误日期返回nil,正确日期返回分类链接和分类
local function month_day_to_wikitext(month, day)
	month, day = tonumber(month), tonumber(day)
	local max_day = MAX_DAY_OF_MONTH[month]
	if not (max_day and day and day >= 1 and day <= max_day) then
		return nil
	end
	local month_day = month..'月'..day..'日'
	return '[[:分类:'..month_day..'|'..month_day..']]'..ns0_category(month_day)
end


---@param text string
---@param ignore_error boolean? # 错误时不添加“错误生日”分类
---@return string
function p.from_text(text, ignore_error)
	local var = ''
	repeat  -- 仅执行一次,当出现错误时跳出
		local prefix, month, day, suffix = text:match('^(.-)(%d+)月(%d+)日(.-)$')
		if not prefix then  -- string库比mw.ustring更快,也因此不能写“[日号]”
			prefix, month, day, suffix = text:match('^(.-)(%d+)月(%d+)号(.-)$')
		end
		if not prefix then break end
		
		var = for_hacking_template(month..'月'..day..'日')

		if prefix ~= '' and prefix:gsub('^..-年', '', 1):gsub(REF_MARKER_PATTERN, '') ~= '' then
			-- “X月X日”前面的不是“X年”+任意数量的“<ref>……</ref>”
			break
		end
		if suffix ~= '' and suffix:gsub(REF_MARKER_PATTERN, '') ~= '' then
			-- “X月X日”后面的不是任意数量的“<ref>……</ref>”
			break
		end

		local wikitext = month_day_to_wikitext(month, day)
		if not wikitext then break end  -- 月份或日期不合法

		return var..prefix..wikitext..suffix
	until true

	if ignore_error then return var..text end
	return var..text..ns0_category('错误生日')
end


---@param month string
---@param day string
---@param ignore_error boolean? # 错误时不添加“错误生日”分类
---@return string
function p.from_month_day(month, day, ignore_error)
	local result = month_day_to_wikitext(month, day)
	if result then return result end

	result = month..'月'..day..'日'
	if ignore_error then return result end
	return result..ns0_category('错误生日')
end


function p.main(frame)
	local args = frame.args
	local arg1, arg2 = args[1], args[2]
	arg1 = arg1 and mw.text.trim(arg1)
	if not arg1 or arg1 == '' then
		return ''
	end
	local ignore_error = args.ft and args.ft ~= ''
	arg2 = arg2 and mw.text.trim(arg2)
	if arg2 and arg2 ~= '' then
		return p.from_month_day(arg1, arg2, ignore_error)
	end
	return p.from_text(arg1, ignore_error)
end


function p.from_parent(frame)
	return p.main(frame:getParent())
end


return p