ข้ามไปเนื้อหา

มอดูล:parameter utilities

จาก วิกิพจนานุกรม พจนานุกรมเสรี

local export = {}

local labels_module = "Module:labels"
local parse_utilities_module = "Module:parse utilities"
local references_module = "Module:references"

local function track(page, track_module)
	return require("Module:debug/track")((track_module or "parameter utilities") .. "/" .. page)
end

function export.parse_qualifier(arg, parse_err)
	return {arg}
end

function export.parse_accent_qualifier(arg, parse_err)
	-- FIXME: Pass `parse_err` to split_labels_on_comma().
	return require(labels_module).split_labels_on_comma(arg)
end

function export.parse_references(arg, parse_err)
	return require(references_module).parse_references(arg, parse_err)
end

--[==[ intro:
The `param_mods` structure holds per-param modifiers, which can be specified either as separate parameters (e.g.
{{para|t2}}, {{para|pos3}}) or as inline modifiers (`<t:...>`, `<pos:...>`, etc). The key is the name of the parameter
(e.g. {"t"}, {"pos"}) and the value is a table with optional elements as follows:
* `extra_specs`: A table of extra key-value pairs to add to the spec used for parsing the parameter when specified as a
  separate parameter (e.g. { {type = "boolean"}} for a Boolean parameter, or { {alias_of = "t"}} for the {{para|gloss}}
  parameter, which is aliased to {{para|t}}, on top of the default, which is { {list = true, allow_holes = true}}.
* `convert`: A function to convert the raw argument into the form stored in a term object. This function takes two
  parameters: (1) `arg` (the raw argument); (2) `parse_err` (a function used to throw an error in case of a parse
  error).
* `item_dest`: The name of the key used when storing the parameter's value into the processed term object. Normally the
  same as the parameter's name. Different in the case e.g. of {"t"}, where we store the gloss in {"gloss"}, and {"g"},
  where we store the genders in {"genders"} (in both cases for compatibility with [[Module:links]]).
* `param_key`: The name of the key used when storing the parameter's value into the `args` object returned by
  [[Module:parameters]]. Normally the same as the parameter's name. May be different e.g. in the case of the separate
  no-index pattern (where e.g. {{para|sc}} is distinct from {{para|sc1}}), where e.g. the key {"sc"} would be used to
  hold the value of {{para|sc}} and a key like {"listsc"} would be used to hold the value of {{para|sc1}}, {{para|sc2}},
  etc.; but prefer using `separate_no_index = true` in place of this.
* `require_index`: Same as the `require_index` property in [[Module:parameters]].
* `separate_no_index`: Same as the `separate_no_index` property in [[Module:parameters]].
]==]

local pron_qualifier_param_mods = {
	q = {
		separate_no_index = true,
		convert = export.parse_qualifier,
	},
	qq = {
		separate_no_index = true,
		convert = export.parse_qualifier,
	},
	a = {
		separate_no_index = true,
		convert = export.parse_accent_qualifier,
	},
	aa = {
		separate_no_index = true,
		convert = export.parse_accent_qualifier,
	},
	ref = {
		item_dest = "refs",
		convert = export.parse_references,
	},
}

function export.augment_param_mods_with_pron_qualifiers(param_mods)
	for k, v in pairs(pron_qualifier_param_mods) do
		param_mods[k] = v
	end
end

function export.augment_params_with_modifiers(params, param_mods)
	local list_with_holes = { list = true, allow_holes = true }
	-- Add parameters for each term modifier.
	for param_mod, param_mod_spec in pairs(param_mods) do
		local param_key = param_mod_spec.param_key or param_mod
		if not param_mod_spec.extra_specs and not param_mod_spec.require_index and not param_mod_spec.separate_no_index then
			params[param_key] = list_with_holes
		else
			local param_spec = mw.clone(list_with_holes)
			if param_mod_spec.extra_specs then
				for k, v in pairs(param_mod_spec.extra_specs) do
					param_spec[k] = v
				end
			end
			if param_mod_spec.require_index then
				param_spec.require_index = true
			end
			if param_mod_spec.separate_no_index then
				param_spec.separate_no_index = true
			end
			params[param_key] = param_spec
		end
	end
end

function export.process_list_arguments(data)
	-- Find the maximum index among any of the list parameters.
	local term_args = data.args[data.termarg]
	-- As a special case, the term args might not have a `maxindex` field because they might have
	-- been declared with `disallow_holes = true`, so fall back to the actual length of the list.
	local maxmaxindex = term_args.maxindex or #term_args
	for k, v in pairs(data.args) do
		if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
			maxmaxindex = v.maxindex
		end
	end

	local items = {}
	local ind = 0
	local lang_cache = {}
	if data.lang then
		lang_cache[data.lang:getCode()] = data.lang
	end
	local use_semicolon = false

	local termno = 0
	for i = 1, maxmaxindex do
		local term = term_args[i]
		if term ~= ";" then
			termno = termno + 1

			-- Compute whether any of the separate indexed params exist for this index.
			local any_param_at_index = term ~= nil
			if not any_param_at_index then
				for k, v in pairs(data.args) do
					-- Look for named list parameters. We check:
					-- (1) key is a string (excludes the term param, which is a number);
					-- (2) value is a table, i.e. a list;
					-- (3) v.maxindex is set (i.e. allow_holes was used);
					-- (4) the value has an entry at index `termno` (the current logical index).
					if type(k) == "string" and type(v) == "table" and v.maxindex and v[termno] then
						any_param_at_index = true
						break
					end
				end
			end

			-- If any of the params used for formatting this term is present, create a term and add it to the list.
			if not any_param_at_index then
				track("skipped-term", data.track_module)
			else
				if not term then
					track("missing-term", data.track_module)
				end
				-- Initialize the `termobj` object passed to full_link() in [[Module:links]].
				local termobj = {
					separator = i > 1 and (term_args[i - 1] == ";" and "; " or ", ") or "",
					term = term,
				}

				-- Parse all the term-specific parameters and store in `termobj`.
				for param_mod, param_mod_spec in pairs(data.param_mods) do
					local dest = param_mod_spec.item_dest or param_mod
					local param_key = param_mod_spec.param_key or param_mod
					local arg = data.args[param_key] and data.args[param_key][termno]
					if arg then
						if param_mod_spec.convert then
							local function parse_err(msg, stack_frames_to_ignore)
								error(("%s: %s%s=%s"):format(
									msg, param_mod, (termno > 1 or param_mod_spec.require_index or
										param_mod_spec.separate_no_index) and termno or "", arg
								), stack_frames_to_ignore
								)
							end
							arg = param_mod_spec.convert(arg, parse_err)
						end
						termobj[dest] = arg
					end
				end

				local function generate_obj(term, parse_err)
					local term_dest = data.term_dest or "term"
					if data.parse_lang_prefix and term:find(":") then
						local actual_term, termlangs = require(parse_utilities_module).parse_term_with_lang {
							term = term,
							parse_err = parse_err,
							paramname = paramname,
							allow_bad = data.allow_bad_lang_prefix,
							allow_multiple = data.allow_multiple_lang_prefixes,
							lang_cache = lang_cache,
						}
						termobj[term_dest] = actual_term ~= "" and actual_term or nil
						if data.allow_multiple_lang_prefixes then
							termobj.termlangs = termlangs
							termobj.lang = termlangs and termlangs[1] or nil
						else
							termobj.termlang = termlangs
							termobj.lang = termlangs
						end
					else
						termobj[term_dest] = term ~= "" and term or nil
					end
					return termobj
				end

				-- Check for inline modifier, e.g. מרים<tr:Miryem>. But exclude top-level HTML entry with <span ...>,
				-- <br/> or similar in it, often caused by wrapping an argument in {{m|...}} or similar.
				if term and term:find("<") and not require(parse_utilities_module).term_contains_top_level_html(term) then
					require(parse_utilities_module).parse_inline_modifiers(term, {
						-- Add 1 because first term index starts at 2.
						paramname = data.termarg + i - 1,
						param_mods = data.param_mods,
						generate_obj = generate_obj,
					})
				elseif term then
					generate_obj(term)
				end
				-- Set these after parsing inline modifiers, not in generate_obj(), otherwise we'll get an error in
				-- parse_inline_modifiers() if we try to use <lang:...> or <sc:...> as inline modifiers.
				termobj.lang = termobj.lang or data.lang
				termobj.sc = termobj.sc or data.sc

				-- If the displayed term (from .term or .alt) has an embedded comma, use a semicolon to join the terms.
				local term_text = termobj.term or termobj.alt
				if not use_semicolon and term_text then
					if term_text:find(",", 1, true) then
						use_semicolon = true
					end
				end

				-- If the to-be-linked term is the same as the pagename, maybe display it unlinked.
				if data.disallow_self_link and data.lang and data.pagename and termobj.term and
					(data.lang:makeEntryName(termobj.term)) == data.pagename then
					track("term-is-pagename", data.track_module)
					termobj.alt = termobj.alt or termobj.term
					termobj.term = nil
				end

				table.insert(items, termobj)
			end
		end
	end

	if use_semicolon then
		for i, item in ipairs(items) do
			if i > 1 then
				item.separator = "; "
			end
		end
	end

	return items
end


return export