Module:bo-noun
Appearance
- This module lacks a documentation subpage. Please create it.
- Useful links: subpage list • links • transclusions • testcases • sandbox
local export = {}
local m_table = require("Module:table")
local m_links = require("Module:links")
local m_str_utils = require("Module:string utilities")
local lang = require("Module:languages").getByCode("bo")
local iut = require("Module:inflection utilities")
local put = require("Module:parse utilities")
local m_para = require("Module:parameters")
local find = m_str_utils.find
local format = m_str_utils.format
local gsub = m_str_utils.gsub
local len = m_str_utils.len
local match = m_str_utils.match
local sub = m_str_utils.sub
local u = m_str_utils.char
local da_drag = u(0xFFF0)
local output_noun_slots = {
absv_sg = "absv|s",
gen_sg = "gen|s",
agc_sg = "agc|s",
dat_sg = "dat|s",
loc_sg = "loc|s",
ter_sg = "ter|s",
abl_sg = "abl|s",
ela_sg = "ela|s",
ass_sg = "ass|s",
comc_sg = "comc|s",
absv_pl = "absv|p",
gen_pl = "gen|p",
agc_pl = "agc|p",
dat_pl = "dat|p",
loc_pl = "loc|p",
ter_pl = "ter|p",
abl_pl = "abl|p",
ela_pl = "ela|p",
ass_pl = "ass|p",
comc_pl = "comc|p",
}
local output_noun_slots_with_linked = m_table.shallowCopy(output_noun_slots)
output_noun_slots_with_linked["absv_sg_linked"] = "absv|s"
output_noun_slots_with_linked["absv_pl_linked"] = "absv|p"
local input_params_to_slots_both = {
[1] = "absv_sg",
[2] = "gen_sg",
[3] = "agc_sg",
[4] = "dat_sg",
[5] = "loc_sg",
[6] = "ter_sg",
[7] = "abl_sg",
[8] = "ela_sg",
[9] = "ass_sg",
[10] = "comc_sg",
[11] = "absv_pl",
[12] = "gen_pl",
[13] = "agc_pl",
[14] = "dat_pl",
[15] = "loc_pl",
[16] = "ter_pl",
[17] = "abl_pl",
[18] = "ela_pl",
[19] = "ass_pl",
[20] = "comc_pl",
}
local input_params_to_slots_sg = {
[1] = "absv_sg",
[2] = "gen_sg",
[3] = "agc_sg",
[4] = "dat_sg",
[5] = "loc_sg",
[6] = "ter_sg",
[7] = "abl_sg",
[8] = "ela_sg",
[9] = "ass_sg",
[10] = "comc_sg",
}
local input_params_to_slots_pl = {
[1] = "absv_pl",
[2] = "gen_pl",
[3] = "agc_pl",
[4] = "dat_pl",
[5] = "loc_pl",
[6] = "ter_pl",
[7] = "abl_pl",
[8] = "ela_pl",
[9] = "ass_pl",
[10] = "comc_pl",
}
local cases = {
absv = true,
gen = true,
agc = true,
dat = true,
loc = true,
ter = true,
abl = true,
ela = true,
ass = true,
comc = true,
}
local function skip_slot(number, slot)
return number == "sg" and find(slot, "_p$") or
number == "pl" and find(slot, "_s$")
end
local function add(data, slot, stem_and_ending, footnotes)
local stem
local ending
if not stem_and_ending then
return
end
if skip_slot(data.number, slot) then
return
end
if type(stem_and_ending) == "string" then
stem = stem_and_ending
ending = ""
else
stem = stem_and_ending[1]
ending = stem_and_ending[2]
end
iut.add_forms(data.forms, slot, stem, ending, function(stem, ending) return stem .. ending end, lang)
end
local function process_slot_overrides(data, do_slot)
for slot, overrides in pairs(data.overrides) do
if skip_slot(data.number, slot) then
error("Override specified for invalid slot '" .. slot .. "' due to '" .. data.number .. "' number restriction")
end
if do_slot(slot) then
data.forms[slot] = nil
local slot_is_plural = find(slot, "_p$")
for _, override in ipairs(overrides) do
for _, value in ipairs(override.values) do
local form = value.form
local combined_notes = iut.combine_footnotes(data.footnotes, value.footnotes)
end
end
end
end
end
local function plural(lemma, translit)
return lemma .. "་ཚོ"
end
local function kyi(lemma, translit)
local matches = {
{match(translit, "[dbs" .. da_drag .. "]$"),
"་ཀྱི"
},
{match(translit, "[nmrl]$"),
"་གྱི"
},
{match(translit, "n?g$"),
"་གི"
},
{match(translit, "[aāiīuūṛṝḷḹeo']$"),
"འི"
},
}
for i, suffix in ipairs(matches) do
if suffix[1] then
return lemma .. suffix[2]
end
end
end
local function genitive(lemma, translit)
return kyi(lemma, translit)
end
local function agentive(lemma, translit)
lemma = kyi(lemma, translit)
if match(lemma, "འི$") then
return gsub(lemma, "འི$", "ས")
else
return lemma .. "ས"
end
end
local function dative(lemma, translit)
return lemma .. "་ལ"
end
local function locative(lemma, translit)
return lemma .. "་ན"
end
local function terminative(lemma, translit)
local matches = {
{{
match(translit, "[^n]g$"),
match(translit, "[b" .. da_drag .. "]$")
},
"་ཏུ"
},
{{
match(translit, "ng$"),
match(translit, "[dnmrl]$")
},
"་དུ"
},
{{match(translit, "s$")},
"་སུ"
},
{{match(translit, "[aāiīuūṛṝḷḹeo']$")},
"ར"
},
}
for i, suffix in ipairs(matches) do
for j, test in pairs(suffix[1]) do
if test then
return lemma .. suffix[2]
end
end
end
end
local function ablative(lemma, translit)
return lemma .. "་ལས"
end
local function elative(lemma, translit)
return lemma .. "་ནས"
end
local function associative(lemma, translit)
return lemma .. "་དང"
end
local function comparative(lemma, translit)
local matches = {
{match(translit, "[^gdnbms" .. da_drag .. "]$"),
"་བས"
},
{match(translit, "[gdnbms" .. da_drag .. "]$"),
"་པས"
},
}
for i, suffix in ipairs(matches) do
if suffix[1] then
return lemma .. suffix[2]
end
end
end
local function handle_derived_slots_and_overrides(data)
translit_sg = lang:transliterate(data.lemma):gsub("%s*$", "")
if data.da_drag then translit_sg = translit_sg .. da_drag end
add(data, "absv_sg", data.lemma)
add(data, "gen_sg", genitive(data.lemma, translit_sg))
add(data, "agc_sg", agentive(data.lemma, translit_sg))
add(data, "dat_sg", dative(data.lemma, translit_sg))
add(data, "loc_sg", locative(data.lemma, translit_sg))
add(data, "ter_sg", terminative(data.lemma, translit_sg))
add(data, "abl_sg", ablative(data.lemma, translit_sg))
add(data, "ela_sg", elative(data.lemma, translit_sg))
add(data, "ass_sg", associative(data.lemma, translit_sg))
add(data, "comc_sg", comparative(data.lemma, translit_sg))
add(data, "absv_pl", plural(data.lemma, translit_sg))
translit_pl = lang:transliterate(data.forms["absv_pl"][1].form):gsub("%s*$", "")
add(data, "gen_pl", genitive(data.forms["absv_pl"][1].form, translit_pl))
add(data, "agc_pl", agentive(data.forms["absv_pl"][1].form, translit_pl))
add(data, "dat_pl", dative(data.forms["absv_pl"][1].form, translit_pl))
add(data, "loc_pl", locative(data.forms["absv_pl"][1].form, translit_pl))
add(data, "ter_pl", terminative(data.forms["absv_pl"][1].form, translit_pl))
add(data, "abl_pl", ablative(data.forms["absv_pl"][1].form, translit_pl))
add(data, "ela_pl", elative(data.forms["absv_pl"][1].form, translit_pl))
add(data, "comc_pl", comparative(data.forms["absv_pl"][1].form, translit_pl))
-- Compute linked versions of potential lemma slots, for use in {{bo-noun}}.
-- We substitute the original lemma (before removing links) for forms that
-- are the same as the lemma, if the original lemma has links.
for _, slot in ipairs({ "absv_sg", "absv_pl" }) do
iut.insert_forms(data.forms, slot .. "_linked", iut.map_forms(data.forms[slot], function(form)
if form == data.orig_lemma_no_links and find(data.orig_lemma, "%[%[") then
return data.orig_lemma
else
return form
end
end))
end
end
local function fetch_footnotes(separated_group)
local footnotes
for j = 2, #separated_group - 1, 2 do
if separated_group[j + 1] ~= "" then
error("Extraneous text after bracketed footnotes: '" .. table.concat(separated_group) .. "'")
end
if not footnotes then
footnotes = {}
end
table.insert(footnotes, separated_group[j])
end
return footnotes
end
local function parse_override(segments, case)
local retval = { values = {} }
local part = segments[1]
if cases[case] then
-- ok
elseif accented_cases[case] then
case = accented_cases[case]
retval.stemstressed = true
else
error("Internal error: unrecognized case in override: '" .. table.concat(segments) .. "'")
end
local rest = sub(part, len(case)+1, len(case)+3)
local slot
if find(rest, "^pl") then
rest = gsub(rest, "^pl", "")
slot = case .. "_pl"
else
slot = case .. "_sl"
end
if find(rest, "^:") then
retval.full = true
rest = gsub(rest, "^:", "")
end
segments[1] = rest
local colon_separated_groups = put.split_alternating_runs(segments, ":")
for i, colon_separated_group in ipairs(colon_separated_groups) do
local value = {}
local form = colon_separated_group[1]
if form == "" then
error("Use - to indicate an empty ending for slot '" .. slot .. "': '" .. table.concat(segments .. "'"))
elseif form == "-" then
value.form = ""
else
value.form = form
end
value.footnotes = fetch_footnotes(colon_separated_group)
table.insert(retval.values, value)
end
return slot, retval
end
local function parse_indicator_spec(angle_bracket_spec)
local inside = match(angle_bracket_spec, "^<(.*)>$")
local data = { overrides = {}, forms = {} }
if inside ~= "" then
local segments = put.parse_balanced_segment_run(inside, "[", "]")
local dot_separated_groups = put.split_alternating_runs(segments, "%.")
for i, dot_separated_group in ipairs(dot_separated_groups) do
local part = dot_separated_group[1]
local case_prefix
for case,_ in pairs(cases) do
if match(part, "^" .. case .. "[:$]") then
case_prefix = match(part, "^" .. case)
local slot, override = parse_override(dot_separated_group, case_prefix)
if data.overrides[slot] then
table.insert(data.overrides[slot], override)
else
data.overrides[slot] = { override }
end
end
end
if case_prefix ~= nil then
elseif part == "" then
if #dot_separated_group == 1 then
error("Blank indicator: '" .. inside .. "'")
end
data.footnotes = fetch_footnotes(dot_separated_group)
elseif part == "both" or part == "pl" then
if data.number then
error("Can't specify number twice: '" .. inside .. "'")
end
data.number = part
elseif part == "da_drag" then
if data.da_drag then
error( "Can't specify da drag twice: '" .. inside .. "'" )
end
data.da_drag = true
else
error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'")
end
end
end
return data
end
local function set_defaults_and_check_bad_indicators(data)
-- Set default values.
if not data.adj then
if data.proper then
data.number = data.number or "sg"
else
data.number = data.number or "sg"
end
end
end
local function detect_all_indicator_specs(alternant_multiword_spec)
local is_multiword = #alternant_multiword_spec.alternant_or_word_specs > 1
iut.map_word_specs(alternant_multiword_spec, function(data)
set_defaults_and_check_bad_indicators(data)
data.multiword = is_multiword
end)
end
local propagate_multiword_properties
local function propagate_alternant_properties(alternant_spec, property, mixed_value, nouns_only)
local seen_property
for _, multiword_spec in ipairs(alternant_spec.alternants) do
propagate_multiword_properties(multiword_spec, property, mixed_value, nouns_only)
if seen_property == nil then
seen_property = multiword_spec[property]
elseif multiword_spec[property] and seen_property ~= multiword_spec[property] then
seen_property = mixed_value
end
end
alternant_spec[property] = seen_property
end
propagate_multiword_properties = function(multiword_spec, property, mixed_value, nouns_only)
local seen_property = nil
local last_seen_nounal_pos = 0
local word_specs = multiword_spec.alternant_or_word_specs or multiword_spec.word_specs
for i = 1, #word_specs do
local is_nounal
if word_specs[i].alternants then
propagate_alternant_properties(word_specs[i], property, mixed_value)
is_nounal = not not word_specs[i][property]
elseif nouns_only then
is_nounal = not word_specs[i].adj
else
is_nounal = not not word_specs[i][property]
end
if is_nounal then
if not word_specs[i][property] then
error("Internal error: noun-type word spec without " .. property .. " set")
end
for j = last_seen_nounal_pos + 1, i - 1 do
word_specs[j][property] = word_specs[j][property] or word_specs[i][property]
end
last_seen_nounal_pos = i
if seen_property == nil then
seen_property = word_specs[i][property]
elseif seen_property ~= word_specs[i][property] then
seen_property = mixed_value
end
end
end
if last_seen_nounal_pos > 0 then
for i = last_seen_nounal_pos + 1, #word_specs do
word_specs[i][property] = word_specs[i][property] or word_specs[last_seen_nounal_pos][property]
end
end
multiword_spec[property] = seen_property
end
local function propagate_properties_downward(alternant_multiword_spec, property, default_propval)
local propval1 = alternant_multiword_spec[property] or default_propval
for _, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do
local propval2 = alternant_or_word_spec[property] or propval1
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
local propval3 = multiword_spec[property] or propval2
for _, word_spec in ipairs(multiword_spec.word_specs) do
local propval4 = word_spec[property] or propval3
if propval4 == "mixed" then
error("Attempt to assign mixed " .. property .. " to word")
end
word_spec[property] = propval4
end
end
else
if propval2 == "mixed" then
error("Attempt to assign mixed " .. property .. " to word")
end
alternant_or_word_spec[property] = propval2
end
end
end
local function propagate_properties(alternant_multiword_spec, property, default_propval, mixed_value)
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, "nouns only")
propagate_multiword_properties(alternant_multiword_spec, property, mixed_value, false)
propagate_properties_downward(alternant_multiword_spec, property, default_propval)
end
local function normalize_all_lemmas(alternant_multiword_spec)
iut.map_word_specs(alternant_multiword_spec, function(data)
data.orig_lemma = data.lemma
data.orig_lemma_no_links = m_links.remove_links(data.lemma)
data.lemma = data.orig_lemma_no_links
end)
end
local function compute_categories_and_annotation(alternant_multiword_spec)
local cats = {}
local function insert(cattype)
m_table.insertIfNot(cats, "Tibetan " .. cattype)
end
if alternant_multiword_spec.pos == "noun" then
if alternant_multiword_spec.number == "sg" then
insert("uncountable nouns")
elseif alternant_multiword_spec.number == "pl" then
insert("pluralia tantum")
end
end
local annotation
if alternant_multiword_spec.manual then
alternant_multiword_spec.annotation =
alternant_multiword_spec.number == "sg" and "sg-only" or
alternant_multiword_spec.number == "pl" and "pl-only" or
""
else
local annparts = {}
local bor = nil
local decl = {}
local irregs = {}
local stems = {}
local reducible = nil
local vh = {}
local function do_word_spec(data)
if data.da_drag == true then
table.insert(annparts, "da drag stem")
else
table.insert(annparts, "regular stem")
end
end
local key_entry = alternant_multiword_spec.first_noun or 1
if #alternant_multiword_spec.alternant_or_word_specs >= key_entry then
local alternant_or_word_spec = alternant_multiword_spec.alternant_or_word_specs[key_entry]
if alternant_or_word_spec.alternants then
for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do
key_entry = multiword_spec.first_noun or 1
if #multiword_spec.word_specs >= key_entry then
do_word_spec(multiword_spec.word_specs[key_entry])
end
end
else
do_word_spec(alternant_or_word_spec)
end
end
if alternant_multiword_spec.number == "both" then
table.insert(annparts, "with def pl")
elseif alternant_multiword_spec.number == "pl" then
table.insert(annparts, "pl-only")
end
if #irregs > 0 then
table.insert(annparts, table.concat(irregs, " // "))
end
alternant_multiword_spec.annotation = table.concat(annparts, " ")
if #stems > 1 then
insert("nouns with multiple stems")
end
end
alternant_multiword_spec.categories = cats
end
local function combine_stem_ending(stem, ending)
return stem .. ending
end
local function show_forms(alternant_multiword_spec)
local lemmas = {}
if alternant_multiword_spec.forms.absv_sg then
for _, absv_sg in ipairs(alternant_multiword_spec.forms.absv_sg) do
table.insert(lemmas, absv_sg.form)
end
elseif alternant_multiword_spec.forms.absv_pl then
for _, absv_pl in ipairs(alternant_multiword_spec.forms.absv_pl) do
table.insert(lemmas, absv_pl.form)
end
end
local props = {
lemmas = lemmas,
slot_table = output_noun_slots_with_linked,
lang = lang,
canonicalize = function(form) return form end,
include_translit = true,
footnotes = alternant_multiword_spec.footnotes,
allow_footnote_symbols = not not alternant_multiword_spec.footnotes,
}
iut.show_forms(alternant_multiword_spec.forms, props)
end
local function make_table(alternant_multiword_spec)
local forms = alternant_multiword_spec.forms
local function header(min_width)
min_width = min_width or "70"
return gsub([===[
<div class="NavFrame" style="display:inline-block;min-width:MINWIDTHem">
<div class="NavHead" style="background:#eff7ff">{title}{annotation} </div>
<div class="NavContent">
{\op}| style="background:#f9f9f9;text-align:center;min-width:MINWIDTHem;width:100%" class="inflection-table"
|-
]===], "MINWIDTH", min_width)
end
local function template_footer()
return [===[|-
|{\cl}{notes_clause}</div></div>]===]
end
local table_spec_both = header("45") .. [===[
! style="background:#d9ebff;width:10em" |
! style="background:#d9ebff;width:17.5em" | singular / indefinite
! style="background:#d9ebff;width:17.5em" | definite plural
|-
! style="background:#eff7ff" | absolutive
| {absv_sg}
| {absv_pl}
|-
! style="background:#eff7ff" | genitive
| {gen_sg}
| {gen_pl}
|-
! style="background:#eff7ff" | agentive
| {agc_sg}
| {agc_pl}
|-
! style="background:#eff7ff" | dative
| {dat_sg}
| {dat_pl}
|-
! style="background:#eff7ff" | locative
| {loc_sg}
| {loc_pl}
|-
! style="background:#eff7ff" | terminative
| {ter_sg}
| {ter_pl}
|-
! style="background:#eff7ff" | ablative
| {abl_sg}
| {abl_pl}
|-
! style="background:#eff7ff" | elative
| {ela_sg}
| {ela_pl}
|-
! style="background:#eff7ff" | associative
| {ass_sg}
| {ass_pl}
|-
! style="background:#eff7ff" | comparative
| {comc_sg}
| {comc_pl}
]===] .. template_footer()
local function table_spec_one(num, number)
return gsub(gsub(header("30") .. [===[
! style="background:#eff7ff" | absolutive
| {absv_NUM}
|-
! style="background:#eff7ff" | genitive
| {gen_NUM}
|-
! style="background:#eff7ff" | agentive
| {agc_NUM}
|-
! style="background:#eff7ff" | dative
| {dat_NUM}
|-
! style="background:#eff7ff" | locative
| {loc_NUM}
|-
! style="background:#eff7ff" | terminative
| {ter_NUM}
|-
! style="background:#eff7ff" | ablative
| {abl_NUM}
|-
! style="background:#eff7ff" | elative
| {ela_NUM}
|-
! style="background:#eff7ff" | associative
| {ass_NUM}
|-
! style="background:#eff7ff" | comparative
| {comc_NUM}
]===] .. template_footer(), "NUMBER", number), "NUM", num)
end
local notes_template = [===[
<div style="width:100%;text-align:left;background:#d9ebff">
<div style="display:inline-block;text-align:left;padding-left:1em;padding-right:1em">
{footnote}
</div></div>
]===]
if alternant_multiword_spec.title then
forms.title = alternant_multiword_spec.title
else
forms.title = "Declension of <i lang=\"bo\" class=\"Tibt\">" .. forms.lemma .. "</i>"
end
local annotation = alternant_multiword_spec.annotation or ""
if annotation == "" then
forms.annotation = ""
else
forms.annotation = " (<span style=\"font-weight:normal;font-size:small\">" .. annotation .. "</span>)"
end
local table_spec =
alternant_multiword_spec.number == "both" and table_spec_both or
alternant_multiword_spec.number == "pl" and table_spec_one("pl", "definite plural") or
alternant_multiword_spec.number == "sg" and alternant_multiword_spec.pos == "proper noun" and table_spec_one("sg", "singular") or
table_spec_one("sg", "indefinite")
forms.notes_clause = forms.footnote ~= "" and
format(notes_template, forms) or ""
return format(table_spec, forms)
end
function export.do_generate_forms(parent_args, pos, from_headword, def)
local params = {
[1] = { required = true, default = "སངས་རྒྱས" },
footnote = { list = true },
title = {},
pos = { default = "noun" }
}
local args, parse_props = m_para.process(parent_args, params), {}
if not match(args[1], "<.*>") then args[1] = args[1] .. "<>" end
parse_props = {parse_indicator_spec = parse_indicator_spec}
local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props)
alternant_multiword_spec.title = args.title
alternant_multiword_spec.pos = pos or args.pos
alternant_multiword_spec.footnotes = args.footnote
alternant_multiword_spec.args = args
normalize_all_lemmas(alternant_multiword_spec)
detect_all_indicator_specs(alternant_multiword_spec)
propagate_properties(alternant_multiword_spec, "number", "sg", "both")
local inflect_props = {
skip_slot = function(slot)
return skip_slot(alternant_multiword_spec.number, slot)
end,
slot_table = output_noun_slots_with_linked,
get_variants = get_variants,
inflect_word_spec = handle_derived_slots_and_overrides,
lang = lang
}
iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props)
compute_categories_and_annotation(alternant_multiword_spec)
return alternant_multiword_spec
end
function export.show(frame)
local parent_args = frame:getParent().args or frame.args
local pos = "noun"
local alternant_multiword_spec = export.do_generate_forms(parent_args, pos)
show_forms(alternant_multiword_spec)
return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang)
end
return export