Module:parse utilities: Difference between revisions

Jump to navigation Jump to search
no edit summary
No edit summary
No edit summary
 
Line 1: Line 1:
local export = {}
local export = {}


local m_string_utilities = require("Module:string utilities")
local fun_is_callable_module = "Module:fun/isCallable"
local languages_module = "Module:languages"
local parameters_module = "Module:parameters"
local parameters_module = "Module:parameters"
local string_char_module = "Module:string/char"
local string_utilities_module = "Module:string utilities"
local table_insert_if_not_module = "Module:table/insertIfNot"


local assert = assert
local concat = table.concat
local dump = mw.dumpObject
local error = error
local insert = table.insert
local ipairs = ipairs
local list_to_text = mw.text.listToText
local list_to_text = mw.text.listToText
local rfind = mw.ustring.find
local pairs = pairs
local rsplit = mw.text.split
local require = require
local u = mw.ustring.char
local sort = table.sort
local rsubn = mw.ustring.gsub
local type = type
local ugsub = mw.ustring.gsub


-- version of rsubn() that discards all but the first return value
local function convert_val(...)
local function rsub(term, foo, bar)
convert_val = require(parameters_module).convert_val
local retval = rsubn(term, foo, bar)
return convert_val(...)
return retval
end
end


local function get_lang(...)
get_lang = require(languages_module).getByCode
return get_lang(...)
end
local function insert_if_not(...)
insert_if_not = require(table_insert_if_not_module)
return insert_if_not(...)
end
local function is_callable(...)
is_callable = require(fun_is_callable_module)
return is_callable(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function u(...)
u = require(string_char_module)
return u(...)
end
local function umatch(...)
umatch = require(string_utilities_module).match
return umatch(...)
end


--[==[ intro:
--[==[ intro:
Line 63: Line 102:
]==]
]==]
function export.parse_balanced_segment_run(segment_run, open, close)
function export.parse_balanced_segment_run(segment_run, open, close)
return m_string_utilities.split(segment_run, "(%b" .. open .. close .. ")")
return split(segment_run, "(%b" .. open .. close .. ")")
end
end


Line 69: Line 108:
--[=[
--[=[
function export.parse_balanced_segment_run(segment_run, open, close)
function export.parse_balanced_segment_run(segment_run, open, close)
local break_on_open_close = m_string_utilities.split(segment_run, "([%" .. open .. "%" .. close .. "])")
local break_on_open_close = split(segment_run, "([%" .. open .. "%" .. close .. "])")
local text_and_specs = {}
local text_and_specs = {}
local level = 0
local level = 0
Line 76: Line 115:
if i % 2 == 0 then
if i % 2 == 0 then
if seg == open then
if seg == open then
table.insert(seg_group, seg)
insert(seg_group, seg)
level = level + 1
level = level + 1
else
else
assert(seg == close)
assert(seg == close)
table.insert(seg_group, seg)
insert(seg_group, seg)
level = level - 1
level = level - 1
if level < 0 then
if level < 0 then
error("Unmatched " .. close .. " sign: '" .. segment_run .. "'")
error("Unmatched " .. close .. " sign: '" .. segment_run .. "'")
elseif level == 0 then
elseif level == 0 then
table.insert(text_and_specs, table.concat(seg_group))
insert(text_and_specs, concat(seg_group))
seg_group = {}
seg_group = {}
end
end
end
end
elseif level > 0 then
elseif level > 0 then
table.insert(seg_group, seg)
insert(seg_group, seg)
else
else
table.insert(text_and_specs, seg)
insert(text_and_specs, seg)
end
end
end
end
Line 124: Line 163:
local open_items = {}
local open_items = {}
for _, open_close in ipairs(delimiter_pairs) do
for _, open_close in ipairs(delimiter_pairs) do
local open, close = unpack(open_close)
local open, close = open_close[1], open_close[2]
open = rsub(open, "([%[%]%%%%-])", "%%%1")
open = open:gsub("([%[%]%%%%-])", "%%%1")
close = rsub(close, "([%[%]%%%%-])", "%%%1")
close = close:gsub("([%[%]%%%%-])", "%%%1")
table.insert(open_close_items, open)
insert(open_close_items, open)
table.insert(open_close_items, close)
insert(open_close_items, close)
table.insert(open_items, open)
insert(open_items, open)
open = "[" .. open .. "]"
open = "[" .. open .. "]"
close = "[" .. close .. "]"
close = "[" .. close .. "]"
open_to_close_map[open] = close
open_to_close_map[open] = close
table.insert(escaped_delimiter_pairs, {open, close})
insert(escaped_delimiter_pairs, {open, close})
end
end
local open_close_pattern = "([" .. table.concat(open_close_items) .. "])"
local open_close_pattern = "([" .. concat(open_close_items) .. "])"
local open_pattern = "([" .. table.concat(open_items) .. "])"
local open_pattern = "([" .. concat(open_items) .. "])"
local break_on_open_close = m_string_utilities.split(segment_run, open_close_pattern)
local break_on_open_close = split(segment_run, open_close_pattern)
local text_and_specs = {}
local text_and_specs = {}
local level = 0
local level = 0
Line 145: Line 184:
for i, seg in ipairs(break_on_open_close) do
for i, seg in ipairs(break_on_open_close) do
if i % 2 == 0 then
if i % 2 == 0 then
table.insert(seg_group, seg)
insert(seg_group, seg)
if level == 0 then
if level == 0 then
if not rfind(seg, open_pattern) then
if not umatch(seg, open_pattern) then
local errmsg = "Unmatched close sign " .. seg .. ": '" .. segment_run .. "'"
local errmsg = "Unmatched close sign " .. seg .. ": '" .. segment_run .. "'"
if no_error_on_unmatched then
if no_error_on_unmatched then
Line 157: Line 196:
assert(open_at_level_zero == nil)
assert(open_at_level_zero == nil)
for _, open_close in ipairs(escaped_delimiter_pairs) do
for _, open_close in ipairs(escaped_delimiter_pairs) do
local open, close = unpack(open_close)
local open = open_close[1]
if rfind(seg, open) then
if umatch(seg, open) then
open_at_level_zero = open
open_at_level_zero = open
            break
            break
Line 167: Line 206:
end
end
level = level + 1
level = level + 1
elseif rfind(seg, open_at_level_zero) then
elseif umatch(seg, open_at_level_zero) then
level = level + 1
level = level + 1
elseif rfind(seg, open_to_close_map[open_at_level_zero]) then
elseif umatch(seg, open_to_close_map[open_at_level_zero]) then
level = level - 1
level = level - 1
assert(level >= 0)
assert(level >= 0)
if level == 0 then
if level == 0 then
table.insert(text_and_specs, table.concat(seg_group))
insert(text_and_specs, concat(seg_group))
seg_group = {}
seg_group = {}
open_at_level_zero = nil
open_at_level_zero = nil
Line 179: Line 218:
end
end
elseif level > 0 then
elseif level > 0 then
table.insert(seg_group, seg)
insert(seg_group, seg)
else
else
table.insert(text_and_specs, seg)
insert(text_and_specs, seg)
end
end
end
end
Line 222: Line 261:
end
end
return false
return false
end
--[==[
Check whether a term appears to have already been passed through `full_link()`. Passing it again will mangle it in
various ways; at best it will have unnecessary lang/script wrapping, which might do nothing but might result in
overly large fonts or other issues. We also check for uses of {{tl|ja-r/args}}, {{tl|ryu-r/args}} or {{tl|ko-l/args}},
which will be manged by `full_link()`. If this check succeeds, use the text raw instead of passing through
`full_link()`.
]==]
function export.term_already_linked(term)
return term:find("<span") or term:find("{{ja%-r|") or term:find("{{ryu%-r|") or term:find("{{ko%-l|")
end
end


Line 273: Line 323:
for i, seg in ipairs(segment_runs) do
for i, seg in ipairs(segment_runs) do
if i % 2 == 0 then
if i % 2 == 0 then
table.insert(run, seg)
insert(run, seg)
else
else
local parts =
local parts = split(seg, preserve_splitchar and "(" .. splitchar .. ")" or splitchar)
preserve_splitchar and m_string_utilities.split(seg, "(" .. splitchar .. ")") or
insert(run, parts[1])
rsplit(seg, splitchar)
table.insert(run, parts[1])
for j=2,#parts do
for j=2,#parts do
table.insert(grouped_runs, run)
insert(grouped_runs, run)
run = {parts[j]}
run = {parts[j]}
end
end
Line 286: Line 334:
end
end
if #run > 0 then
if #run > 0 then
table.insert(grouped_runs, run)
insert(grouped_runs, run)
end
end
return grouped_runs
return grouped_runs
Line 329: Line 377:
i = i + 2
i = i + 2
else
else
table.insert(joined_runs, run)
insert(joined_runs, run)
i = i + 1
i = i + 1
end
end
Line 338: Line 386:


function export.strip_spaces(text)
function export.strip_spaces(text)
return rsub(text, "^%s*(.-)%s*$", "%1")
return (ugsub(text, "^%s*(.-)%s*$", "%1"))
end
end


Line 398: Line 446:
-- First replace comma with a temporary character in comma+whitespace sequences.
-- First replace comma with a temporary character in comma+whitespace sequences.
local need_unescape = false
local need_unescape = false
for i, seg in ipairs(run) do
for i in ipairs(run) do
if i % 2 == 1 then
if i % 2 == 1 and escape_fun then
local this_need_unescape
local this_need_unescape
run[i], this_need_unescape = escape_fun(run[i])
run[i], this_need_unescape = escape_fun(run[i])
Line 422: Line 470:


if run:find("\\,") then
if run:find("\\,") then
run = run:gsub("\\,", "\\" .. tempcomma) -- assign to temp to discard second return value
-- FIXME: we should probably convert literal \\ to \ to allow people to put a backslash before a comma that
-- should be passed through; but maybe it's enough to use an HTML escape for the comma or backslash.
run = (run:gsub("\\,", tempcomma)) -- discard backslash before comma, doing its duty to protect the comma
escaped = true
escaped = true
end
end
if run:find(",%s") then
if run:find(",%s") then
run = run:gsub(",(%s)", tempcomma .. "%1") -- assign to temp to discard second return value
run = (run:gsub(",(%s)", tempcomma .. "%1"))
escaped = true
escaped = true
end
end
Line 439: Line 489:
tempcomma = tempcomma or u(0xFFF0)
tempcomma = tempcomma or u(0xFFF0)


run = run:gsub(tempcomma, ",") -- assign to temp to discard second return value
return (run:gsub(tempcomma, ","))
return run
end
end


Line 474: Line 523:
]==]
]==]
function export.split_escaping(text, splitchar, preserve_splitchar, escape_fun, unescape_fun)
function export.split_escaping(text, splitchar, preserve_splitchar, escape_fun, unescape_fun)
if not rfind(text, splitchar) then
if not umatch(text, splitchar) then
return {text}
return {text}
end
end
Line 490: Line 539:
unescape_fun)
unescape_fun)
for i = 1, #split_runs, (preserve_splitchar and 2 or 1) do
for i = 1, #split_runs, (preserve_splitchar and 2 or 1) do
split_runs[i] = table.concat(split_runs[i])
split_runs[i] = concat(split_runs[i])
end
end
return split_runs
return split_runs
Line 498: Line 547:
-- First escape sequences we don't want to count for splitting.
-- First escape sequences we don't want to count for splitting.
local need_unescape
local need_unescape
text, need_unescape = escape_fun(text)
if escape_fun then
text, need_unescape = escape_fun(text)
end


local parts =
local parts = split(text, preserve_splitchar and "(" .. splitchar .. ")" or splitchar)
preserve_splitchar and m_string_utilities.split(text, "(" .. splitchar .. ")") or
rsplit(text, splitchar)
if need_unescape then
if need_unescape then
for i = 1, #parts, (preserve_splitchar and 2 or 1) do
for i = 1, #parts, (preserve_splitchar and 2 or 1) do
Line 572: Line 621:
return term, nil
return term, nil
end
end
local parts = rsplit(inside, "|")
local parts = split(inside, "|")
if #parts > 2 then
if #parts > 2 then
parse_err("Saw more than two parts inside a bracketed link")
parse_err("Saw more than two parts inside a bracketed link")
end
end
return unpack(parts)
return parts[1], parts[2]
end
end
return term, nil
return term, nil
Line 583: Line 632:


--[==[
--[==[
Parse a term that may have a language code (or possibly multiple comma-separated language codes, if `allow_multiple`
Parse a term that may have a language code (or possibly multiple plus-separated language codes, if
is given) preceding it (e.g. {la:minūtia} or {grc:[[σκῶρ|σκατός]]} or {nan-hbl,hak:[[毋]][[知]]}). Return four
`data.allow_multiple` is given) preceding it (e.g. {la:minūtia} or {grc:[[σκῶρ|σκατός]]} or
arguments:
{nan-hbl+hak:[[毋]][[知]]}). Return four arguments:
# the term minus the language code;
# the term minus the language code;
# the language object corresponding to the language code (possibly a family object if `allow_family` is given), or a
# the language object corresponding to the language code (possibly a family object if `data.allow_family` is given), or
   list of such objects if `allow_multiple` is given;
   a list of such objects if `data.allow_multiple` is given;
# the link if the term is of the form {[[<var>link</var>|<var>display</var>]]} (it may be generated into that form with
# the link if the term is of the form {[[<var>link</var>|<var>display</var>]]} (it may be generated into that form with
   Wikipedia and Wikisource prefixes) or of the form {{[[<var>link</var>]]}, otherwise the full term;
   Wikipedia and Wikisource prefixes) or of the form {{[[<var>link</var>]]}, otherwise the full term;
# the display part if the term is of the form {[[<var>link</var>|<var>display</var>]]}, otherwise nil.
# the display part if the term is of the form {[[<var>link</var>|<var>display</var>]]}, otherwise nil.
Etymology-only languages are allowed. This function also correctly handles Wikipedia prefixes (e.g. {w:Abatemarco}
Etymology-only languages are always allowed. This function also correctly handles Wikipedia prefixes (e.g.
or {w:it:Colle Val d'Elsa} or {lw:ru:Филарет}) and Wikisource prefixes (e.g. {s:Twelve O'Clock} or
{w:Abatemarco} or {w:it:Colle Val d'Elsa} or {lw:ru:Филарет}) and Wikisource prefixes (e.g. {s:Twelve O'Clock} or
{s:[[Walden/Chapter XVIII|Walden]]} or {s:fr:Perceval ou le conte du Graal} or {s:ro:[[Domnul Vucea|Mr. Vucea]]} or
{s:[[Walden/Chapter XVIII|Walden]]} or {s:fr:Perceval ou le conte du Graal} or {s:ro:[[Domnul Vucea|Mr. Vucea]]} or
{ls:ko:이상적 부인} or {ls:ko:[[조선 독립의 서#一. 槪論|조선 독립의 서]]}) and converts them into two-part links,
{ls:ko:이상적 부인} or {ls:ko:[[조선 독립의 서#一. 槪論|조선 독립의 서]]}) and converts them into two-part links,
Line 606: Line 655:
display parts, you must check for this.
display parts, you must check for this.


`parse_err_or_paramname` is an optional function of one or two arguments to display an error, or a string naming a
The calling convention is to pass in a single argument `data` containing the following fields:
parameter to display in the error message. If omitted, a function is generated based off of `term`. (The second
* `term`: The term to parse.
argument to the function is the number of stack frames to ignore when calling error(); if you declare your error
* `parse_err`: An optional function of one or two arguments to display an error. (The second argument to the function is
function with only one argument, things will still work fine.)
  the number of stack frames to ignore when calling error(); if you declare your error function with only one argument,
  things will still work fine.)
* `paramname`: If `parse_err` is omitted, this should be a string naming a parameter to display in the error message,
  along with the term in question, and will be used to generate a `parse_err` function using `make_parse_err()`. (If
  `paramname` is omitted, just the term itself appears in the error message.)
* `allow_multiple`: Allow multiple plus-separated language codes, e.g. {nan-hbl+hak:[[毋]][[知]]}. See above.
* `allow_family`: Allow family objects to appear in place of language codes.
* `allow_bad`: Don't throw an error on invalid language code prefixes; instead, include the prefix and colon as part of
  the term. Note that if a prefix doesn't look like a language code (e.g. if it's a number), the code won't even try to
  parse it as a language code, regardless of the `allow_bad` setting, but will always include it in the term.
* `lang_cache`: A table mapping language codes to language objects. If the value is `false`, the language code is
  invalid. If specified, the cache will be consulted before calling `getByCode()` in [[Module:languages]], and the
  result cached. If not specified, no cache will be used.
]==]
]==]
function export.parse_term_with_lang(data_or_term, parse_err_or_paramname)
function export.parse_term_with_lang(data)
if type(data_or_term) == "string" then
local term = data.term
data_or_term = {
local parse_err = data.parse_err or
term = data_or_term
data.paramname and export.make_parse_err(("%s=%s"):format(data.paramname, term)) or
}
if type(parse_err_or_paramname) == "function" then
data_or_term.parse_err = parse_err_or_paramname
else
data_or_term.paramname = parse_err_or_paramname
end
end
local term = data_or_term.term
local parse_err = data_or_term.parse_err or
data_or_term.paramname and export.make_parse_err(("%s=%s"):format(data_or_term.paramname, term)) or
export.make_parse_err(term)
export.make_parse_err(term)
-- Parse off an initial language code (e.g. 'la:minūtia' or 'grc:[[σκῶρ|σκατός]]'). First check for Wikipedia
-- Parse off an initial language code (e.g. 'la:minūtia' or 'grc:[[σκῶρ|σκατός]]'). First check for Wikipedia
Line 644: Line 695:
parse_err("Cannot have embedded brackets following a Wikipedia (w:... or lw:...) link; expand the term to a fully bracketed term w:[[LINK|DISPLAY]] or similar")
parse_err("Cannot have embedded brackets following a Wikipedia (w:... or lw:...) link; expand the term to a fully bracketed term w:[[LINK|DISPLAY]] or similar")
end
end
local lang = wiki_links and require("Module:languages").getByCode(foreign_wiki, parse_err, "allow etym") or nil
local lang = wiki_links and get_lang(foreign_wiki, parse_err, "allow etym") or nil
local prefixed_link = wiki_prefix .. link
local prefixed_link = wiki_prefix .. link
return ("[[%s|%s]]"):format(prefixed_link, display or link), lang, prefixed_link, display
return ("[[%s|%s]]"):format(prefixed_link, display or link), lang, prefixed_link, display
Line 685: Line 736:
local function get_by_code(code, allow_bad)
local function get_by_code(code, allow_bad)
local lang
local lang
if data_or_term.lang_cache then
if data.lang_cache then
lang = data_or_term.lang_cache[code]
lang = data.lang_cache[code]
end
end
if lang == nil then
if lang == nil then
lang = require("Module:languages").getByCode(code, not allow_bad and parse_err or nil, "allow etym",
lang = get_lang(code, not allow_bad and parse_err or nil, "allow etym",
data_or_term.allow_family)
data.allow_family)
if data_or_term.lang_cache then
if data.lang_cache then
data_or_term.lang_cache[code] = lang or false
data.lang_cache[code] = lang or false
end
end
end
end
Line 698: Line 749:
end
end


if data_or_term.allow_multiple then
if data.allow_multiple then
local termlang_spec
local termlang_spec
termlang_spec, actual_term = term:match("^([a-zA-Z.,+-]+):([^ ].*)$")
termlang_spec, actual_term = term:match("^([a-zA-Z.,+-]+):([^ ].*)$")
if termlang_spec then
if termlang_spec then
termlang = rsplit(termlang_spec, "[,+]")
termlang = split(termlang_spec, "[,+]")
local all_possible_code = true
local all_possible_code = true
for _, code in ipairs(termlang) do
for _, code in ipairs(termlang) do
Line 713: Line 764:
local saw_nil = false
local saw_nil = false
for i, code in ipairs(termlang) do
for i, code in ipairs(termlang) do
termlang[i] = get_by_code(code, data_or_term.allow_bad)
termlang[i] = get_by_code(code, data.allow_bad)
if not termlang[i] then
if not termlang[i] then
saw_nil = true
saw_nil = true
Line 731: Line 782:
if termlang then
if termlang then
if is_possible_lang_code(termlang) then
if is_possible_lang_code(termlang) then
termlang = get_by_code(termlang, data_or_term.allow_bad)
termlang = get_by_code(termlang, data.allow_bad)
if termlang then
if termlang then
term = actual_term
term = actual_term
Line 780: Line 831:
** `escape_fun` and `unescape_fun` are as in split_escaping() and split_alternating_runs_escaping() above and
** `escape_fun` and `unescape_fun` are as in split_escaping() and split_alternating_runs_escaping() above and
   control the protected sequences that won't be split. By default, `escape_comma_whitespace` and
   control the protected sequences that won't be split. By default, `escape_comma_whitespace` and
   `unescape_comma_whitespace` are used, so that comma+whitespace sequences won't be split.
   `unescape_comma_whitespace` are used, so that comma+whitespace sequences won't be split. Set to `false` to disable
  escaping/unescaping.
** `pre_normalize_modifiers`, if specified, is a function of one argument, which can be used to "normalize" modifiers
** `pre_normalize_modifiers`, if specified, is a function of one argument, which can be used to "normalize" modifiers
   prior to further parsing. This is used, for example, in [[Module:tl-pronunciation]] to convert modifiers of the
   prior to further parsing. This is used, for example, in [[Module:tl-pronunciation]] to convert modifiers of the
Line 848: Line 900:
   a value. (This means that multiple occurrences of a given modifier are allowed if `store` is given, but not
   a value. (This means that multiple occurrences of a given modifier are allowed if `store` is given, but not
   otherwise.) `store` can be one of the following:
   otherwise.) `store` can be one of the following:
** {"insert"}: the converted value is appended to the key's value using {table.insert()}; if the key has no value, it
** {"insert"}: the converted value is appended to the key's value using {insert()}; if the key has no value, it
   is first converted to an empty list;
   is first converted to an empty list;
** {"insertIfNot"}: is similar but appends the value using {insertIfNot()} in [[Module:table]];
** {"insertIfNot"}: is similar but appends the value using {insertIfNot()} in [[Module:table]];
** {"insert-flattened"}, the converted value is assumed to be a list and the objects are appended one-by-one into the
** {"insert-flattened"}, the converted value is assumed to be a list and the objects are appended one-by-one into the
   key's existing value using {table.insert()};
   key's existing value using {insert()};
** {"insertIfNot-flattened"} is similar but appends using {insertIfNot()} in [[Module:table]]; (WARNING: When using
** {"insertIfNot-flattened"} is similar but appends using {insertIfNot()} in [[Module:table]]; (WARNING: When using
   {"insert-flattened"} and {"insertIfNot-flattened"}, if there is no existing value for the key, the converted value is
   {"insert-flattened"} and {"insertIfNot-flattened"}, if there is no existing value for the key, the converted value is
Line 905: Line 957:


local function verify_no_overall()
local function verify_no_overall()
for mod, mod_props in pairs(props.param_mods) do
for _, mod_props in pairs(props.param_mods) do
if mod_props.overall then
if mod_props.overall then
error("Internal caller error: Can't specify `overall` for a modifier in `param_mods` unless `outer_container` property is given")
error("Internal caller error: Can't specify `overall` for a modifier in `param_mods` unless `outer_container` property is given")
Line 930: Line 982:
else
else
verify_no_overall()
verify_no_overall()
end
local escape_fun = props.escape_fun
if escape_fun == nil then
escape_fun = export.escape_comma_whitespace
end
local unescape_fun = props.unescape_fun
if unescape_fun == nil then
unescape_fun = export.unescape_comma_whitespace
end
end
local separated_groups = export.split_alternating_runs_escaping(segments, props.splitchar,
local separated_groups = export.split_alternating_runs_escaping(segments, props.splitchar,
props.preserve_splitchar, props.escape_fun or export.escape_comma_whitespace,
props.preserve_splitchar, escape_fun, unescape_fun)
props.unescape_fun or export.unescape_comma_whitespace)
for j = 1, #separated_groups, (props.preserve_splitchar and 2 or 1) do
for j = 1, #separated_groups, (props.preserve_splitchar and 2 or 1) do
if rejoin_square_brackets_after_split then
if rejoin_square_brackets_after_split then
Line 948: Line 1,007:
parsed[props.delimiter_key or "delimiter"] = separated_groups[j - 1][1]
parsed[props.delimiter_key or "delimiter"] = separated_groups[j - 1][1]
end
end
table.insert(terms, parsed)
insert(terms, parsed)
end
end
if props.outer_container then
if props.outer_container then
Line 986: Line 1,045:
local function get_valid_prefixes()
local function get_valid_prefixes()
local valid_prefixes = {}
local valid_prefixes = {}
for param_mod, _ in pairs(props.param_mods) do
for param_mod, mod_props in pairs(props.param_mods) do
table.insert(valid_prefixes, param_mod)
if not mod_props.deprecated then
insert(valid_prefixes, param_mod)
end
end
end
table.sort(valid_prefixes)
sort(valid_prefixes)
return valid_prefixes
return valid_prefixes
end
end
Line 1,067: Line 1,128:
-- makes use of the field `required`, but only if `set` is set.) If this becomes problematic, consider
-- makes use of the field `required`, but only if `set` is set.) If this becomes problematic, consider
-- removing the optimization.
-- removing the optimization.
converted = require(parameters_module).convert_val(converted, prefix_parse_err, mod_props)
converted = convert_val(converted, prefix_parse_err, mod_props)
end
end
local store = props.param_mods[prefix].store
local store = props.param_mods[prefix].store
Line 1,079: Line 1,140:
dest[key] = {converted}
dest[key] = {converted}
else
else
table.insert(dest[key], converted)
insert(dest[key], converted)
end
end
elseif store == "insertIfNot" then
elseif store == "insertIfNot" then
Line 1,085: Line 1,146:
dest[key] = {converted}
dest[key] = {converted}
else
else
require("Module:table").insertIfNot(dest[key], converted)
insert_if_not(dest[key], converted)
end
end
elseif store == "insert-flattened" then
elseif store == "insert-flattened" then
Line 1,092: Line 1,153:
else
else
for _, obj in ipairs(converted) do
for _, obj in ipairs(converted) do
table.insert(dest[key], obj)
insert(dest[key], obj)
end
end
end
end
Line 1,100: Line 1,161:
else
else
for _, obj in ipairs(converted) do
for _, obj in ipairs(converted) do
require("Module:table").insertIfNot(dest[key], obj)
insert_if_not(dest[key], obj)
end
end
end
end
elseif type(store) == "string" then
elseif type(store) == "string" then
prefix_parse_err(("Internal caller error: Unrecognized value '%s' for `store` property"):format(store))
prefix_parse_err(("Internal caller error: Unrecognized value '%s' for `store` property"):format(store))
elseif type(store) ~= "function" then
elseif not is_callable(store) then
prefix_parse_err(("Internal caller error: Unrecognized type for `store` property %s"):format(
prefix_parse_err(("Internal caller error: Unrecognized type for `store` property %s"):format(dump(store)))
mw.dumpObject(store)))
else
else
store {
store{
dest = dest,
dest = dest,
key = key,
key = key,

Navigation menu