Module:affix/templates: Difference between revisions

No edit summary
No edit summary
 
(9 intermediate revisions by the same user not shown)
Line 3: Line 3:
local m_affix = require("Module:affix")
local m_affix = require("Module:affix")
local m_utilities = require("Module:utilities")
local m_utilities = require("Module:utilities")
local en_utilities_module = "Module:en-utilities"
local parameter_utilities_module = "Module:parameter utilities"
local parameter_utilities_module = "Module:parameter utilities"
local pseudo_loan_module = "Module:affix/pseudo-loan"
local pseudo_loan_module = "Module:affix/pseudo-loan"


local insert = table.insert
local boolean_param = {type = "boolean"}


local function is_property_key(k)
local function is_property_key(k)
Line 11: Line 15:
end
end


local recognized_affix_types = {
prefix = "prefix",
pre = "prefix",
suffix = "suffix",
suf = "suffix",
interfix = "interfix",
inter = "interfix",
infix = "infix",
["in"] = "infix",
circumfix = "circumfix",
circum = "circumfix",
["non-affix"] = "non-affix",
naf = "non-affix",
root = "non-affix",
}
local function pre_normalize_affix_type(data)
local modtext = data.modtext
modtext = modtext:match("^<(.*)>$")
if not modtext then
error(("Internal error: Passed-in modifier isn't surrounded by angle brackets: %s"):format(data.modtext))
end
if recognized_affix_types[modtext] then
modtext = "type:" .. modtext
end
return "<" .. modtext .. ">"
end


-- Parse raw arguments. A single parameter `data` is passed in, with the following fields:
-- Parse raw arguments. A single parameter `data` is passed in, with the following fields:
Line 23: Line 55:
-- * `require_index_for_pos`: There is no separate |pos= parameter distinct from |pos1=, |pos2=, etc. Instead,
-- * `require_index_for_pos`: There is no separate |pos= parameter distinct from |pos1=, |pos2=, etc. Instead,
--  specifying |pos= results in an error.
--  specifying |pos= results in an error.
-- * `dont_require_index`: Allow |foo= to be specified as a synonym for |foo1= (except for |lit=, which remains
--  distinct).
-- * `allow_type`: Allow |type1=, |type2=, etc. or inline <type:...> for the affix type, and allow a separate |type=
--  parameter for the etymology type (FIXME: this may be confusing; consider changing the etymology type to |etype=).
-- * `allow_semicolon_separator`: Allow semicolon as a separator, displaying as " or ". This requires changes in the
--  display of the output, to not always put a + between the items.
--
--
-- Note that all language parameters are allowed to be etymology-only languages.
-- Note that all language parameters are allowed to be etymology-only languages.
Line 44: Line 82:
[term_index] = {list = true, allow_holes = true},
[term_index] = {list = true, allow_holes = true},
["sort"] = {},
["sort"] = {},
["nocap"] = {type = "boolean"}, -- always allow this even if not used, for use with {{surf}}, which adds it
["nocap"] = boolean_param, -- always allow this even if not used, for use with {{surf}}, which adds it
}
}


Line 58: Line 96:


     local m_param_utils = require(parameter_utilities_module)
     local m_param_utils = require(parameter_utilities_module)
local param_mods = m_param_utils.construct_param_mods {
local param_mod_source = {}
-- We want to require an index for all params (or use separate_no_index, which also requires an index for the
if not data.dont_require_index then
-- param corresponding to the first item).
insert(param_mod_source,
{default = true, require_index = true},
-- We want to require an index for all params (or use separate_no_index, which also requires an index for the
{group = {"link", "ref", "lang", "q", "l"}},
-- param corresponding to the first item).
-- Override these two to have separate_no_index, unless `data.require_index_for_pos` is specified.
{default = true, require_index = true}
{param = "lit", separate_no_index = true},
)
{param = "pos", separate_no_index = not data.require_index_for_pos, require_index = data.require_index_for_pos},
end
}
insert(param_mod_source, {group = {"link", "ref", "lang", "q", "l", "infl"}})
-- Override lit= to be separate from lit1=.
insert(param_mod_source, {param = "lit", separate_no_index = true})
if not data.dont_require_index and not data.require_index_for_pos then
-- Override pos= to be separate from pos1=.
insert(param_mod_source, {param = "pos", separate_no_index = true})
end
if data.allow_type then
insert(param_mod_source, {param = "type", separate_no_index = true})
end
 
local param_mods = m_param_utils.construct_param_mods(param_mod_source)
if data.extra_params then
if data.extra_params then
data.extra_params(params)
data.extra_params(params)
end
end


local items, args = m_param_utils.process_list_arguments {
local items, args = m_param_utils.parse_list_with_inline_modifiers_and_separate_params {
params = params,
params = params,
param_mods = param_mods,
param_mods = param_mods,
Line 78: Line 127:
parse_lang_prefix = true,
parse_lang_prefix = true,
track_module = "homophones",
track_module = "homophones",
disallow_custom_separators = true,
-- the inclusion of &lrm; is what [[Module:affix]] has always done
default_separator = data.allow_semicolon_separator and " +&lrm; " or nil,
special_separators = data.allow_semicolon_separator and {[";"] = " or "} or nil,
disallow_custom_separators = not data.allow_semicolon_separator,
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- For compatibility, we need to not skip completely unspecified items. It is common, for example, to do
-- {{suffix|lang||foo}} to generate "+ -foo".
-- {{suffix|lang||foo}} to generate "+ -foo".
dont_skip_items = true,
dont_skip_items = true,
-- Allow e.g. <infix> to be specified in place of <type:infix>.
pre_normalize_modifiers = pre_normalize_affix_type,
-- Don't pass in `lang` or `sc`, as they will be used as defaults to initialize the items, which we don't want
-- Don't pass in `lang` or `sc`, as they will be used as defaults to initialize the items, which we don't want
-- (particularly for `lang`), as the code in [[Module:affix]] uses the presence of `lang` as an indicator that
-- (particularly for `lang`), as the code in [[Module:affix]] uses the presence of `lang` as an indicator that
Line 107: Line 161:
if not saw_item_property then
if not saw_item_property then
items[i] = nil
items[i] = nil
elseif item.type then
-- Validate and canonicalize affix types.
if not recognized_affix_types[item.type] then
local valid_types = {}
for k in pairs(recognized_affix_types) do
insert(valid_types, ("'%s'"):format(k))
end
table.sort(recognized_affix_types)
error(("Unrecognized affix type '%s' in item %s; valid values are %s"):format(
item.type, item.itemno, table.concat(valid_types, ", ")))
else
item.type = recognized_affix_types[item.type]
end
end
end
end
if args.type and args.type.default and not m_affix.etymology_types[args.type.default] then
error("Unrecognized etymology type: '" .. args.type.default .. "'")
end
end


Line 120: Line 191:
data.lit = args.lit and args.lit.default
data.lit = args.lit and args.lit.default
data.sort_key = args.sort
data.sort_key = args.sort
data.type = args.type
data.type = args.type and args.type.default
data.nocap = args.nocap
data.nocap = args.nocap
data.notext = args.notext
data.notext = args.notext
Line 129: Line 200:
data.q = args.q.default
data.q = args.q.default
data.qq = args.qq.default
data.qq = args.qq.default
data.infl = args.infl.default
return data
return data
end
end
Line 135: Line 207:
function export.affix(frame)
function export.affix(frame)
local function extra_params(params)
local function extra_params(params)
params.type = {}
params.notext = boolean_param
params.notext = {type = "boolean"}
params.nocat = boolean_param
params.nocat = {type = "boolean"}
params.force_cat = boolean_param
params.force_cat = {type = "boolean"}
end
end


Line 144: Line 215:
raw_args = frame:getParent().args,
raw_args = frame:getParent().args,
extra_params = extra_params,
extra_params = extra_params,
allow_type = true,
allow_semicolon_separator = true,
}
}
if args.type and not m_affix.etymology_types[args.type] then
error("Unrecognized etymology type: '" .. args.type .. "'")
end


-- There must be at least one part to display. If there are gaps, a term
-- There must be at least one part to display. If there are gaps, a term
-- request will be shown.
-- request will be shown.
if not next(parts) and not args.type then
if not next(parts) and not args.type.default then
if mw.title.getCurrentTitle().nsText == "Template" then
if mw.title.getCurrentTitle().nsText == "Template" then
parts = { {term = "prefix-"}, {term = "base"}, {term = "-suffix"} }
parts = { {term = "prefix-"}, {term = "base"}, {term = "-suffix"} }
Line 165: Line 234:
function export.compound(frame)
function export.compound(frame)
local function extra_params(params)
local function extra_params(params)
params.type = {}
params.notext = boolean_param
params.notext = {type = "boolean"}
params.nocat = boolean_param
params.nocat = {type = "boolean"}
params.force_cat = boolean_param
params.force_cat = {type = "boolean"}
end
end


Line 174: Line 242:
raw_args = frame:getParent().args,
raw_args = frame:getParent().args,
extra_params = extra_params,
extra_params = extra_params,
allow_type = true,
allow_semicolon_separator = true,
}
}
if args.type and not m_affix.etymology_types[args.type] then
error("Unrecognized etymology type: '" .. args.type .. "'")
end


-- There must be at least one part to display. If there are gaps, a term
-- There must be at least one part to display. If there are gaps, a term
-- request will be shown.
-- request will be shown.
if not next(parts) and not args.type then
if not next(parts) and not args.type.default then
if mw.title.getCurrentTitle().nsText == "Template" then
if mw.title.getCurrentTitle().nsText == "Template" then
parts = { {term = "first"}, {term = "second"} }
parts = { {term = "first"}, {separator = " +&lrm; ", term = "second"} }
else
else
error("You must provide at least one part of a compound.")
error("You must provide at least one part of a compound.")
Line 193: Line 259:
end
end


-- FIXME: Temporary for check in compound_like() below for old-style {{contraction}} parameters. Remove eventually.
local function ine(arg)
if arg == "" then
return nil
else
return arg
end
end


function export.compound_like(frame)
function export.compound_like(frame)
Line 201: Line 276:
["oftext"] = {},
["oftext"] = {},
["cat"] = {},
["cat"] = {},
["noaffixcat"] = boolean_param,
["dont_require_index"] = boolean_param,
}
}


local iargs = require("Module:parameters").process(frame.args, iparams)
local iargs = require("Module:parameters").process(frame.args, iparams)
local parent_args = frame:getParent().args
-- Error to catch most uses of old-style parameters for {{contraction}}. (FIXME: Remove eventually.)
local term_param = iargs.lang and 1 or 2
if ine(parent_args[term_param + 2]) and not ine(parent_args[term_param + 1]) and not ine(parent_args.tr2) and not ine(parent_args.ts2)
and not ine(parent_args.t2) and not ine(parent_args.gloss2) and not ine(parent_args.g2)
and not ine(parent_args.alt2) then
error(("You specified a term in %s= and not one in %s=. You probably meant to use t= to specify a gloss instead. "
.. "If you intended to specify two terms, put the second term in %s=."):format(term_param + 2, term_param + 1,
term_param + 1))
end
if not ine(parent_args[term_param + 1]) and not ine(parent_args.alt2) and not ine(parent_args.tr2) and not ine(parent_args.ts2)
and ine(parent_args.g2) then
error(("You specified a gender in g2= but no term in %s=. You were probably trying to specify two genders for "
.. "a single term. To do that, put both genders in g=, comma-separated."):format(term_param + 1))
end


local function extra_params(params)
local function extra_params(params)
params.notext = {type = "boolean"}
params.notext = boolean_param
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


local args, parts, lang, sc = parse_args {
local args, parts, lang, sc = parse_args {
raw_args = frame:getParent().args,
raw_args = parent_args,
extra_params = extra_params,
extra_params = extra_params,
ilang = iargs.lang,
ilang = iargs.lang,
dont_require_index = iargs.dont_require_index,
-- FIXME, why are we doing this? Formerly we had 'params.pos = nil' whose intention was to disable the overall
-- FIXME, why are we doing this? Formerly we had 'params.pos = nil' whose intention was to disable the overall
-- pos= while preserving posN=, which is equivalent to the following using the new syntax. But why is this
-- pos= while preserving posN=, which is equivalent to the following using the new syntax. But why is this
-- necessary?
-- necessary?
require_index_for_pos = true,
require_index_for_pos = not iargs.dont_require_index,
allow_semicolon_separator = true,
}
}


Line 227: Line 322:
local oftext = not notext and (iargs.oftext or text and "of")
local oftext = not notext and (iargs.oftext or text and "of")
local cat = not nocat and iargs.cat
local cat = not nocat and iargs.cat
local noaffixcat = nocat or iargs.noaffixcat


if not next(parts) then
if not next(parts) then
if mw.title.getCurrentTitle().nsText == "Template" then
if mw.title.getCurrentTitle().nsText == "Template" then
parts = { {term = "first"}, {term = "second"} }
parts = { {term = "first"}, {separator = " +&lrm; ", term = "second"} }
end
end
end
end


return m_affix.show_compound_like(augment_affix_data({ parts = parts, text = text, oftext = oftext, cat = cat },
return m_affix.show_compound_like(augment_affix_data({ parts = parts, text = text, oftext = oftext, cat = cat, noaffixcat = noaffixcat },
args, lang, sc))
args, lang, sc))
end
end
Line 282: Line 378:


local function extra_params(params)
local function extra_params(params)
params.type = {}
params.notext = boolean_param
params.notext = {type = "boolean"}
params.nocat = boolean_param
params.nocat = {type = "boolean"}
params.force_cat = boolean_param
params.force_cat = {type = "boolean"}
end
end


Line 291: Line 386:
raw_args = parent_args,
raw_args = parent_args,
extra_params = extra_params,
extra_params = extra_params,
allow_type = true,
allow_semicolon_separator = true,
}
}
if args.type and not m_affix.etymology_types[args.type] then
error("Unrecognized etymology type: '" .. args.type .. "'")
end


-- There must be at least one part to display. If there are gaps, a term
-- There must be at least one part to display. If there are gaps, a term
Line 301: Line 394:
if not next(parts) then
if not next(parts) then
if mw.title.getCurrentTitle().nsText == "Template" then
if mw.title.getCurrentTitle().nsText == "Template" then
parts = { {term = "first"}, {term = "second"} }
parts = { {term = "first"}, {separator = " +&lrm; ", term = "second"} }
else
else
error("You must provide at least one part.")
error("You must provide at least one part.")
Line 332: Line 425:
function export.circumfix(frame)
function export.circumfix(frame)
local function extra_params(params)
local function extra_params(params)
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 363: Line 456:
function export.confix(frame)
function export.confix(frame)
local function extra_params(params)
local function extra_params(params)
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 393: Line 486:
function export.pseudo_loan(frame)
function export.pseudo_loan(frame)
local function extra_params(params)
local function extra_params(params)
params.notext = {type = "boolean"}
params.notext = boolean_param
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 406: Line 499:
-- necessary?
-- necessary?
require_index_for_pos = true,
require_index_for_pos = true,
allow_semicolon_separator = true,
}
}


Line 415: Line 509:
function export.infix(frame)
function export.infix(frame)
local function extra_params(params)
local function extra_params(params)
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 444: Line 538:
function export.prefix(frame)
function export.prefix(frame)
local function extra_params(params)
local function extra_params(params)
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 480: Line 574:
function export.suffix(frame)
function export.suffix(frame)
local function extra_params(params)
local function extra_params(params)
params.nocat = {type = "boolean"}
params.nocat = boolean_param
params.force_cat = {type = "boolean"}
params.force_cat = boolean_param
end
end


Line 523: Line 617:
}
}
local derivtype = iargs.derivtype
local derivtype = iargs.derivtype
if derivtype == "PIE root" then
params[1] = {required = "true", type = "language", default = "und"}
params[1] = {}
params[2] = {}
else
params[1] = {required = "true", type = "language", default = "und"}
params[2] = {}
end


local args = require("Module:parameters").process(frame:getParent().args, params)
local args = require("Module:parameters").process(frame:getParent().args, params)


local lang
local lang = args[1]
local term
local term = args[2] or args.head
 
if derivtype == "PIE root" then
lang = m_languages.getByCode("ine-pro", true)
term = args[1] or args.head
 
if term then
term = "*" .. term .. "-"
end
else
lang = args[1]
term = args[2] or args.head
end
 
local id = args.id
local id = args.id
local sc = args.sc
local sc = args.sc
local pos = require("Module:string utilities").pluralize(args.pos or "word")
local pos = require(en_utilities_module).pluralize(args.pos or "term")


if not term then
if not term then
local SUBPAGE = mw.title.getCurrentTitle().subpageText
local SUBPAGE = mw.loadData("Module:headword/data").pagename
if lang:hasType("reconstructed") or mw.title.getCurrentTitle().nsText == "Reconstruction" then
if lang:hasType("reconstructed") or mw.title.getCurrentTitle().nsText == "Reconstruction" then
term = "*" .. SUBPAGE
term = "*" .. SUBPAGE
Line 560: Line 637:
term = SUBPAGE
term = SUBPAGE
end
end
end
if derivtype == "PIE root" then
require('Module:debug/track')('collapsible category tree/PIE root')
return require('Module:collapsible category tree').make{
category = "Terms derived from the Proto-Indo-European root " .. term .. (id and " (" .. id .. ")" or ""),
}
end
end


Line 577: Line 647:
elseif derivtype == "compound" then
elseif derivtype == "compound" then
category = langname .. " compound " .. pos .. " with " .. term
category = langname .. " compound " .. pos .. " with " .. term
elseif derivtype == "classifier" then
category = langname .. " " .. pos .. " classified by " .. term
else
else
category = langname .. " " .. pos .. " " .. derivtype .. "ed with " .. term .. (id and " (" .. id .. ")" or "")
category = langname .. " " .. pos .. " " .. derivtype .. "ed with " .. term .. (id and " (" .. id .. ")" or "")