Module:object usage
Jump to navigation
Jump to search
- The following documentation is located at Module:object usage/documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
This module implements templates showing case and adposition usage for verb objects and similar constructs. It generates content for {{+aux}}
and {{+obj}}
.
local export = {}
local dump = mw.dumpObject
local m_links = require("Module:links")
local form_of_module = "Module:form of"
local labels_module = "Module:labels"
local parse_utilities_module = "Module:parse utilities"
local pron_qualifier_module = "Module:pron qualifier"
local references_module = "Module:references"
local rsubn = mw.ustring.gsub
local rfind = mw.ustring.find
-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
local retval = rsubn(term, foo, bar)
return retval
end
-- version of rsubn() that returns a 2nd argument boolean indicating whether
-- a substitution was made.
local function rsubb(term, foo, bar)
local retval, nsubs = rsubn(term, foo, bar)
return retval, nsubs > 0
end
function export.show_obj(frame)
local pargs = frame:getParent().args
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = {list = true},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local lang = args[1]
local m_parse_utilities = require(parse_utilities_module)
local qualifier_label_mod = {"q", "qq", "l", "ll"}
local qualifier_label_mod_with_starred_set = {}
for _, mod in ipairs(qualifier_label_mod) do
qualifier_label_mod_with_starred_set[mod] = true
qualifier_label_mod_with_starred_set[mod .. "*"] = true
end
local function parse_object(object, paramno)
local function parse_one_form(run)
local function parse_err(msg)
error(msg .. ": '" .. table.concat(run) .. "'")
end
if #run == 1 and run[1] == "" then
error("Blank form not allowed")
end
local retval = {}
retval.form = run[1]
retval.form, retval.is_postposition = rsubb(retval.form, "^::", "")
if retval.is_postposition then
retval.is_term = true
else
retval.form, retval.is_term = rsubb(retval.form, "^:", "")
end
for i = 2, #run - 1, 2 do
if run[i + 1] ~= "" then
parse_err("Extraneous text '" .. run[i + 1] .. "' after modifier")
end
if run[i]:find("^%(") then
if not retval.is_term then
parse_err("Can't attach case '" .. run[i] .. "' to non-term")
end
local raw_case = run[i]:gsub("^%((.*)%)$", "%1")
if raw_case:find("[+/&<>()%[%]]") then
retval.case = parse_object(raw_case, ("%s:%s(...)"):format(retval.form, paramno))
else
retval.case = raw_case
end
else
local modtext = run[i]:match("^<(.*)>$")
if not modtext then
parse_err("Internal error: Modifier '" .. modtext .. "' isn't surrounded by angle brackets")
end
local prefix, arg = modtext:match("^([a-z]+%*?):(.*)$")
if prefix then
if qualifier_label_mod_with_starred_set[prefix] or prefix == "t" or prefix == "id" or
prefix == "tr" or prefix == "ts" or prefix == "alt" or prefix == "ref" then
if not retval.is_term and not qualifier_label_mod_with_starred_set[prefix] and
prefix ~= "ref" and prefix ~= "t" then
parse_err("Can't attach prefix '" .. prefix .. "' to non-term")
end
local item_dest = prefix == "ref" and "refs" or prefix
if retval[item_dest] then
parse_err("Can't set two values for prefix '" .. prefix .. "'")
end
if prefix == "l" or prefix == "ll" or prefix == "l*" or prefix == "ll*" then
arg = require(labels_module).split_labels_on_comma(arg)
elseif prefix == "ref" then
arg = require(references_module).parse_references(arg, parse_err)
end
retval[item_dest] = arg
else
parse_err("Unrecognized prefix '" .. prefix .. "' in modifier " .. run[i])
end
else
retval.t = modtext
end
end
end
return retval
end
local parsed_object = {arguments = {}}
local orig_segments =
m_parse_utilities.parse_multi_delimiter_balanced_segment_run(object, {{"[", "]"}, {"(", ")"}, {"<", ">"}})
-- rejoin bracketed segments with nearby ones; we only parse them to ensure that we leave alone parens and
-- angle brackets inside of square brackets.
local joined_segments = {}
local i = 1
while i <= #orig_segments do
local segment = orig_segments[i]
if i % 2 == 0 and segment:find("^%[") then
joined_segments[#joined_segments] = joined_segments[#joined_segments] .. segment .. orig_segments[i + 1]
i = i + 2
else
table.insert(joined_segments, segment)
i = i + 1
end
end
local split_runs =
m_parse_utilities.split_alternating_runs(joined_segments, "%s*[+/&]%s*", "preserve splitchar")
-- Now parse the forms.
i = 1
while i <= #split_runs do
if i == 1 and #split_runs[1] == 1 and split_runs[1][1] == "" and #split_runs > 1 and
rfind(split_runs[2][1], "^%s*&%s*$") then
-- Blank argument at beginning followed by & to suppress the +. Ignore it.
else
local form = parse_one_form(split_runs[i])
local prev_joiner = i > 1 and rsub(split_runs[i - 1][1], "^%s*(.-)%s*$", "%1")
if prev_joiner == "/" then
local this_alternants = parsed_object.arguments[#parsed_object.arguments].alternants
-- Join to the previous alternant.
table.insert(this_alternants, form)
if not form.is_term and form.form == "etc." then
for j = 2, #this_alternants do
this_alternants[j].separator = ", "
end
end
else
local suppress_with = prev_joiner == "&"
-- Create a new argument.
table.insert(parsed_object.arguments, {alternants = {form}, suppress_with = suppress_with})
end
end
i = i + 2
end
-- Now move qualifiers up as necessary.
local function parse_err(msg)
error(("%s: %s=%s"):format(msg, paramno, object))
end
for _, argument in ipairs(parsed_object.arguments) do
for i, alternant in ipairs(argument.alternants) do
if #argument.alternants == 1 then
-- If there's only one alternant, convert regular qualifiers to starred versions if there's not
-- already a starred version.
for _, mod in ipairs(qualifier_label_mod) do
if alternant[mod] and not alternant[mod .. "*"] then
alternant[mod .. "*"] = alternant[mod]
alternant[mod] = nil
end
end
end
if i < #argument.alternants then
-- Starred versions cannot be attached to non-final alternants.
for _, mod in ipairs(qualifier_label_mod) do
if alternant[mod .. "*"] then
parse_err(("Starred version '%s' of label or qualifier must be attached to last alternant"):
format(mod .. "*"))
end
end
else
-- Starred versions attached to final alternants should be moved up to argument level.
for _, mod in ipairs(qualifier_label_mod) do
if alternant[mod .. "*"] then
argument[mod] = alternant[mod .. "*"]
alternant[mod .. "*"] = nil
end
end
end
end
end
return parsed_object
end
local parsed_objects = {}
for argno, object in ipairs(args[2]) do
if object == ";" then
-- bare semicolon separator, to create a higher-level separation between parameters than
-- the normal "; or ..." separator.
if not parsed_objects[1] then
error("Can't have bare semicolon separator parameter as first parameter")
end
else
-- argno + 1 because object arguments begin at 2=
local parsed_object = parse_object(object, argno + 1)
if argno > 1 and args[2][argno - 1] == ";" then
parsed_object.separator = ";"
end
table.insert(parsed_objects, parsed_object)
end
end
local function format_parsed_object(parsed_object, recursive_suppress_with)
local argument_parts = {}
local multiple_alternants = false
for _, argument in ipairs(parsed_object.arguments) do
if #argument.alternants > 1 then
multiple_alternants = true
break
end
end
local used_with_in_prefix = false
for i, argument in ipairs(parsed_object.arguments) do
local alternant_parts = {}
local prefix, separator
local suppress_with = argument.suppress_with or i == 1 and recursive_suppress_with
if not suppress_with then
if not used_with_in_prefix then
separator = i > 1 and " " or ""
prefix = "''with'' "
used_with_in_prefix = true
elseif multiple_alternants then
separator = ", "
prefix = "''along with'' "
else
separator = " "
prefix = "''and'' "
end
else
separator = i > 1 and " " or ""
prefix = ""
end
-- If there are multiple alternants and a non-final alternant has a gloss, assume that each alternant has
-- its own gloss, or at least that the gloss on the final alternant doesn't apply to all alternants.
-- Otherwise, we assume the gloss on the final alternant applies to all alternants. This affects the
-- placement of right labels and qualifiers vis-à-vis the gloss: if there's a single gloss applying to
-- multiple alternants, we put the right labels and qualifiers before gloss, otherwise after.
local gloss_with_non_final_alternant = false
for j, alternant in ipairs(argument.alternants) do
if j < #argument.alternants and alternant.t then
gloss_with_non_final_alternant = true
break
end
end
-- Process each alternant.
for j, alternant in ipairs(argument.alternants) do
-- Construct the "case text" for the alternant (what goes in parens). We always assume that a given case
-- text goes only with its associated alternant, unlike for the gloss (see above).
local case_text
if alternant.case then
if type(alternant.case) == "string" then
case_text = require(form_of_module).tagged_inflections {
lang = lang, tags = {alternant.case}, text_classes = text_classes
}
else
case_text = format_parsed_object(alternant.case, "suppress with")
end
if alternant.is_postposition then
case_text = "(" .. case_text .. " +)"
else
case_text = "+ " .. case_text
end
end
-- Construct the argument itself (inflection tag or literal word), and add any case text.
local form
--local text_classes = "object-usage-form-of-tag"
local text_classes = "object-usage-tag"
if alternant.is_term then
local term = alternant.form
if term == "" then
term = nil
end
form = m_links.full_link({lang = lang, term = term, alt = alternant.alt, id = alternant.id,
tr = alternant.tr, ts = alternant.ts, pos = not alternant.is_postposition and case_text or nil},
"bold")
if alternant.is_postposition and case_text then
form = case_text .. " " .. form
end
else
form = require(form_of_module).tagged_inflections {
lang = lang, tags = {alternant.form}, text_classes = text_classes
}
if case_text then
if alternant.is_postposition then
form = case_text .. " " .. form
else
form = form .. " (" .. case_text .. ")"
end
end
end
local part = form
local function add_qualifiers_and_labels_to_alternant(refs)
if alternant.q or alternant.qq or alternant.l or alternant.ll or refs then
part = require(pron_qualifier_module).format_qualifiers {
text = part,
lang = lang,
q = alternant.q and {alternant.q} or nil,
qq = alternant.qq and {alternant.qq} or nil,
l = alternant.l,
ll = alternant.ll,
refs = refs,
}
end
end
local meaning_text = ""
if alternant.t then
meaning_text = " <small>‘" .. alternant.t .. "’</small>"
end
if gloss_with_non_final_alternant or #argument.alternants == 1 then
-- See above. If there is only one alternant, or multiple alternants where each gloss goes with an
-- individual alternant, right labels and qualifiers go after the gloss, otherwise before. The
-- reference always goes directly after the form (before the gloss), so if the right labels and
-- qualifiers go after the gloss, we need to split up their handling.
if alternant.refs then
part = require(pron_qualifier_module).format_qualifiers {
text = part,
lang = lang,
refs = alternant.refs,
}
end
part = part .. meaning_text
add_qualifiers_and_labels_to_alternant()
else
add_qualifiers_and_labels_to_alternant(alternant.refs)
part = part .. meaning_text
end
if j > 1 and not used_with_in_prefix and not recursive_suppress_with then
-- If we used e.g. {{+obj|ca|&transitve/:en}} to suppress the initial ''with'', we want it
-- to appear after the ''or'' so we get ''transitive or with [[en]]'' rather than just
-- ''transitive or [[en]]''.
part = "''with'' " .. part
used_with_in_prefix = true
end
if j > 1 then
table.insert(alternant_parts, alternant.separator or " ''or'' ")
end
table.insert(alternant_parts, part)
end
local part = prefix .. table.concat(alternant_parts)
if argument.q or argument.qq or argument.l or argument.ll then
part = require(pron_qualifier_module).format_qualifiers {
text = part,
lang = lang,
q = argument.q and {argument.q} or nil,
qq = argument.qq and {argument.qq} or nil,
l = argument.l,
ll = argument.ll,
}
end
table.insert(argument_parts, separator .. part)
end
return table.concat(argument_parts)
end
-- Now generate the text.
local object_parts = {}
local function ins(txt)
table.insert(object_parts, txt)
end
ins(require("Module:TemplateStyles")("Module:object usage/style.css"))
ins("[")
for i, parsed_object in ipairs(parsed_objects) do
if i > 1 then
if parsed_object.separator == ";" then
ins("; ''in addition,'' ")
else
ins("; ''or'' ")
end
end
ins(format_parsed_object(parsed_object, false))
end
ins("]")
return table.concat(object_parts)
end
function export.show_aux(frame)
local pargs = frame:getParent().args
local params = {
[1] = {required = true, default = "und"},
[2] = {list = true, allow_holes = true},
["alt"] = {list = true, allow_holes = true},
["q"] = {list = true, allow_holes = true},
["id"] = {list = true, allow_holes = true},
["senseid"] = {list = true, allow_holes = true, alias_of = "id"},
["means"] = {list = true, allow_holes = true},
}
local args = require("Module:parameters").process(frame:getParent().args, params)
local lang = require("Module:languages").getByCode(args[1], 1)
-- Find the maximum index among any of the list parameters.
local maxmaxindex = 0
for k, v in pairs(args) do
if type(v) == "table" and v.maxindex and v.maxindex > maxmaxindex then
maxmaxindex = v.maxindex
end
end
if mw.title.getCurrentTitle().nsText == "Template" and mw.title.getCurrentTitle().text == "+aux" then
return "[auxiliary " .. m_links.full_link({lang = lang, term = "auxiliary"}, "term") .. " = meaning]"
end
local parts = {}
for i = 1, maxmaxindex do
local term = m_links.full_link({lang = lang, term = args[2][i], alt = args.alt[i], id = args.id[i]}, "term")
if args.means[i] then
term = term .. " = " .. args.means[i]
end
if args.q[i] then
term = require("Module:qualifier").format_qualifier(args.q[i]) .. " " .. term
end
table.insert(parts, term)
end
return "[auxiliary " .. require("Module:table").serialCommaJoin(parts, {conj = "or"}) .. "]"
end
return export