Wiktionary
thwiktionary
https://th.wiktionary.org/wiki/%E0%B8%A7%E0%B8%B4%E0%B8%81%E0%B8%B4%E0%B8%9E%E0%B8%88%E0%B8%99%E0%B8%B2%E0%B8%99%E0%B8%B8%E0%B8%81%E0%B8%A3%E0%B8%A1:%E0%B8%AB%E0%B8%99%E0%B9%89%E0%B8%B2%E0%B8%AB%E0%B8%A5%E0%B8%B1%E0%B8%81
MediaWiki 1.48.0-wmf.1
case-sensitive
สื่อ
พิเศษ
พูดคุย
ผู้ใช้
คุยกับผู้ใช้
วิกิพจนานุกรม
คุยเรื่องวิกิพจนานุกรม
ไฟล์
คุยเรื่องไฟล์
มีเดียวิกิ
คุยเรื่องมีเดียวิกิ
แม่แบบ
คุยเรื่องแม่แบบ
วิธีใช้
คุยเรื่องวิธีใช้
หมวดหมู่
คุยเรื่องหมวดหมู่
ภาคผนวก
คุยเรื่องภาคผนวก
ดัชนี
คุยเรื่องดัชนี
สัมผัส
คุยเรื่องสัมผัส
อรรถาภิธาน
คุยเรื่องอรรถาภิธาน
TimedText
TimedText talk
มอดูล
คุยเรื่องมอดูล
Event
Event talk
มอดูล:la-verb
828
152348
5776195
5729520
2026-11-02T02:08:15Z
Octahedron80
267
5776195
Scribunto
text/plain
-- TODO:
-- 1. (DONE) detect_decl_and_subtypes doesn't do anything with perf_stem or supine_stem.
-- 2. (DONE) Should error on bad subtypes.
-- 3. Make sure Google Books link still works.
-- 4. (DONE) Add 4++ that has alternative perfects -īvī/-iī.
-- 5. (DONE) If sup but no perf, allow passive perfect forms.
-- 6. (DONE) Remove no-actv-perf.
-- 7. (DONE) Support plural prefix/suffix and plural passive prefix/suffix
-- 8. Delegate title line generation to [[Module:la-headword]].
-- 9. Automatic forms of slots which depend on the values of other slots should take into account overrides on the other slot:
-- futr_actv_ptc: determines futr_actv_inf and perp_actv_inf
-- perf_pasv_ptc: determines perf_pasv_indc, plup_pasv_indc, futp_pasv_indc, perf_pasv_inf and futp_pasv_inf (with changes if deponent/semideponent etc)
-- acc_sup: determines futr_pasv_inf (and futr_actv_inf if there's no futr_pasv_ptc)
-- 10. Handle "revertor"-type: deponent in the present, but not in the perfect.
-- If enabled, compare this module with new version of module to make
-- sure all conjugations are the same.
local export = {}
local test_new_la_verb_module = false
local m_la_headword = require("Module:la-headword")
local m_la_nominal = require("Module:la-nominal")
local m_la_utilities = require("Module:la-utilities")
local m_links = require("Module:links")
local m_table = require("Module:table")
local m_utilities = require("Module:utilities")
local lang = require("Module:languages").getByCode("la")
local sc = require("Module:scripts").getByCode("Latn")
local allowed_subtypes = mw.loadData("Module:la-verb/data").allowed_subtypes
local PAGENAME = mw.loadData("Module:headword/data").pagename
local NAMESPACE = mw.title.getCurrentTitle().namespace
if NAMESPACE == 118 then -- Reconstruction
PAGENAME = "*" .. PAGENAME
end
-- Conjugations are the functions that do the actual
-- conjugating by creating the forms of a basic verb.
-- They are defined further down.
local make_conjugation = {}
local add_form = m_la_utilities.add_form
local concat = table.concat
local do_generate_noun_forms = m_la_nominal.do_generate_noun_forms
local do_generate_adj_forms = m_la_nominal.do_generate_adj_forms
local dump = mw.dumpObject
local flatten = m_table.flatten
local form_contains = m_la_utilities.form_contains
local form_is_empty = m_la_utilities.form_is_empty
local forms_equal = m_la_utilities.forms_equal
local full_link = m_links.full_link
local insert = table.insert
local insert_if_not = m_table.insertIfNot
local ipairs = ipairs
local pairs = pairs
local pattern_escape = require("Module:string utilities").pattern_escape
local remove = table.remove
local remove_forms = m_la_utilities.remove_forms
local remove_links = m_links.remove_links
local split = mw.text.split
local table_len = m_table.length
local toNFC = mw.ustring.toNFC
local toNFD = mw.ustring.toNFD
local tostring = tostring
local type = type
local u = mw.ustring.char
local ugsub = mw.ustring.gsub
local umatch = mw.ustring.match
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local usub = mw.ustring.sub
local MACRON = u(0x304)
local BREVE = u(0x306)
local TREMA = u(0x308)
local LENGTH = MACRON .. "?" .. BREVE .. "?" .. TREMA .. "?"
local vowel = "[aeiouyAEIOUY]"
local generic_slots = {}
local non_generic_slots = {}
local potential_lemma_slots = {
"1s_pres_actv_indc", -- regular
"3s_pres_actv_indc", -- impersonal
"1s_perf_actv_indc", -- "coepī"
"3s_perf_actv_indc", -- doesn't occur?
"2s_pres_actv_impr", -- "cedo"
}
local persons = {"1s", "2s", "3s", "1p", "2p", "3p"}
local persons_2 = {"2s", "2p"}
local persons_23 = {"2s", "3s", "2p", "3p"}
local function handle_person(t, v, mood)
local non_pers_slot = t .. "_" .. v .. "_" .. mood
insert(generic_slots, non_pers_slot)
for _, p in ipairs(persons) do
insert(non_generic_slots, p .. "_" .. non_pers_slot)
end
end
local voices = {"actv", "pasv"}
local indc_tenses = {"pres", "impf", "futr", "perf", "plup", "futp", "sigf"}
local subj_tenses = {"pres", "impf", "perf", "plup", "siga"}
local impr_tenses = {"pres", "futr"}
local nonfin_tenses = {"pres", "futr", "perf", "futp", "perp"}
for _, v in ipairs(voices) do
for _, t in ipairs(indc_tenses) do
handle_person(t, v, "indc")
end
for _, t in ipairs(subj_tenses) do
handle_person(t, v, "subj")
end
for _, t in ipairs(impr_tenses) do
handle_person(t, v, "impr")
end
for _, f in ipairs{"inf", "ptc"} do
for _, t in ipairs(nonfin_tenses) do
insert(non_generic_slots, t .. "_" .. v .. "_" .. f)
end
end
end
insert(non_generic_slots, "sigm_actv_inf")
local ger_cases = {"gen", "dat", "acc", "abl"}
for _, c in ipairs(ger_cases) do
insert(non_generic_slots, c .. "_ger")
end
local sup_cases = {"acc", "abl"}
for _, c in ipairs(sup_cases) do
insert(non_generic_slots, c .. "_sup")
end
local function track(page)
require("Module:debug").track("la-verb/" .. page)
end
-- Constant version of :find()
local function cfind(str, text)
return str:find(text, nil, true)
end
-- Used for testing differences between production and sandbox module; see `test_new_la_verb_module` arm in show().
-- Do not delete!
local function concat_vals(val)
if type(val) == "table" then
return table.concat(val, ",")
else
return val
end
end
local function make_link(page, display, face, accel)
return full_link({term = page, alt = display, lang = lang, sc = sc, accel = accel}, face)
end
-- Iterate over all the "slots" associated with a verb declension, where a slot
-- is e.g. 1s_pres_actv_indc (a non-generic slot), pres_actv_indc (a generic slot),
-- or linked_1s_pres_actv_indc (a linked slot). Only include the generic and/or linked
-- slots if called for.
local function iter_slots(include_generic, include_linked)
-- stage == 1: non-generic slots
-- stage == 2: generic slots
-- stage == 3: linked slots
local stage = 1
local slotnum = 0
local max_slotnum = table_len(non_generic_slots)
return function()
slotnum = slotnum + 1
if slotnum > max_slotnum then
slotnum = 1
stage = stage + 1
if stage == 2 then
if include_generic then
max_slotnum = table_len(generic_slots)
else
stage = stage + 1
end
end
if stage == 3 then
if include_linked then
max_slotnum = table_len(potential_lemma_slots)
else
stage = stage + 1
end
end
if stage > 3 then
return nil
end
end
if stage == 1 then
return non_generic_slots[slotnum]
elseif stage == 2 then
return generic_slots[slotnum]
end
return "linked_" .. potential_lemma_slots[slotnum]
end
end
local function ine(val)
if val == "" then
return nil
end
return val
end
-- Construct a simple one- or two-part link, which will be put through full_link later.
local function make_raw_link(page, display)
if page and display then
return "[[" .. page .. "|" .. display .. "]]"
elseif page then
return "[[" .. page .. "]]"
end
return display
end
local function split_prefix_and_base(lemma, main_verbs)
lemma = toNFD(lemma)
for _, main in ipairs(main_verbs) do
local orig_main = main
main = toNFD(main)
local main_pattern = pattern_escape(main)
local prefix = umatch(lemma, "^(.*)" .. main_pattern .. "$")
-- If `main` starts with a vowel and there's no macron or trema, try it with a trema iff it's preceded by a vowel.
if (
not prefix and
main:match("^" .. vowel) and
not umatch(main, "^.[" .. MACRON .. TREMA .. "]")
) then
main_pattern = pattern_escape((main:gsub("^.", "%0" .. TREMA)))
prefix = umatch(lemma, "^(.*" .. vowel .. LENGTH .. ")" .. main_pattern .. "$")
end
if prefix then
return toNFC(prefix), orig_main
end
end
require("Module:collation").sort(main_verbs, lang)
error(("Conjugation %s may only be used with %s, or a derived verb that ends with one of them"):format(
dump("irreg"), m_table.serialCommaJoin(main_verbs, {conj = "or", dontTag = true, dump = true})
))
end
-- Given an ending (or possibly a full regex matching the entire lemma, if
-- a regex group is present), return the base minus the ending, or nil if
-- the ending doesn't match.
local function extract_base(lemma, ending)
if cfind(ending, "(") then
return umatch(lemma, ending)
end
return umatch(lemma, "^(.*)" .. ending .. "$")
end
local function check_spec(lemma, spec, specified_subtypes)
local ending, subtypes, incompatible = unpack(spec)
if incompatible then
for _, subtype in ipairs(incompatible) do
if specified_subtypes[subtype] then
return
end
end
end
for _, subtype in ipairs(subtypes) do
-- A cancelled subtype will have the value false.
if specified_subtypes[subtype] == false then
return
end
end
local base = extract_base(lemma, ending)
if base then
return base, subtypes
end
end
-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- subtypes, where each pair consists of a single ending spec and a list of
-- subtypes), check each ending in turn against LEMMA. If it matches, return
-- the pair BASE, SUBTYPES where BASE is the remainder of LEMMA minus the
-- ending, and SUBTYPES is the subtypes associated with the ending. If no
-- endings match, throw an error if DECLTYPE is non-nil, mentioning the
-- DECLTYPE (the user-specified declension); but if DECLTYPE is nil, just
-- return the pair nil, nil.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "ātur", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
local function get_subtype_by_ending(lemma, conj, specified_subtypes, specs)
for _, spec in ipairs(specs) do
local base, subtypes = check_spec(lemma, spec, specified_subtypes)
if base then
return base, subtypes
end
end
if conj then
error("Unrecognized ending for conjugation " .. conj .. " verb: " .. lemma)
end
return nil, nil
end
local genders = {"m", "f", "n", "mp", "fp", "np"}
local irreg_verbs = {
"aiō",
"aiiō",
"aijō",
"ajō",
"cedo", -- final "o" not "ō"
"dīcō",
"dūcō",
"edō", -- FIXME: Will praedō cause problems?
"dō", -- list after edō
"eō",
"faciō",
"ferō",
"fīō",
"inquam",
"mālō",
"nōlō",
"possum",
"sum",
"volō",
}
local irreg_verbs_conj = {
["aiō"] = "3rd-io",
["aiiō"] = "3rd-io",
["aijō"] = "3rd-io",
["ajō"] = "3rd-io",
["cedo"] = "irreg",
["dīcō"] = "3rd",
["dō"] = "1st",
["dūcō"] = "3rd",
["edō"] = "3rd",
["eō"] = "irreg",
["faciō"] = "3rd-io",
["ferō"] = "3rd",
["fīō"] = "3rd",
["inquam"] = "irreg",
["mālō"] = "irreg",
["nōlō"] = "irreg",
["possum"] = "irreg",
["sum"] = "irreg",
["volō"] = "irreg",
}
local function detect_decl_and_subtypes(args)
local subtypes, specs = {}, split(args[1] or "", "%.")
local conj_arg = remove(specs, 1)
for _, spec in ipairs(specs) do
subtypes[spec:gsub("%-", "")] = spec:sub(1, 1) ~= "-"
end
local orig_lemma = toNFC(args[2] or PAGENAME)
local lemma = remove_links(orig_lemma)
local base, conj, irreg, detected_subtypes
local base_conj_arg, auto_perf_supine = conj_arg:match("^([124])(%+%+?)$")
if base_conj_arg then
if auto_perf_supine == "++" and base_conj_arg ~= "4" then
error("Conjugation types 1++ and 2++ not allowed")
end
conj_arg = base_conj_arg
end
if orig_lemma:sub(1, 1) == "-" then
subtypes.suffix = true
end
local auto_perf, auto_supine, auto_sigm
if subtypes.sigmpasv then
subtypes.sigm = true
end
if subtypes.sigm then
auto_sigm = true
end
if conj_arg == "1" then
conj = "1st"
base, detected_subtypes = get_subtype_by_ending(lemma, "1", subtypes, {
{"ō", {}},
{"or", {"depon"}},
{"at", {"impers"}},
{"ātur", {"depon", "impers"}},
{"ī", {"nopres"}},
{"it", {"nopres", "impers"}},
{"ā", {"imponly"}},
{"āre", {"depon", "imponly"}},
})
if auto_perf_supine then
if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
auto_perf = base
else
auto_perf = base .. "āv"
auto_supine = base .. "āt"
end
end
if auto_sigm then
auto_sigm = base .. "āss"
end
if subtypes.suffix then
subtypes.p3inf = true
end
elseif conj_arg == "2" then
conj = "2nd"
base, detected_subtypes = get_subtype_by_ending(lemma, "2", subtypes, {
{"eō", {}},
{"eor", {"depon"}},
{"et", {"impers"}},
{"ētur", {"depon", "impers"}},
{"ī", {"nopres"}},
{"it", {"nopres", "impers"}},
{"ē", {"imponly"}},
{"ēre", {"depon", "imponly"}},
})
if auto_perf_supine then
if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
auto_perf = base
else
auto_perf = base .. "u"
auto_supine = base .. "it"
end
end
if auto_sigm then
auto_sigm = base .. "ēss"
end
elseif conj_arg == "3" then
base, detected_subtypes = get_subtype_by_ending(lemma, nil, subtypes, {
{"iō", {"i"}},
{"ior", {"depon", "i"}},
})
if base then
conj = "3rd-io"
else
base, detected_subtypes = get_subtype_by_ending(lemma, "3", subtypes, {
{"ō", {}},
{"or", {"depon"}},
{"it", {"impers"}, {"nopres"}},
{"itur", {"depon", "impers"}},
{"ī", {"nopres"}},
{"it", {"nopres", "impers"}},
{"e", {"imponly"}, {"depon"}},
{"ere", {"depon", "imponly"}},
})
if subtypes.i then
conj = "3rd-io"
else
conj = "3rd"
end
end
if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
auto_perf = base
end
if subtypes.suffix then
auto_perf = "-"
auto_supine = "-"
auto_sigm = "-"
end
elseif conj_arg == "4" then
conj = "4th"
base, detected_subtypes = get_subtype_by_ending(lemma, "4", subtypes, {
{"iō", {}},
{"ior", {"depon"}},
{"it", {"impers"}, {"nopres"}},
{"ītur", {"depon", "impers"}},
{"ī", {"nopres"}, {"imponly"}},
{"it", {"nopres", "impers"}},
{"ī", {"imponly"}},
{"īre", {"depon", "imponly"}},
})
if subtypes.nopres or subtypes.perfaspres or detected_subtypes.nopres then
auto_perf = base
elseif auto_perf_supine == "++" then
auto_perf = base .. "īv/" .. base .. "i"
auto_supine = base .. "īt"
elseif auto_perf_supine == "+" then
auto_perf = base .. "īv"
auto_supine = base .. "īt"
end
if auto_sigm then
auto_sigm = base .. "īss"
end
elseif conj_arg == "irreg" then
irreg = true
local prefix
prefix, base = split_prefix_and_base(lemma, irreg_verbs)
conj = irreg_verbs_conj[base]
args[1] = base
args[2] = prefix
-- args[3] and args[4] are used by ferō and sum and stay where they are
detected_subtypes = {}
else
error("Unrecognized conjugation: " .. dump(conj_arg))
end
for _, detected_subtype in ipairs(detected_subtypes) do
if detected_subtype == "impers" and subtypes["3only"] then
-- 3only overrides impers
elseif detected_subtype == "nopres" and subtypes.perfaspres then
-- perfaspres overrides nopres
else
subtypes[detected_subtype] = true
end
end
-- Get the gender of the verb's complement.
local gender
for _, g in ipairs(genders) do
if subtypes[g] then
if gender then
error(("Only one of the subtypes %s may be specified"):format(
m_table.serialCommaJoin(genders, {conj = "or", dontTag = true, dump = true})
))
end
gender = g
end
end
if not gender then
gender = (subtypes.impers or subtypes.passimpers or subtypes["3only"] or subtypes["pass3only"]) and "n" or "m"
end
if subtypes.nopres or subtypes.perfaspres then
subtypes.noger = true
end
if not irreg then
args[1] = base
local perf_stem, supine_stem
if subtypes.depon or subtypes.semidepon or subtypes.nopres or subtypes.perfaspres then
supine_stem = args[3] or auto_supine
if supine_stem == "-" and not subtypes.suffix then
supine_stem = nil
end
if not supine_stem then
if subtypes.depon or subtypes.semidepon then
subtypes.noperf = true
end
subtypes.nosup = true
end
if subtypes.sigm then
local sigm_stem = args[5] or auto_sigm
if sigm_stem == "-" and not subtypes.suffix then
sigm_stem = nil
end
args[5] = sigm_stem
end
args[2] = supine_stem
args[3] = nil
else
perf_stem = args[3] or auto_perf
if perf_stem == "-" and not subtypes.suffix then
perf_stem = nil
end
if not perf_stem then
subtypes.noperf = true
end
supine_stem = args[4] or auto_supine
if supine_stem == "-" and not subtypes.suffix then
supine_stem = nil
end
if not supine_stem then
subtypes.nosup = true
end
if subtypes.sigm then
local sigm_stem = args[5] or auto_sigm
if sigm_stem == "-" and not subtypes.suffix then
sigm_stem = nil
end
args[5] = sigm_stem
end
args[2] = perf_stem
args[3] = supine_stem
end
args[4] = nil
end
if auto_sigm and subtypes.depon then
subtypes.sigmpasv = true
end
for subtype in pairs(subtypes) do
if (
subtype == "i" and conj_arg ~= "3" or
not allowed_subtypes[subtype]
) then
error(("Unrecognized verb subtype %s for this conjugation"):format(dump(subtype)))
end
end
return conj, irreg, subtypes, orig_lemma, lemma, gender
end
local function _add_forms(forms, keytype, persons, stem, ...)
for i, p in ipairs(persons) do
local suf = select(i, ...)
if suf ~= nil then
add_form(forms, p .. "_" .. keytype, stem, suf)
end
end
end
-- Add a value to all persons/numbers of a given tense/voice/mood, e.g.
-- "pres_actv_indc" (specified by KEYTYPE). If a value is already present
-- in a key, it won't be added again.
--
-- The value for a given person/number combination is formed by concatenating
-- STEM and the appropriate suffix for that person/number, e.g. SUF1S. The
-- suffix can be a list, in which case STEM will be concatenated in turn to
-- each value in the list and all the resulting forms added to the key. To
-- not add a value for a specific person/number, specify nil or {} for the
-- suffix for the person/number.
local function add_forms(forms, keytype, stem, ...)
return _add_forms(forms, keytype, persons, stem, ...)
end
-- Add a value to the 2nd person (singular and plural) of a given
-- tense/voice/mood. This works like add_forms().
local function add_2_forms(forms, keytype, stem, ...)
return _add_forms(forms, keytype, persons_2, stem, ...)
end
-- Add a value to the 2nd and 3rd persons (singular and plural) of a given
-- tense/voice/mood. This works like add_forms().
local function add_23_forms(forms, keytype, stem, ...)
return _add_forms(forms, keytype, persons_23, stem, ...)
end
local function override(args, data)
local forms = data.forms
for slot in iter_slots(true, false) do
if args[slot] then
forms[slot] = split(args[slot], "/")
end
end
end
local function set_linked_forms(data, typeinfo)
-- Generate linked variants of slots that may be the lemma.
-- If the form is the same as the lemma (with links removed),
-- substitute the original lemma (with links included).
local forms = data.forms
for _, slot in ipairs(potential_lemma_slots) do
local formval = forms[slot]
local linked_forms = {}
if formval then
if type(formval) ~= "table" then
formval = {formval}
end
for _, form in ipairs(formval) do
if form == typeinfo.lemma then
insert(linked_forms, typeinfo.orig_lemma)
else
insert(linked_forms, form)
end
end
end
forms["linked_" .. slot] = linked_forms
end
end
local function link_words_if_unlinked(affix)
return affix:match("%[%[.-]]") and affix or ugsub(affix, "%w+", "[[%0]]")
end
local function get_affix(data, key)
local affix = data[key] or ""
return link_words_if_unlinked(affix), remove_links(affix)
end
-- Add prefixes and suffixes to non-generic slots. The generic slots (e.g.
-- perf_pasv_indc, whose text indicates to use the past passive participle +
-- the present active indicative of [[sum]]), handle prefixes and suffixes
-- themselves in make_perfect_passive().
local function add_prefix_suffix(data)
if not data.prefix and not data.suffix then
return
end
local active_prefix, active_prefix_no_links = get_affix(data, "prefix")
local passive_prefix, passive_prefix_no_links = get_affix(data, "passive_prefix")
local plural_prefix, plural_prefix_no_links = get_affix(data, "plural_prefix")
local plural_passive_prefix, plural_passive_prefix_no_links = get_affix(data, "plural_passive_prefix")
local active_suffix, active_suffix_no_links = get_affix(data, "suffix")
local passive_suffix, passive_suffix_no_links = get_affix(data, "passive_suffix")
local plural_suffix, plural_suffix_no_links = get_affix(data, "plural_suffix")
local plural_passive_suffix, plural_passive_suffix_no_links = get_affix(data, "plural_passive_suffix")
local forms = data.forms
for slot in iter_slots(false, true) do
local prefix, suffix, prefix_no_links, suffix_no_links
if cfind(slot, "pasv") and slot:match("[123]p") then
prefix = plural_passive_prefix
suffix = plural_passive_suffix
prefix_no_links = plural_passive_prefix_no_links
suffix_no_links = plural_passive_suffix_no_links
elseif cfind(slot, "pasv") and not cfind(slot, "_inf") then
prefix = passive_prefix
suffix = passive_suffix
prefix_no_links = passive_prefix_no_links
suffix_no_links = passive_suffix_no_links
elseif slot:match("[123]p") then
prefix = plural_prefix
suffix = plural_suffix
prefix_no_links = plural_prefix_no_links
suffix_no_links = plural_suffix_no_links
else
prefix = active_prefix
suffix = active_suffix
prefix_no_links = active_prefix_no_links
suffix_no_links = active_suffix_no_links
end
local formval = forms[slot]
if not form_is_empty(formval) then
local affixed_forms = {}
if type(formval) ~= "table" then
formval = {formval}
end
for _, form in ipairs(formval) do
if form_is_empty(form) then
insert(affixed_forms, form)
elseif slot:match("^linked") then
-- If we're dealing with a linked slot, include the original
-- links in the prefix/suffix and also add a link around the
-- form itself if links aren't already present. (Note, above
-- we early-exited if there was no prefix and no suffix.)
if not form:match("%[%[.-]]") then
form = "[[" .. form .. "]]"
end
insert(affixed_forms, prefix .. form .. suffix)
elseif form:match("%[%[.-]]") then
-- If not dealing with a linked slot, but there are links
-- in the slot, include the original, potentially linked
-- versions of the prefix and suffix (e.g. in perfect
-- passive forms).
insert(affixed_forms, prefix .. form .. suffix)
else
-- Otherwise, use the non-linking versions of the prefix and
-- suffix so that the whole term (including prefix/suffix)
-- gets linked.
insert(affixed_forms, prefix_no_links .. form .. suffix_no_links)
end
end
forms[slot] = affixed_forms
end
end
end
local function notes_override(args, data)
local notes = {args["note1"], args["note2"], args["note3"]}
for n, note in pairs(notes) do
if note == "-" then
data.footnotes[n] = nil
elseif note == "p3inf" then
data.footnotes[n] = "The present passive infinitive in ''-ier'' is a rare poetic form which is attested."
elseif note == "poetsyncperf" then
data.footnotes[n] = "At least one rare poetic syncopated perfect form is attested."
elseif note == "sigm" then
data.footnotes[n] = "At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\")."
elseif note == "sigmpasv" then
data.footnotes[n] = "At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\"). It is also attested as having a rare sigmatic future passive indicative form (\"will have been\"), which is not attested in the plural for any verb."
elseif note == "sigmdepon" then
data.footnotes[n] = "At least one use of the Old Latin \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, and, as the verb is deponent, takes the form of what would otherwise be the rare sigmatic future passive indicative tense (which is not attested in the plural for any verb)."
elseif note then
data.footnotes[n] = note
end
end
if args["notes"] == "-" then
data.footnotes = {}
end
end
-- Space added by default; hyphens unchanged; underscore represents no space.
local affix_joining_chars = {
[""] = " ",
["-"] = "-",
["_"] = "",
}
local function normalize_prefix(prefix)
return prefix and prefix:gsub("[%-_]?$", affix_joining_chars, 1)
end
local function normalize_suffix(suffix)
return suffix and suffix:gsub("^[%-_]?", affix_joining_chars, 1)
end
local function handle_subtype_affix(args, affix, subtype_affix, normalize_func)
if args[subtype_affix] then
if not args[affix] then
error(("Can't specify %s= without %s="):format(subtype_affix, affix))
end
return normalize_func(args[subtype_affix])
end
end
local function handle_affix(args, data, affix, normalize_func)
data[affix] = normalize_func(args[affix])
for _, subtype in ipairs{"passive", "plural"} do
local subtype_affix = subtype .. "_" .. affix
data[subtype_affix] = handle_subtype_affix(args, affix, subtype_affix, normalize_func) or data[affix]
end
-- First fall back to the passive (e.g. "poenās dare", where the plural noun
-- is used with both singular and plural verbs, but there's a separate
-- passive form ''poenae datur''), then to the plural prefix, then to the
-- base prefix.
local subtype_affix = "plural_passive_" .. affix
data[subtype_affix] = handle_subtype_affix(args, affix, subtype_affix, normalize_func) or
data["passive_" .. affix] or data["plural_" .. affix] or data[affix]
end
function export.make_data(parent_args, from_headword, def1, def2)
local params = {
[1] = {required = true, default = def1 or "1+"},
[2] = {required = true, default = def2 or "amō"},
[3] = true,
[4] = true,
[5] = true,
prefix = true,
passive_prefix = true,
plural_prefix = true,
plural_passive_prefix = true,
suffix = true,
passive_suffix = true,
plural_suffix = true,
plural_passive_suffix = true,
label = true,
note1= true,
note2= true,
note3= true,
notes= true,
-- examined directly in export.show()
search = true,
json = {type = "boolean"},
}
for slot in iter_slots(true, false) do
params[slot] = true
end
if from_headword then
local list = {list = true}
params.lemma = list
params.id = true
params.cat = list
end
local args = require("Module:parameters").process(parent_args, params)
local conj, irreg, subtypes, orig_lemma, lemma, gender = detect_decl_and_subtypes(args)
-- note: the addition of red superscripted footnotes ('<sup class="roa-red-superscript">' ... </sup>) is only implemented for the three form printing loops in which it is used
local data = {
forms = {},
title = {},
categories = args.cat and m_table.deepCopy(args.cat) or {},
form_footnote_indices = {},
footnotes = {},
id = args.id,
overriding_lemma = args.lemma,
gender = gender,
}
local typeinfo = {
lemma = lemma,
orig_lemma = orig_lemma,
conj = conj,
irreg = irreg,
subtypes = subtypes,
}
handle_affix(args, data, "prefix", normalize_prefix)
handle_affix(args, data, "suffix", normalize_suffix)
-- Generate the verb forms
make_conjugation(args, data, typeinfo)
-- Generate the title text
m_la_headword.verb_title(data.title, typeinfo, export.get_lemma_forms(data))
-- Override with user-set forms
override(args, data)
-- Set linked_* forms
set_linked_forms(data, typeinfo)
-- Prepend any prefixes, append any suffixes
add_prefix_suffix(data)
if args["label"] then
insert_if_not(data.title, args["label"])
end
notes_override(args, data)
-- If json=1 specified, return a JSON string; used by bots
if args.json then
return require("Module:JSON").toJSON {
data = data,
typeinfo = typeinfo
}
end
return data, typeinfo
end
local ptc_lemma_form = {
["m"] = "sg_m",
["f"] = "sg_f",
["n"] = "sg_n",
["mp"] = "pl_m",
["fp"] = "pl_f",
["np"] = "pl_n",
}
-- Make the gerund and gerundive/future passive participle. For the forms
-- labeled "gerund", we generate both gerund and gerundive variants if there's
-- a case-specific prefix or suffix for the case in question; otherwise we
-- generate only the gerund per se. BASE is the stem (ending in -nd).
-- UND_VARIANT, if true, means that a gerundive in -und should be generated
-- along with a gerundive in -end.
local function make_gerund(data, typeinfo, base, und_variant)
local subtypes = typeinfo.subtypes
if subtypes.noger then
return
end
local forms, gender = data.forms, data.gender
local slot = "nom_" .. ptc_lemma_form[gender]
add_form(forms, "futr_pasv_ptc", "", do_generate_adj_forms({base .. "us<1&2+>"}, "participles", "bare").forms[slot])
if und_variant and not (subtypes.nound or base:match("[vV]end$") or umatch(base, "[uU]" .. LENGTH .. "end$")) then
-- Per Lane's grammar section 899: "Verbs in -ere and -īre often have
-- -undus, when not preceded by u or v, especially in formal style"
-- There is also an optional exclusion if -undus is not attested
add_form(forms, "futr_pasv_ptc", "", do_generate_adj_forms({base:gsub("end$", "und") .. "us<1&2+>"}, "participles", "bare").forms[slot])
end
local ger_forms = do_generate_noun_forms({base .. "um<2>"}, "nouns", "bare").forms
for _, case in ipairs(ger_cases) do
for _, form in ipairs(ger_forms[case .. "_sg"]) do
add_form(forms, case .. "_ger", "", form)
end
end
end
-- Conjugation functions
local function get_regular_stems(args, typeinfo)
local subtypes = typeinfo.subtypes
-- Get the parameters
if subtypes.nopres or subtypes.perfaspres then
typeinfo.pres_stem = nil
typeinfo.perf_stem = ine(args[1])
typeinfo.supine_stem = ine(args[2])
elseif subtypes.depon or subtypes.semidepon then
-- Deponent and semi-deponent verbs don't have the perfective principal part.
-- But optionally semi-deponent verbs do.
typeinfo.pres_stem = ine(args[1])
typeinfo.perf_stem = nil
typeinfo.supine_stem = ine(args[2])
else
typeinfo.pres_stem = ine(args[1])
typeinfo.perf_stem = ine(args[2])
typeinfo.supine_stem = ine(args[3])
end
if subtypes.sigm then
typeinfo.sigm_stem = ine(args[5])
end
-- Prepare stems
if not (typeinfo.pres_stem or subtypes.nopres or subtypes.perfaspres) then -- Template:
error("Present stem has not been provided")
end
if typeinfo.perf_stem then
typeinfo.perf_stem = split(typeinfo.perf_stem, "/")
else
typeinfo.perf_stem = {}
end
if typeinfo.supine_stem then
typeinfo.supine_stem = split(typeinfo.supine_stem, "/")
else
typeinfo.supine_stem = {}
end
if typeinfo.sigm_stem then
typeinfo.sigm_stem = split(typeinfo.sigm_stem, "/")
else
typeinfo.sigm_stem = {}
end
end
local function set_conj_categories(ord, data, typeinfo)
local categories, subtypes = data.categories, typeinfo.subtypes
insert_if_not(categories, "คำกริยากลุ่ม" .. ord .. " ภาษาละติน") -- ละ "ผันรูปกริยา"
if subtypes.depon then
insert_if_not(categories, "Latin " .. ord .. " conjugation deponent verbs")
end
if subtypes.noperf then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing perfect stem")
end
if subtypes.nosup then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing supine stem")
elseif subtypes.supfutractvonly then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with missing supine stem except in the future active participle ")
end
local pres_stem = typeinfo.pres_stem
if not pres_stem then
return
end
pres_stem = pres_stem and pres_stem:gsub("[Qq]u", "\1")
for _, perf_stem in ipairs(typeinfo.perf_stem) do
perf_stem = perf_stem:gsub("[Qq]u", "\1")
if perf_stem == pres_stem .. "āv" then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -āv-")
elseif perf_stem == pres_stem .. "ēv" then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -ēv-")
elseif perf_stem == pres_stem .. "īv" then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -īv-")
elseif perf_stem == pres_stem .. "i" then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -i-")
elseif perf_stem == pres_stem .. "u" then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -u-")
elseif perf_stem == pres_stem then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with suffixless perfect")
elseif perf_stem:match("[sx]$") then
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with perfect in -s- or -x-")
else
insert_if_not(categories, "Latin " .. ord .. " conjugation verbs with irregular perfect")
end
end
end
-- Form-generating functions
local make_pres = {}
make_pres["1st"] = function(data, typeinfo, pres_stem)
local forms = data.forms
local a = typeinfo.subtypes.shorta and "a" or "ā"
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", pres_stem, "ō", "ās", "at", a .. "mus", a .. "tis", "ant")
add_forms(forms, "impf_actv_indc", pres_stem, a .. "bam", a .. "bās", a .. "bat", a .. "bāmus", a .. "bātis", a .. "bant")
add_forms(forms, "futr_actv_indc", pres_stem, a .. "bō", a .. "bis", a .. "bit", a .. "bimus", a .. "bitis", a .. "bunt")
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", pres_stem, "or", {a .. "ris", a .. "re"}, a .. "tur", a .. "mur", a .. "minī", "antur")
add_forms(forms, "impf_pasv_indc", pres_stem, a .. "bar", {a .. "bāris", a .. "bāre"}, a .. "bātur", a .. "bāmur", a .. "bāminī", a .. "bantur")
add_forms(forms, "futr_pasv_indc", pres_stem, a .. "bor", {a .. "beris", a .. "bere"}, a .. "bitur", a .. "bimur", a .. "biminī", a .. "buntur")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", pres_stem, "em", "ēs", "et", "ēmus", "ētis", "ent")
add_forms(forms, "impf_actv_subj", pres_stem, a .. "rem", a .. "rēs", a .. "ret", a .. "rēmus", a .. "rētis", a .. "rent")
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", pres_stem, "er", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
add_forms(forms, "impf_pasv_subj", pres_stem, a .. "rer", {a .. "rēris", a .. "rēre"}, a .. "rētur", a .. "rēmur", a .. "rēminī", a .. "rentur")
-- Imperative
add_2_forms(forms, "pres_actv_impr", pres_stem, "ā", a .. "te")
add_23_forms(forms, "futr_actv_impr", pres_stem, a .. "tō", a .. "tō", a .. "tōte", "antō")
add_2_forms(forms, "pres_pasv_impr", pres_stem, a .. "re", a .. "minī")
add_23_forms(forms, "futr_pasv_impr", pres_stem, a .. "tor", a .. "tor", nil, "antor")
-- Present infinitives
forms["pres_actv_inf"] = pres_stem .. a .. "re"
forms["pres_pasv_inf"] = pres_stem .. a .. "rī"
-- Imperfective participles
forms["pres_actv_ptc"] = pres_stem .. "āns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "and")
end
make_pres["2nd"] = function(data, typeinfo, pres_stem)
local forms = data.forms
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", pres_stem, "eō", "ēs", "et", "ēmus", "ētis", "ent")
add_forms(forms, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
add_forms(forms, "futr_actv_indc", pres_stem, "ēbō", "ēbis", "ēbit", "ēbimus", "ēbitis", "ēbunt")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", pres_stem, "eam", "eās", "eat", "eāmus", "eātis", "eant")
add_forms(forms, "impf_actv_subj", pres_stem, "ērem", "ērēs", "ēret", "ērēmus", "ērētis", "ērent")
-- Active imperative
add_2_forms(forms, "pres_actv_impr", pres_stem, "ē", "ēte")
add_23_forms(forms, "futr_actv_impr", pres_stem, "ētō", "ētō", "ētōte", "entō")
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", pres_stem, "eor", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
add_forms(forms, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
add_forms(forms, "futr_pasv_indc", pres_stem, "ēbor", {"ēberis", "ēbere"}, "ēbitur", "ēbimur", "ēbiminī", "ēbuntur")
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", pres_stem, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
add_forms(forms, "impf_pasv_subj", pres_stem, "ērer", {"ērēris", "ērēre"}, "ērētur", "ērēmur", "ērēminī", "ērentur")
-- Passive imperative
add_2_forms(forms, "pres_pasv_impr", pres_stem, "ēre", "ēminī")
add_23_forms(forms, "futr_pasv_impr", pres_stem, "ētor", "ētor", nil, "entor")
-- Present infinitives
forms["pres_actv_inf"] = pres_stem .. "ēre"
forms["pres_pasv_inf"] = pres_stem .. "ērī"
-- Imperfective participles
forms["pres_actv_ptc"] = pres_stem .. "ēns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "end")
end
make_pres["3rd"] = function(data, typeinfo, pres_stem)
local forms = data.forms
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", pres_stem, "ō", "is", "it", "imus", "itis", "unt")
add_forms(forms, "impf_actv_indc", pres_stem, "ēbam", "ēbās", "ēbat", "ēbāmus", "ēbātis", "ēbant")
add_forms(forms, "futr_actv_indc", pres_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", pres_stem, "or", {"eris", "ere"}, "itur", "imur", "iminī", "untur")
add_forms(forms, "impf_pasv_indc", pres_stem, "ēbar", {"ēbāris", "ēbāre"}, "ēbātur", "ēbāmur", "ēbāminī", "ēbantur")
add_forms(forms, "futr_pasv_indc", pres_stem, "ar", {"ēris", "ēre"}, "ētur", "ēmur", "ēminī", "entur")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", pres_stem, "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(forms, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", pres_stem, "ar", {"āris", "āre"}, "ātur", "āmur", "āminī", "antur")
add_forms(forms, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")
-- Imperative
add_2_forms(forms, "pres_actv_impr", pres_stem, "e", "ite")
add_23_forms(forms, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "untō")
add_2_forms(forms, "pres_pasv_impr", pres_stem, "ere", "iminī")
add_23_forms(forms, "futr_pasv_impr", pres_stem, "itor", "itor", nil, "untor")
-- Present infinitives
forms["pres_actv_inf"] = pres_stem .. "ere"
forms["pres_pasv_inf"] = pres_stem .. "ī"
-- Imperfective participles
forms["pres_actv_ptc"] = pres_stem .. "ēns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "end", "und-variant")
end
make_pres["3rd-io"] = function(data, typeinfo, pres_stem)
local forms = data.forms
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", pres_stem, "iō", "is", "it", "imus", "itis", "iunt")
add_forms(forms, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
add_forms(forms, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
add_forms(forms, "impf_actv_subj", pres_stem, "erem", "erēs", "eret", "erēmus", "erētis", "erent")
-- Active imperative
add_2_forms(forms, "pres_actv_impr", pres_stem, "e", "ite")
add_23_forms(forms, "futr_actv_impr", pres_stem, "itō", "itō", "itōte", "iuntō")
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", pres_stem, "ior", {"eris", "ere"}, "itur", "imur", "iminī", "iuntur")
add_forms(forms, "impf_pasv_indc", pres_stem, "iēbar", {"iēbāris", "iēbāre"}, "iēbātur", "iēbāmur", "iēbāminī", "iēbantur")
add_forms(forms, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
add_forms(forms, "impf_pasv_subj", pres_stem, "erer", {"erēris", "erēre"}, "erētur", "erēmur", "erēminī", "erentur")
-- Passive imperative
add_2_forms(forms, "pres_pasv_impr", pres_stem, "ere", "iminī")
add_23_forms(forms, "futr_pasv_impr", pres_stem, "itor", "itor", nil, "iuntor")
-- Present infinitives
forms["pres_actv_inf"] = pres_stem .. "ere"
forms["pres_pasv_inf"] = pres_stem .. "ī"
-- Imperfective participles
forms["pres_actv_ptc"] = pres_stem .. "iēns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant")
end
make_pres["4th"] = function(data, typeinfo, pres_stem)
local forms = data.forms
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", pres_stem, "iō", "īs", "it", "īmus", "ītis", "iunt")
add_forms(forms, "impf_actv_indc", pres_stem, "iēbam", "iēbās", "iēbat", "iēbāmus", "iēbātis", "iēbant")
add_forms(forms, "futr_actv_indc", pres_stem, "iam", "iēs", "iet", "iēmus", "iētis", "ient")
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", pres_stem, "ior", {"īris", "īre"}, "ītur", "īmur", "īminī", "iuntur")
add_forms(forms, "impf_pasv_indc", pres_stem, "iēbar", {"iēbāris", "iēbāre"}, "iēbātur", "iēbāmur", "iēbāminī", "iēbantur")
add_forms(forms, "futr_pasv_indc", pres_stem, "iar", {"iēris", "iēre"}, "iētur", "iēmur", "iēminī", "ientur")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", pres_stem, "iam", "iās", "iat", "iāmus", "iātis", "iant")
add_forms(forms, "impf_actv_subj", pres_stem, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", pres_stem, "iar", {"iāris", "iāre"}, "iātur", "iāmur", "iāminī", "iantur")
add_forms(forms, "impf_pasv_subj", pres_stem, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")
-- Imperative
add_2_forms(forms, "pres_actv_impr", pres_stem, "ī", "īte")
add_23_forms(forms, "futr_actv_impr", pres_stem, "ītō", "ītō", "ītōte", "iuntō")
add_2_forms(forms, "pres_pasv_impr", pres_stem, "īre", "īminī")
add_23_forms(forms, "futr_pasv_impr", pres_stem, "ītor", "ītor", nil, "iuntor")
-- Present infinitives
forms["pres_actv_inf"] = pres_stem .. "īre"
forms["pres_pasv_inf"] = pres_stem .. "īrī"
-- Imperfective participles
forms["pres_actv_ptc"] = pres_stem .. "iēns"
-- Gerund
make_gerund(data, typeinfo, pres_stem .. "iend", "und-variant")
end
local make_pres_mt = {}
function make_pres_mt:__call(data, typeinfo, pres_stem)
if not pres_stem then
return
end
return self[typeinfo.conj](data, typeinfo, pres_stem)
end
setmetatable(make_pres, make_pres_mt)
local function make_perf(data, perf_stem, no_inf)
if not perf_stem then
return
end
local forms = data.forms
if type(perf_stem) ~= "table" then
perf_stem = {perf_stem}
end
for _, stem in ipairs(perf_stem) do
-- Perfective indicative
add_forms(forms, "perf_actv_indc", stem, "ī", "istī", "it", "imus", "istis", {"ērunt", "ēre"})
add_forms(forms, "plup_actv_indc", stem, "eram", "erās", "erat", "erāmus", "erātis", "erant")
add_forms(forms, "futp_actv_indc", stem, "erō", "eris", "erit", "erimus", "eritis", "erint")
-- Perfective subjunctive
add_forms(forms, "perf_actv_subj", stem, "erim", "erīs", "erit", "erīmus", "erītis", "erint")
add_forms(forms, "plup_actv_subj", stem, "issem", "issēs", "isset", "issēmus", "issētis", "issent")
-- Perfect infinitive
if not no_inf then
add_form(forms, "perf_actv_inf", stem, "isse")
end
end
end
local function make_true_supine(data, typeinfo, supine_stem)
local subtypes = typeinfo.subtypes
if subtypes.nosup or subtypes.supfutractvonly or not supine_stem then
return
end
local forms = data.forms
if type(supine_stem) ~= "table" then
supine_stem = {supine_stem}
end
for _, stem in ipairs(supine_stem) do
local supine_forms = do_generate_noun_forms({stem .. "us<4>"}, "nouns", "bare").forms
add_form(forms, "acc_sup", "", supine_forms["acc_sg"])
add_form(forms, "abl_sup", "", supine_forms["abl_sg"])
add_form(forms, "futr_pasv_inf", forms["acc_sup"], " īrī")
if subtypes.nofutractvptc then
add_form(forms, "futr_actv_inf", forms["acc_sup"], " īre")
end
end
end
local function make_perf_pasv_ptc(data, typeinfo, supine_stem)
local subtypes = typeinfo.subtypes
if subtypes.nosup or subtypes.supfutractvonly or not supine_stem then
return
end
local forms, gender = data.forms
if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
gender = "m" -- perf_pasv_ptc becomes perf_actv_ptc, so the gender of the complement isn't relevant
else
gender = data.gender
end
if type(supine_stem) ~= "table" then
supine_stem = {supine_stem}
end
for _, stem in ipairs(supine_stem) do
-- Perfect passive participle
local perf_pasv_ptc_forms = do_generate_adj_forms({stem .. "us<1&2+>"}, "participles", "bare").forms
local form = perf_pasv_ptc_forms["nom_" .. ptc_lemma_form[gender]]
add_form(forms, "perf_pasv_ptc", "", form)
add_forms(forms, "perf_pasv_indc", form, " sum", " es", " est", nil, nil, nil)
add_forms(forms, "plup_pasv_indc", form, " eram", " erās", " erat", nil, nil, nil)
add_forms(forms, "futp_pasv_indc", form, " erō", " eris", " erit", nil, nil, nil)
add_forms(forms, "perf_pasv_subj", form, " sim", " sīs", " sit", nil, nil, nil)
add_forms(forms, "plup_pasv_subj", form, " essem", " essēs", " esset", nil, nil, nil)
form = perf_pasv_ptc_forms["nom_" .. ptc_lemma_form[gender]:gsub("sg", "pl")]
add_forms(forms, "perf_pasv_indc", form, nil, nil, nil, " sumus", " estis", " sunt")
add_forms(forms, "plup_pasv_indc", form, nil, nil, nil, " erāmus", " erātis", " erant")
add_forms(forms, "futp_pasv_indc", form, nil, nil, nil, " erimus", " eritis", " erint")
add_forms(forms, "perf_pasv_subj", form, nil, nil, nil, " sīmus", " sītis", " sint")
add_forms(forms, "plup_pasv_subj", form, nil, nil, nil, " essēmus", " essētis", " essent")
form = perf_pasv_ptc_forms["acc_" .. ptc_lemma_form[gender]]
add_form(forms, "perf_pasv_inf", form, " esse")
add_form(forms, "futp_pasv_inf", form, " fore")
end
end
-- A final "u" on the supine stem is omitted (e.g. "mortu" to "moritūrus").
local function make_futr_actv_ptc_stem(supine_stem)
local futr_actv_ptc_stem = {}
for _, stem in ipairs(supine_stem) do
insert(futr_actv_ptc_stem, (stem:gsub("u?$", "")))
end
return futr_actv_ptc_stem
end
local function make_futr_actv_ptc(data, typeinfo, futr_actv_ptc_stem)
local subtypes = typeinfo.subtypes
if subtypes.nosup or subtypes.nofutractvptc or not futr_actv_ptc_stem then
return
end
local forms = data.forms
if type(futr_actv_ptc_stem) ~= "table" then
futr_actv_ptc_stem = {futr_actv_ptc_stem}
end
for _, stem in ipairs(futr_actv_ptc_stem) do
local futr_actv_ptc_forms = do_generate_adj_forms({stem .. "ūrus<1&2+>"}, "participles", "bare").forms
add_form(forms, "futr_actv_ptc", "", futr_actv_ptc_forms["nom_sg_m"])
local form = futr_actv_ptc_forms["acc_sg_m"]
add_form(forms, "futr_actv_inf", form, " esse")
add_form(forms, "perp_actv_inf", form, " fuisse")
end
end
local function make_supine(data, typeinfo, supine_stem, futr_actv_ptc_stem)
if typeinfo.subtypes.nosup or not supine_stem then
return
end
make_true_supine(data, typeinfo, supine_stem)
make_perf_pasv_ptc(data, typeinfo, supine_stem)
if type(supine_stem) ~= "table" then
supine_stem = {supine_stem}
end
if futr_actv_ptc_stem == nil then
futr_actv_ptc_stem = make_futr_actv_ptc_stem(supine_stem)
end
make_futr_actv_ptc(data, typeinfo, futr_actv_ptc_stem)
end
local function make_sigm(data, typeinfo, sigm_stem)
local subtypes = typeinfo.subtypes
if not subtypes.sigm then
return
end
local forms = data.forms
if type(sigm_stem) ~= "table" then
sigm_stem = {sigm_stem}
end
local noteindex = table_len(data.footnotes) + 1
-- Deponent verbs use the passive form
for _, stem in ipairs(sigm_stem) do
-- Sigmatic future active indicative
add_forms(forms, "sigf_actv_indc", stem, "ō", "is", "it", "imus", "itis", "int")
-- Sigmatic future passive indicative
if subtypes.sigmpasv then
add_forms(forms, "sigf_pasv_indc", stem, "or", "eris", "itur", nil, nil, nil) -- Plurals not attested.
end
-- Sigmatic future active subjunctive
add_forms(forms, "siga_actv_subj", stem, "im", "īs", "īt", "īmus", "ītis", "int")
-- Perfect infinitive
if not no_inf then
add_form(forms, "sigm_actv_inf", stem, "ere")
end
end
data.form_footnote_indices["sigm"] = noteindex
if subtypes.depon then
data.footnotes[noteindex] = 'At least one use of the Old Latin \"sigmatic future\" tense is attested, which is used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, and, as the verb is deponent, takes the form of what would otherwise be the rare sigmatic future passive indicative tense (which is not attested in the plural for any verb).'
else
data.footnotes[noteindex] = 'At least one use of the Old Latin \"sigmatic future\" and \"sigmatic aorist\" tenses is attested, which are used by [[Old Latin]] writers; most notably [[w:Plautus|Plautus]] and [[w:Terence|Terence]]. The sigmatic future is generally ascribed a future or future perfect meaning, while the sigmatic aorist expresses a possible desire (\"might want to\").'
if subtypes.sigmpasv then
data.footnotes[noteindex] = data.footnotes[noteindex] .. ' It is also attested as having a rare sigmatic future passive indicative form (\"will have been\"), which is not attested in the plural for any verb.'
end
end
end
make_conjugation["1st"] = function(args, data, typeinfo)
set_conj_categories("ที่ 1", data, typeinfo)
local pres_stem = typeinfo.pres_stem
make_pres(data, typeinfo, pres_stem)
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, typeinfo.supine_stem)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
-- Additional forms in specific cases.
local forms = data.forms
if pres_stem == "dīlapid" then
add_form(forms, "3p_sigf_actv_indc", "", "dīlapidāssunt", 2)
elseif pres_stem == "invol" then
add_form(forms, "3s_sigf_actv_indc", "", "involāsit", 2)
elseif pres_stem == "viol" then
local noteindex = table_len(data.footnotes) + 1
add_form(forms, "3p_futp_actv_indc", "", "violārint", 2)
add_form(forms, "3p_perf_actv_subj", "", "violārint", 2)
add_form(forms, "3s_sigf_actv_indc", "", "violāsit", 2)
data.form_footnote_indices["3p_futp_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3p_perf_actv_subj"] = tostring(noteindex)
data.footnotes[noteindex] = "Old Latin."
end
end
make_conjugation["2nd"] = function(args, data, typeinfo)
set_conj_categories("ที่ 2", data, typeinfo)
local pres_stem = typeinfo.pres_stem
make_pres(data, typeinfo, pres_stem)
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, typeinfo.supine_stem)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
-- Additional forms in specific cases.
if pres_stem == "noc" then
add_form(data.forms, "3s_siga_actv_subj", "", "noxsīt", 2)
end
end
make_conjugation["3rd"] = function(args, data, typeinfo)
set_conj_categories("ที่ 3", data, typeinfo)
local pres_stem = typeinfo.pres_stem
make_pres(data, typeinfo, pres_stem)
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, typeinfo.supine_stem)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
-- Additional forms in specific cases.
if not pres_stem then
return
elseif toNFD(pres_stem):match(vowel .. MACRON .. "sc$") then
insert_if_not(data.categories, "Latin inchoative verbs")
end
if pres_stem:match("nōsc$") then
typeinfo.subtypes.poetsyncperf = true
elseif pres_stem == "ulcīsc" then
local forms = data.forms
local formval = forms["1s_sigf_actv_indc"]
if type(formval) ~= "table" then
formval = {formval}
end
forms["1s_sigf_actv_indc"] = formval
local noteindex = table_len(data.footnotes) + 1
for i, form in ipairs(formval) do
if form == "ulsō" then
add_form(forms, "1s_sigf_actv_indc", "ull", "ō", i + 1)
data.form_footnote_indices["1s_sigf_actv_indc"] = tostring(noteindex)
data.footnotes[noteindex] = 'The form \"ullō\" may have resulted from a later, erroneous misreading of \"ulsō\".'
break
end
end
end
end
make_conjugation["3rd-io"] = function(args, data, typeinfo)
set_conj_categories("ที่ 3", data, typeinfo)
local supine_stem, futr_actv_ptc_stem = typeinfo.supine_stem
if supine_stem then
local subtypes = typeinfo.subtypes
futr_actv_ptc_stem = make_futr_actv_ptc_stem(supine_stem)
for i, stem in ipairs(futr_actv_ptc_stem) do
-- TODO: generalise this as a subtype which is automatic for 3rd-io verbs with -rt in the supine
local unsync_stem = stem:gsub("rt$", "rit")
futr_actv_ptc_stem[i] = unsync_stem
if unsync_stem ~= stem then
subtypes.syncfutractvptc = true
end
end
typeinfo.futr_actv_ptc_stem = futr_actv_ptc_stem
end
make_pres(data, typeinfo, typeinfo.pres_stem)
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, supine_stem, futr_actv_ptc_stem)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
end
local function ivi_ive(form)
form = form:gsub("īvī", "iī")
form = form:gsub("īvi", "ī")
form = form:gsub("īve", "ī")
form = form:gsub("īvē", "ē")
return form
end
make_conjugation["4th"] = function(args, data, typeinfo)
set_conj_categories("ที่ 4", data, typeinfo)
local pres_stem = typeinfo.pres_stem
make_pres(data, typeinfo, pres_stem)
make_perf(data, typeinfo.perf_stem)
make_supine(data, typeinfo, typeinfo.supine_stem)
make_sigm(data, typeinfo, typeinfo.sigm_stem)
local forms = data.forms
if form_contains(forms["1s_pres_actv_indc"], "serviō") or form_contains(forms["1s_pres_actv_indc"], "saeviō") then
add_forms(forms, "impf_actv_indc", pres_stem,
{"iēbam", "ībam"},
{"iēbās", "ībās"},
{"iēbat", "ībat"},
{"iēbāmus", "ībāmus"},
{"iēbātis", "ībātis"},
{"iēbant", "ībant"}
)
add_forms(forms, "futr_actv_indc", pres_stem,
{"iam", "ībō"},
{"iēs", "ībis"},
{"iet", "ībit"},
{"iēmus", "ībimus"},
{"iētis", "ībitis"},
{"ient", "ībunt"}
)
end
local subtypes = typeinfo.subtypes
if not (subtypes.alwayssyncperf or subtypes.optsyncperf) then
return
end
for key, formval in pairs(forms) do
if cfind(key, "perf") or cfind(key, "plup") or cfind(key, "futp") then
if type(formval) ~= "table" then
formval = {formval}
end
forms[key] = {}
for _, f in ipairs(formval) do
if subtypes.optsyncperf then
insert_if_not(forms[key], f)
end
insert_if_not(forms[key], ivi_ive(f))
end
end
end
end
-- Irregular conjugations.
local irreg_conjugations = {}
make_conjugation["irreg"] = function(args, data, typeinfo)
local verb = ine(args[1])
local prefix = ine(args[2])
if verb == nil then
error("The verb to be conjugated has not been specified.")
elseif not irreg_conjugations[verb] then
error(("Internal error: the irregular verb %s does not have a conjugation function specified."):format(dump(verb)))
end
typeinfo.prefix = prefix
-- Generate the verb forms
irreg_conjugations[verb](args, data, typeinfo)
end
for _, verb in ipairs{"aiō", "aiiō", "aijō", "ajō"} do
local stem = usub(verb, 1, -2)
local i_stem = stem:gsub("j$", "i")
irreg_conjugations[verb] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "Latin active-only verbs")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
subtypes.highlydef = true
subtypes.nopass = true
subtypes.nosup = true
local prefix = typeinfo.prefix or ""
forms["1s_pres_actv_indc"] = prefix .. stem .. "ō"
forms["2s_pres_actv_indc"] = prefix .. i_stem .. "s"
forms["3s_pres_actv_indc"] = prefix .. i_stem .. "t"
forms["3p_pres_actv_indc"] = prefix .. stem .. "unt"
forms["1s_impf_actv_indc"] = prefix .. stem .. "ēbam"
forms["2s_impf_actv_indc"] = prefix .. stem .. "ēbās"
forms["3s_impf_actv_indc"] = prefix .. stem .. "ēbat"
forms["1p_impf_actv_indc"] = prefix .. stem .. "ēbāmus"
forms["2p_impf_actv_indc"] = prefix .. stem .. "ēbātis"
forms["3p_impf_actv_indc"] = prefix .. stem .. "ēbant"
forms["2s_perf_actv_indc"] = prefix .. i_stem .. "stī"
forms["3s_perf_actv_indc"] = prefix .. i_stem .. "t"
forms["3p_perf_actv_indc"] = prefix .. stem .. "ērunt"
forms["2s_pres_actv_subj"] = prefix .. stem .. "ās"
forms["3s_pres_actv_subj"] = prefix .. stem .. "at"
forms["3p_pres_actv_subj"] = prefix .. stem .. "ant"
forms["2s_pres_actv_impr"] = prefix .. i_stem
forms["pres_actv_inf"] = prefix .. stem .. "ere"
forms["pres_actv_ptc"] = prefix .. stem .. "ēns"
end
end
irreg_conjugations["cedo"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
subtypes.imponly = true
subtypes.nofut = true
subtypes.nopass = true
subtypes.noperf = true
subtypes.nosup = true
local prefix = typeinfo.prefix or ""
forms["2s_pres_actv_impr"] = prefix .. "cedo"
forms["2p_pres_actv_impr"] = {prefix .. "cette", prefix .. "cedite"}
end
irreg_conjugations["dīcō"] = function(args, data, typeinfo)
local categories, forms = data.categories, data.forms
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
local prefix = typeinfo.prefix or ""
make_pres(data, typeinfo, prefix .. "dīc")
make_perf(data, prefix .. "dīx")
make_supine(data, typeinfo, prefix .. "dict")
make_sigm(data, typeinfo, prefix .. "dīx")
-- Old Latin regular imperative.
local noteindex = table_len(data.footnotes) + 1
add_form(forms, "2s_pres_actv_impr", prefix, "dīc", 1)
data.form_footnote_indices["2s_pres_actv_impr"] = tostring(noteindex)
-- Old Latin future forms.
if prefix == "" then
add_form(forms, "1s_futr_actv_indc", "", "dīcēbō", 2)
add_form(forms, "3s_futr_actv_indc", "", "dīcēbit", 2)
data.form_footnote_indices["1s_futr_actv_indc"] = tostring(noteindex)
data.form_footnote_indices["3s_futr_actv_indc"] = tostring(noteindex)
end
data.footnotes[noteindex] = "Old Latin."
end
irreg_conjugations["dō"] = function(args, data, typeinfo)
local categories, forms = data.categories, data.forms
insert_if_not(categories, "คำกริยากลุ่มที่ 1 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
local prefix = typeinfo.prefix or ""
typeinfo.subtypes.shorta = true
make_pres(data, typeinfo, prefix .. "d")
make_perf(data, prefix .. "ded")
make_supine(data, typeinfo, prefix .. "dat")
end
irreg_conjugations["dūcō"] = function(args, data, typeinfo)
local categories, forms = data.categories, data.forms
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
local prefix = typeinfo.prefix or ""
make_pres(data, typeinfo, prefix .. "dūc")
make_perf(data, prefix .. "dūx")
make_supine(data, typeinfo, prefix .. "duct")
make_sigm(data, typeinfo, prefix .. "dūx")
add_form(forms, "2s_pres_actv_impr", prefix, "dūc", 1)
end
irreg_conjugations["edō"] = function(args, data, typeinfo)
local categories, forms = data.categories, data.forms
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
local prefix = typeinfo.prefix or ""
make_pres(data, typeinfo, prefix .. "ed")
make_perf(data, prefix .. "ēd")
make_supine(data, typeinfo, prefix .. "ēs")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", prefix, nil, "ēs", "ēst", nil, "ēstis", nil)
-- Passive imperfective indicative
add_form(forms, "3s_pres_pasv_indc", prefix, "ēstur")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", prefix, "edim", "edīs", "edit", "edīmus", "edītis", "edint")
add_forms(forms, "impf_actv_subj", prefix, "ēssem", "ēssēs", "ēsset", "ēssēmus", "ēssētis", "ēssent")
-- Active imperative
add_2_forms(forms, "pres_actv_impr", prefix, "ēs", "ēste")
add_23_forms(forms, "futr_actv_impr", prefix, "ēstō", "ēstō", "ēstōte", nil)
-- Present infinitives
add_form(forms, "pres_actv_inf", prefix, "ēsse")
end
irreg_conjugations["eō"] = function(args, data, typeinfo)
local forms = data.forms
insert_if_not(data.categories, "คำกริยาไม่ปรกติภาษาละติน")
local prefix = typeinfo.prefix or ""
make_perf(data, prefix .. "i")
make_supine(data, typeinfo, prefix .. "it")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", prefix, "eō", "īs", "it", "īmus", "ītis",
prefix == "prōd" and {"eunt", "īnunt"} or "eunt")
add_forms(forms, "impf_actv_indc", prefix, "ībam", "ībās", "ībat", "ībāmus", "ībātis", "ībant")
add_forms(forms, "futr_actv_indc", prefix, "ībō", "ībis", "ībit", "ībimus", "ībitis", "ībunt")
-- Active perfective indicative
add_form(forms, "1s_perf_actv_indc", prefix, "īvī")
forms["2s_perf_actv_indc"] = {prefix .. "īstī", prefix .. "īvistī"}
add_form(forms, "3s_perf_actv_indc", prefix, "īvit")
forms["2p_perf_actv_indc"] = prefix .. "īstis"
-- Passive imperfective indicative
add_forms(forms, "pres_pasv_indc", prefix, "eor", {"īris", "īre"}, "ītur", "īmur", "īminī", "euntur")
add_forms(forms, "impf_pasv_indc", prefix, "ībar", {"ībāris", "ībāre"}, "ībātur", "ībāmur", "ībāminī", "ībantur")
add_forms(forms, "futr_pasv_indc", prefix, "ībor", {"īberis", "ībere"}, "ībitur", "ībimur", "ībiminī", "ībuntur")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", prefix, "eam", "eās", "eat", "eāmus", "eātis", "eant")
add_forms(forms, "impf_actv_subj", prefix, "īrem", "īrēs", "īret", "īrēmus", "īrētis", "īrent")
-- Active perfective subjunctive
forms["1s_plup_actv_subj"] = prefix .. "īssem"
forms["2s_plup_actv_subj"] = prefix .. "īssēs"
forms["3s_plup_actv_subj"] = prefix .. "īsset"
forms["1p_plup_actv_subj"] = prefix .. "īssēmus"
forms["2p_plup_actv_subj"] = prefix .. "īssētis"
forms["3p_plup_actv_subj"] = prefix .. "īssent"
-- Passive imperfective subjunctive
add_forms(forms, "pres_pasv_subj", prefix, "ear", {"eāris", "eāre"}, "eātur", "eāmur", "eāminī", "eantur")
add_forms(forms, "impf_pasv_subj", prefix, "īrer", {"īrēris", "īrēre"}, "īrētur", "īrēmur", "īrēminī", "īrentur")
-- Imperative
add_2_forms(forms, "pres_actv_impr", prefix, "ī", "īte")
add_23_forms(forms, "futr_actv_impr", prefix, "ītō", "ītō", "ītōte", "euntō")
add_2_forms(forms, "pres_pasv_impr", prefix, "īre", "īminī")
add_23_forms(forms, "futr_pasv_impr", prefix, "ītor", "ītor", nil, "euntor")
-- Present infinitives
forms["pres_actv_inf"] = prefix .. "īre"
forms["pres_pasv_inf"] = prefix .. "īrī"
-- Perfect/future infinitives
forms["perf_actv_inf"] = prefix .. "īsse"
-- Imperfective participles
forms["pres_actv_ptc"] = prefix .. "iēns"
-- Gerund
make_gerund(data, typeinfo, prefix .. "eund")
end
local function fio(forms, prefix, voice)
-- Active/passive imperfective indicative
add_forms(forms, "pres_" .. voice .. "_indc", prefix,
"fīō", "fīs", "fit", "fīmus", "fītis", "fīunt")
add_forms(forms, "impf_" .. voice .. "_indc", prefix .. "fīēb",
"am", "ās", "at", "āmus", "ātis", "ant")
add_forms(forms, "futr_" .. voice .. "_indc", prefix .. "fī",
"am", "ēs", "et", "ēmus", "ētis", "ent")
-- Active/passive imperfective subjunctive
add_forms(forms, "pres_" .. voice .. "_subj", prefix .. "fī",
"am", "ās", "at", "āmus", "ātis", "ant")
add_forms(forms, "impf_" .. voice .. "_subj", prefix .. "fier",
"em", "ēs", "et", "ēmus", "ētis", "ent")
-- Active/passive imperative
add_2_forms(forms, "pres_" .. voice .. "_impr", prefix .. "fī", "", "te")
add_23_forms(forms, "futr_" .. voice .. "_impr", prefix .. "fī", "tō", "tō", "tōte", "untō")
-- Active/passive present infinitive
add_form(forms, "pres_" .. voice .. "_inf", prefix, "fierī")
end
irreg_conjugations["faciō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน") -- suppletion = การเสริมต่างรูป
subtypes.suppl = true
local prefix = typeinfo.prefix or ""
make_pres(data, typeinfo, prefix .. "fac")
-- Remove passive forms, except the gerund and future passive participle.
remove_forms(forms, {"pasv_indc", "pasv_subj", "pasv_impr", "pasv_inf"})
make_perf(data, prefix .. "fēc")
make_supine(data, typeinfo, prefix .. "fact")
make_sigm(data, typeinfo, prefix .. "fax")
if prefix == "" then
local noteindex = table_len(data.footnotes) + 1
add_form(forms, "3s_perf_actv_indc", "", "fēced")
data.form_footnote_indices["3s_perf_actv_indc"] = tostring(noteindex)
data.footnotes[noteindex] = "Old Latin."
-- Active imperative
add_form(forms, "2s_pres_actv_impr", "", "fac", 1)
-- Sigmatic forms
add_form(forms, "1s_sigf_actv_indc", "", {"faxsō", "facsō", "faxiō"})
add_form(forms, "2s_sigf_actv_indc", "", {"faxsis", "facsis", "facxis", "facxsis"})
add_form(forms, "3s_sigf_actv_indc", "", "faxsit")
add_form(forms, "1p_sigf_actv_indc", "", "faxsimus")
add_form(forms, "2p_sigf_actv_indc", "", "faxsitis")
add_form(forms, "3p_sigf_actv_indc", "", "faxsint")
add_form(forms, "1s_siga_actv_subj", "", {"faxsim", "faxēm"})
add_form(forms, "2s_siga_actv_subj", "", {"faxsīs", "faxseis", "faxeis", "faxēs"})
add_form(forms, "3s_siga_actv_subj", "", {"faxsīt", "faxeit", "faxēt"})
add_form(forms, "1p_siga_actv_subj", "", {"faxsīmus", "faxeimus"})
add_form(forms, "2p_siga_actv_subj", "", {"faxsītis", "faxeitis"})
add_form(forms, "3p_siga_actv_subj", "", {"faxsint", "faxēnt"})
end
fio(forms, prefix, "pasv")
end
irreg_conjugations["ferō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
subtypes.suppl = true
local prefix_pres = typeinfo.prefix or ""
local prefix_perf = ine(args[3])
local prefix_supine = ine(args[4])
prefix_perf = prefix_perf or prefix_pres
prefix_supine = prefix_supine or prefix_pres
make_pres(data, typeinfo, prefix_pres .. "fer")
if prefix_perf == "" then
make_perf(data, {"tul", "tetul"})
else
make_perf(data, prefix_perf .. "tul")
end
make_supine(data, typeinfo, prefix_supine .. "lāt")
-- Active imperfective indicative
forms["2s_pres_actv_indc"] = prefix_pres .. "fers"
forms["3s_pres_actv_indc"] = prefix_pres .. "fert"
forms["2p_pres_actv_indc"] = prefix_pres .. "fertis"
-- Passive imperfective indicative
forms["3s_pres_pasv_indc"] = prefix_pres .. "fertur"
-- Active imperfective subjunctive
forms["1s_impf_actv_subj"] = prefix_pres .. "ferrem"
forms["2s_impf_actv_subj"] = prefix_pres .. "ferrēs"
forms["3s_impf_actv_subj"] = prefix_pres .. "ferret"
forms["1p_impf_actv_subj"] = prefix_pres .. "ferrēmus"
forms["2p_impf_actv_subj"] = prefix_pres .. "ferrētis"
forms["3p_impf_actv_subj"] = prefix_pres .. "ferrent"
-- Passive present indicative
forms["2s_pres_pasv_indc"] = {prefix_pres .. "ferris", prefix_pres .. "ferre"}
-- Passive imperfective subjunctive
forms["1s_impf_pasv_subj"] = prefix_pres .. "ferrer"
forms["2s_impf_pasv_subj"] = {prefix_pres .. "ferrēris", prefix_pres .. "ferrēre"}
forms["3s_impf_pasv_subj"] = prefix_pres .. "ferrētur"
forms["1p_impf_pasv_subj"] = prefix_pres .. "ferrēmur"
forms["2p_impf_pasv_subj"] = prefix_pres .. "ferrēminī"
forms["3p_impf_pasv_subj"] = prefix_pres .. "ferrentur"
-- Imperative
forms["2s_pres_actv_impr"] = prefix_pres .. "fer"
forms["2p_pres_actv_impr"] = prefix_pres .. "ferte"
forms["2s_futr_actv_impr"] = prefix_pres .. "fertō"
forms["3s_futr_actv_impr"] = prefix_pres .. "fertō"
forms["2p_futr_actv_impr"] = prefix_pres .. "fertōte"
forms["2s_pres_pasv_impr"] = prefix_pres .. "ferre"
forms["2s_futr_pasv_impr"] = prefix_pres .. "fertor"
forms["3s_futr_pasv_impr"] = prefix_pres .. "fertor"
-- Present infinitives
forms["pres_actv_inf"] = prefix_pres .. "ferre"
forms["pres_pasv_inf"] = prefix_pres .. "ferrī"
if prefix_perf == "" then
local noteindex = table_len(data.footnotes) + 1
for slot, form in pairs(forms) do
if type(form) == "table" then
for _, f in ipairs(form) do
if f:match("^tetul") then
data.form_footnote_indices[slot] = tostring(noteindex)
data.footnotes[noteindex] = "Old Latin."
break
end
end
end
end
end
end
irreg_conjugations["fīō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยากลุ่มที่ 3 ภาษาละติน")
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
subtypes.semidepon = true
subtypes.nofutractvptc = true
if not subtypes.nosup then
subtypes.suppl = true
end
local prefix = typeinfo.prefix or ""
fio(forms, prefix, "actv")
make_supine(data, typeinfo, prefix .. "fact")
forms["futr_actv_inf"] = forms["futr_pasv_inf"]
-- Imperfective participles
forms["pres_actv_ptc"] = nil
-- Gerund
make_gerund(data, typeinfo, prefix .. "fiend", "und-variant")
end
irreg_conjugations["inquam"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
subtypes.highlydef = true
forms["1s_pres_actv_indc"] = "inquam"
forms["2s_pres_actv_indc"] = "inquis"
forms["3s_pres_actv_indc"] = "inquit"
forms["1p_pres_actv_indc"] = "inquimus"
forms["2p_pres_actv_indc"] = "inquitis"
forms["3p_pres_actv_indc"] = "inquiunt"
forms["2s_futr_actv_indc"] = "inquiēs"
forms["3s_futr_actv_indc"] = "inquiet"
forms["3p_futr_actv_indc"] = "inquient"
forms["3s_impf_actv_indc"] = "inquiēbat"
forms["1s_perf_actv_indc"] = "inquiī"
forms["2s_perf_actv_indc"] = "inquistī"
forms["3s_perf_actv_indc"] = "inquit"
forms["2s_pres_actv_subj"] = "inquiās"
forms["3s_pres_actv_subj"] = "inquiat"
forms["3p_pres_actv_subj"] = "inquiant"
forms["2s_pres_actv_impr"] = "inque"
forms["2s_futr_actv_impr"] = "inquitō"
forms["3s_futr_actv_impr"] = "inquitō"
forms["pres_actv_ptc"] = "inquiēns"
local noteindex = table_len(data.footnotes) + 1
data.footnotes[noteindex] = "Medieval Latin."
noteindex = tostring(noteindex)
data.form_footnote_indices["3p_futr_actv_indc"] = noteindex
data.form_footnote_indices["2s_pres_actv_subj"] = noteindex
data.form_footnote_indices["3p_pres_actv_subj"] = noteindex
end
-- Handle most forms of volō, mālō, nōlō.
local function volo_malo_nolo(forms, indc_stem, subj_stem, subtypes)
-- Present active indicative needs to be done individually as each
-- verb is different.
add_forms(forms, "impf_actv_indc", indc_stem .. "ēb", "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(forms, "futr_actv_indc", indc_stem, "am", "ēs", "et", "ēmus", "ētis", "ent")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", subj_stem, "im", "īs", "it", "īmus", "ītis", "int")
add_forms(forms, "impf_actv_subj", subj_stem .. "l", "em", "ēs", "et", "ēmus", "ētis", "ent")
-- Present infinitives
forms["pres_actv_inf"] = subj_stem .. "le"
-- Imperfective participles
forms["pres_actv_ptc"] = indc_stem .. "ēns"
subtypes.suppl = true
end
irreg_conjugations["mālō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
subtypes.nopass = true
subtypes.noimp = true
make_perf(data, "mālu")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", "", "mālō", "māvīs", "māvult", "mālumus", "māvultis", "mālunt")
volo_malo_nolo(forms, "māl", "māl", subtypes)
end
irreg_conjugations["nōlō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
subtypes.nopass = true
make_perf(data, "nōlu")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", "", "nōlō", "nōn vīs", "nōn vult", "nōlumus", "nōn vultis", "nōlunt")
add_forms(forms, "impf_actv_indc", "nōlēb", "am", "ās", "at", "āmus", "ātis", "ant")
volo_malo_nolo(forms, "nōl", "nōl", subtypes)
-- Imperative
add_2_forms(forms, "pres_actv_impr", "nōlī", "", "te")
add_23_forms(forms, "futr_actv_impr", "nōl", "itō", "itō", "itōte", "untō")
-- Additional Old Latin forms.
local noteindex = table_len(data.footnotes) + 1
data.footnotes[noteindex] = "Old Latin."
noteindex = tostring(noteindex)
add_forms(forms, "pres_actv_indc", "", nil, "nēvīs", {"nōn volt", "nēvolt"}, nil, {"nōn voltis", "nōltis"}, nil)
data.form_footnote_indices["2s_pres_actv_indc"] = noteindex
data.form_footnote_indices["3s_pres_actv_indc"] = noteindex
data.form_footnote_indices["2p_pres_actv_indc"] = noteindex
end
irreg_conjugations["possum"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
subtypes.suppl = true
subtypes.passimpers = true
subtypes.passimpersold = true
make_perf(data, "potu")
-- Active imperfective indicative, impersonal passive
add_forms(forms, "pres_actv_indc", "", "possum", "potes", "potest", "possumus", "potestis", "possunt")
add_forms(forms, "impf_actv_indc", "poter", "am", "ās", "at", "āmus", "ātis", "ant")
add_forms(forms, "futr_actv_indc", "poter", "ō", {"is", "e"}, "it", "imus", "itis", "unt")
forms["3s_pres_pasv_indc"] = "potestur"
forms["3s_impf_pasv_indc"] = "poterātur"
forms["3s_futr_pasv_indc"] = "poteritur"
-- Active imperfective subjunctive, impersonal passive
add_forms(forms, "pres_actv_subj", "poss", "im", "īs", "it", "īmus", "ītis", "int")
add_forms(forms, "impf_actv_subj", "poss", "em", "ēs", "et", "ēmus", "ētis", "ent")
forms["3s_pres_pasv_subj"] = "possītur"
forms["3s_impf_pasv_subj"] = "possētur"
-- Present infinitives
forms["pres_actv_inf"] = {"posse", "potesse"}
-- Imperfective participles
forms["pres_actv_ptc"] = "potēns"
local noteindex = table_len(data.footnotes) + 1
data.footnotes[noteindex] = "Old Latin. The passive forms are only used to govern a passive infinitive; e.g. " .. mw.getCurrentFrame():preprocess("{{uxi|la|nec retrahi '''potestur''' imperiis|t=\"nor '''can it''' be withdrawn by commands\"}}") .. "."
noteindex = tostring(noteindex)
for slot in pairs(data.forms) do
if cfind(slot, "pasv") then
data.form_footnote_indices[slot] = noteindex
end
end
data.form_footnote_indices["pres_actv_inf"] = noteindex
end
-- The vowel of the prefix is lengthened if it ends in -n and the next word begins with f- or s-.
local function shorten_prefix(prefix)
return toNFC((ugsub(toNFD(prefix), "(" .. vowel .. ")" .. MACRON .. "n$", "%1n")))
end
local function lengthen_prefix(prefix)
return toNFC((ugsub(toNFD(prefix), "(" .. vowel .. ")" .. LENGTH .. "n$", "%1" .. MACRON .. "n")))
end
irreg_conjugations["sum"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
local prefix = typeinfo.prefix or ""
local prefix_e = ine(args[3]) or shorten_prefix(prefix)
local prefix_f = ine(args[4]) or lengthen_prefix(prefix)
local prefix_s = lengthen_prefix(prefix)
subtypes.suppl = true
subtypes.nopass = true
subtypes.supfutractvonly = true
make_perf(data, prefix_f .. "fu")
make_supine(data, typeinfo, prefix_f .. "fut")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", prefix_s, "sum", nil, nil, "sumus", nil, "sunt")
add_forms(forms, "pres_actv_indc", prefix_e, nil, "es", "est", nil, "estis", nil)
add_forms(forms, "impf_actv_indc", prefix_e, "eram", "erās", "erat", "erāmus", "erātis", "erant")
add_forms(forms, "futr_actv_indc", prefix_e, "erō", {"eris", "ere"}, "erit", "erimus", "eritis", "erunt")
-- Active imperfective subjunctive
add_forms(forms, "pres_actv_subj", prefix_s, "sim", "sīs", "sit", "sīmus", "sītis", "sint")
add_forms(forms, "pres_actv_subj", prefix_s, "siem", "siēs", "siet", "siēmus", "siētis", "sient")
add_forms(forms, "pres_actv_subj", prefix_f, "fuam", "fuās", "fuat", "fuāmus", "fuātis", "fuant")
local noteindex = table_len(data.footnotes) + 1
for _, p in ipairs(persons) do
data.form_footnote_indices[p .. "_pres_actv_subj"] = tostring(noteindex)
end
data.footnotes[noteindex] = "Old Latin or in poetry."
if prefix_s == "ad" then
local noteindex = table_len(data.footnotes) + 1
add_form(forms, "3p_pres_actv_subj", "", "adessint", 2)
data.form_footnote_indices["3p_pres_actv_subj"] = tostring(noteindex)
data.footnotes[noteindex] = "Old Latin."
end
add_forms(forms, "impf_actv_subj", prefix_e, "essem", "essēs", "esset", "essēmus", "essētis", "essent")
add_forms(forms, "impf_actv_subj", prefix_f, "forem", "forēs", "foret", "forēmus", "forētis", "forent")
-- Imperative
add_2_forms(forms, "pres_actv_impr", prefix_e, "es", "este")
add_23_forms(forms, "futr_actv_impr", prefix_e, "estō", "estō", "estōte", nil)
add_form(forms, "3p_futr_actv_impr", prefix_s, "suntō")
-- Infinitives
add_form(forms, "pres_actv_inf", prefix_e, "esse")
add_form(forms, "futr_actv_inf", prefix_f, "fore")
-- Imperfective participles
if prefix == "ab" or prefix == "cōn" or prefix == "prae" or prefix == "præ" then
add_form(forms, "pres_actv_ptc", prefix_s, "sēns")
-- forms["pres_actv_ptc"] = prefix_s .. "sēns"
end
end
irreg_conjugations["volō"] = function(args, data, typeinfo)
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
insert_if_not(categories, "คำกริยาไม่ปรกติภาษาละติน")
insert_if_not(categories, "คำกริยาเสริมต่างรูปภาษาละติน")
local prefix = typeinfo.prefix or ""
subtypes.nopass = true
subtypes.noimp = true
subtypes.supfutractvonly = true
make_perf(data, prefix .. "volu")
make_supine(data, typeinfo, prefix .. "volit")
-- Active imperfective indicative
add_forms(forms, "pres_actv_indc", prefix, "volō", "vīs", "vult", "volumus", "vultis", "volunt")
volo_malo_nolo(forms, prefix .. "vol", prefix .. "vel", subtypes)
-- Additional Old Latin forms.
if prefix == "" then
local noteindex = table_len(data.footnotes) + 1
data.footnotes[noteindex] = "Old Latin."
noteindex = tostring(noteindex)
add_forms(forms, "pres_actv_indc", prefix, nil, nil, "volt", nil, "voltis", nil)
data.form_footnote_indices["3s_pres_actv_indc"] = noteindex
data.form_footnote_indices["2p_pres_actv_indc"] = noteindex
-- vol- in the present subjunctive.
add_forms(forms, "pres_actv_subj", prefix .. "vol", "im", "īs", "it", "īmus", "ītis", "int")
for slot in pairs(data.forms) do
if cfind(slot, "pres_actv_subj") then
data.form_footnote_indices[slot] = noteindex
end
end
end
end
local function make_perfect_passive(data)
local forms = data.forms
local ppp = forms["perf_pasv_ptc"]
if type(ppp) ~= "table" then
ppp = {ppp}
end
local ppplinks = {}
for _, pppform in ipairs(ppp) do
insert(ppplinks, make_link(pppform, nil, "term"))
end
local ppplink = concat(ppplinks, " or ")
local sumlink = make_link("sum", nil, "term")
local text_for_slot = {
perf_pasv_indc = "present active indicative",
futp_pasv_indc = "future active indicative",
plup_pasv_indc = "imperfect active indicative",
perf_pasv_subj = "present active subjunctive",
plup_pasv_subj = "imperfect active subjunctive"
}
local passive_prefix, passive_suffix = data.passive_prefix, data.passive_suffix
local prefix_joiner = passive_prefix and passive_prefix:match(" $") and "+ " or ""
local suffix_joiner = passive_suffix and passive_suffix:match("^ ") and " +" or ""
for slot, text in pairs(text_for_slot) do
forms[slot] = (passive_prefix or "") .. prefix_joiner .. ppplink .. " + " ..
text .. " of " .. sumlink .. suffix_joiner .. (passive_suffix or "")
end
ppp = forms["1s_pres_actv_indc"]
if type(ppp) ~= "table" then
ppp = {ppp}
end
end
local function handle_deponent(data, subtypes)
local forms = data.forms
-- Generate a list of form types to remove.
local to_remove = {"pres_pasv_ptc", "futr_pasv_inf"}
if subtypes.depon then
insert(to_remove, "actv")
else
insert(to_remove, "pres_pasv")
insert(to_remove, "impf_pasv")
insert(to_remove, "futr_pasv")
if subtypes.semidepon then
insert(to_remove, "perf_actv")
insert(to_remove, "plup_actv")
insert(to_remove, "futp_actv")
end
end
-- Remove forms.
for slot in pairs(forms) do
if not (
slot == "futr_actv_inf" or
slot == "perp_actv_inf" or
slot == "pres_actv_ptc" or
slot == "futr_actv_ptc" or
slot == "futr_pasv_ptc"
) then
for _, part in ipairs(to_remove) do
if cfind(slot, part) then
forms[slot] = nil
break
end
end
end
end
-- Change passive to active, except for the future passive participle.
local changes = {}
for slot in pairs(forms) do
if cfind(slot, "pasv") and slot ~= "futr_pasv_ptc" then
changes[slot] = slot:gsub("pasv", "actv")
end
end
for old, new in pairs(changes) do
add_form(forms, new, "", forms[old])
forms[old] = nil
end
end
local function add_sync_perf(data, form, suf, note, noteindex)
local forms = data.forms
local formval = forms[form]
if type(formval) ~= "table" then
formval = {formval}
end
local i = 0
while true do
i = i + 1
local stem = formval[i]
if not stem then
break
end
stem = stem:match("^(.*)" .. pattern_escape(suf) .. "$")
-- Can only syncopate "vi", "ve", "vē", or any one of them spelled with a "u" after a vowel.
if stem then
local new_stem = toNFD(stem)
local vowel, loc, v, ei, diacritic = umatch(new_stem, "(" .. vowel .. "?)" .. LENGTH .. "()([uv])([ei])(" .. LENGTH .. ")$")
if loc and (v == "v" or vowel ~= "") and (ei == "e" or diacritic == "" or cfind(diacritic, BREVE)) then
new_stem = usub(new_stem, 1, loc - 1)
-- Can't be omitted if it's part of the first syllable.
if new_stem:match(vowel) then
i = i + 1
add_form(forms, form, new_stem, suf, i)
data.form_footnote_indices[form] = tostring(noteindex)
data.footnotes[noteindex] = note
end
end
end
end
end
local function has_no_infinitive(forms)
for slot in pairs(forms) do
if cfind(slot, "inf") and not form_is_empty(forms[slot]) then
return false
end
end
return true
end
local function has_no_gerund(forms)
if not form_is_empty(forms["futr_pasv_ptc"]) then
return false
end
for _, case in ipairs(ger_cases) do
if not form_is_empty(forms[case .. "_ger"]) then
return false
end
end
return true
end
local make_conjugation_mt = {}
function make_conjugation_mt:__call(args, data, typeinfo)
if typeinfo.irreg then
self.irreg(args, data, typeinfo)
else
get_regular_stems(args, typeinfo)
self[typeinfo.conj](args, data, typeinfo)
end
local categories, forms, subtypes = data.categories, data.forms, typeinfo.subtypes
if subtypes.nopres or subtypes.perfaspres then
insert_if_not(categories, "Latin verbs with missing present stem")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
end
-- Maybe clear out the supine-derived forms (except maybe for the
-- future active participle). Do this first because some code below
-- looks at the perfect participle to derive other forms.
if subtypes.nosup then
-- Some verbs have no supine forms or forms derived from the supine
insert_if_not(categories, "Latin verbs with missing supine stem")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
forms["futr_actv_inf"] = nil
forms["futr_actv_ptc"] = nil
forms["perf_actv_ptc"] = nil
forms["futp_actv_inf"] = nil
forms["perp_actv_inf"] = nil
forms["perf_pasv_inf"] = nil
forms["perf_pasv_ptc"] = nil
forms["futr_pasv_inf"] = nil
forms["futp_pasv_inf"] = nil
if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
forms["perf_actv_inf"] = nil
end
for _, case in ipairs(sup_cases) do
forms[case .. "_sup"] = nil
end
elseif subtypes.nofutractvptc then
-- Some verbs have a missing future active participle
insert_if_not(categories, "Latin verbs with supine stem with missing future active participle")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
forms["futr_actv_ptc"] = nil
forms["perp_actv_inf"] = nil
elseif subtypes.supfutractvonly then
-- Some verbs have no supine forms or forms derived from the supine,
-- except for the future active infinitive/participle
insert_if_not(categories, "Latin verbs with missing supine stem except in the future active participle")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
forms["perf_actv_ptc"] = nil
forms["futp_actv_inf"] = nil
forms["perf_pasv_inf"] = nil
forms["perf_pasv_ptc"] = nil
forms["futr_pasv_inf"] = nil
forms["futp_pasv_inf"] = nil
if subtypes.depon or subtypes.semidepon or subtypes.optsemidepon then
forms["perf_actv_inf"] = nil
end
for _, case in ipairs(sup_cases) do
forms[case .. "_sup"] = nil
end
end
-- Add information for the passive perfective forms
if not (
form_is_empty(forms["perf_pasv_ptc"]) or
subtypes.optsemidepon or
subtypes.impers or
subtypes.passimpers and not (subtypes.depon or subtypes.semidepon or subtypes.optsemidepon) or
subtypes["3only"] or
subtypes.pass3only and not (subtypes.depon or subtypes.semidepon or subtypes.optsemidepon)
) then
make_perfect_passive(data)
end
-- Perfect forms as present tense.
if subtypes.perfaspres then
insert_if_not(categories, "Latin verbs with perfect forms used in the present tense")
-- Remove present active, imperfect active and future active forms and passive forms.
-- Change perfect forms to non-perfect forms.
local new_forms = {}
forms["futr_pasv_inf"] = nil
for slot, form in pairs(forms) do
if not (
cfind(slot, "pres") or
cfind(slot, "impf") or
cfind(slot, "futr") and not (cfind(slot, "inf") or cfind(slot, "ptc")) or
slot:match("_ger$")
) then
-- If semi-deponent, the perfect passive forms will be converted to perfect active.
if not (cfind(slot, "pasv") and (subtypes.semidepon or subtypes.optsemidepon)) then
slot = slot:gsub("perf", "pres")
:gsub("plup", "impf")
:gsub("futp", "futr")
end
new_forms[slot] = form
end
end
data.forms, forms = new_forms, new_forms
-- No present stem.
elseif subtypes.nopres then
for slot in pairs(forms) do
if (
cfind(slot, "pres") or
cfind(slot, "impf") or
cfind(slot, "futr") and not (cfind(slot, "inf") or cfind(slot, "ptc")) or
slot:match("_ger$")
) then
forms[slot] = nil
end
end
end
-- Types of irregularity related primarily to the active.
-- These could in theory be combined with those related to the passive and imperative,
-- i.e. there's no reason there couldn't be an impersonal deponent verb with no imperatives.
-- Deponent verbs use passive forms with active meaning.
if subtypes.depon then
insert_if_not(categories, "Latin deponent verbs")
handle_deponent(data, subtypes)
-- Semi-deponent verbs use perfective passive forms with active meaning,
-- and have no imperfective passive.
elseif subtypes.semidepon then
insert_if_not(categories, "Latin semi-deponent verbs")
handle_deponent(data, subtypes)
-- Optional semi-deponent verbs use perfective passive forms with active
-- meaning, but also have perfect active forms with the same meaning,
-- and have no imperfective passive.
elseif subtypes.optsemidepon then
insert_if_not(categories, "Latin semi-deponent verbs")
insert_if_not(categories, "Latin optionally semi-deponent verbs")
handle_deponent(data, subtypes)
elseif subtypes.noperf then
-- Some verbs have no perfect stem (e.g. inalbēscō, -ěre)
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
if not subtypes.depon then
insert_if_not(categories, "Latin verbs with missing perfect stem")
end
-- Remove all active perfect forms (passive perfect forms may
-- still exist as they are formed with the supine stem)
for slot in pairs(forms) do
if cfind(slot, "actv") and (
cfind(slot, "perf") or
cfind(slot, "plup") or
cfind(slot, "futp")
) and slot ~= "futp_actv_inf" then
forms[slot] = nil
end
end
end
-- Handle the removal of any passive forms, including any impersonal/3rd-
-- person-only passives.
-- Third-person only.
if subtypes["3only"] then
insert_if_not(categories, "Latin third-person-only verbs")
for slot in pairs(forms) do
if slot:match("[12][sp]") then
forms[slot] = nil
end
end
-- Impersonal, so remove all non-third-person singular forms.
elseif subtypes.impers then
insert_if_not(categories, "Latin impersonal verbs")
for slot in pairs(forms) do
if slot:match("[12][sp]") or (
-- Keep third-person plural forms if third-person passive is set.
cfind(slot, "3p") and
not (subtypes.pass3only and cfind(slot, "pasv"))
) then
forms[slot] = nil
end
end
end
-- No passive forms.
if subtypes.nopass then
insert_if_not(categories, "Latin active-only verbs")
for slot in pairs(forms) do
if cfind(slot, "pasv") then
forms[slot] = nil
end
end
-- Only third person in the passive. For phrasal verbs with a plural
-- complement, also need to erase the 3s forms.
-- FIXME: handling of mp, fp and np needs to be done separately.
elseif subtypes.pass3only then
insert_if_not(categories, "Latin verbs with third-person passive")
local gender = data.gender
for slot in pairs(forms) do
if cfind(slot, "pasv") and (
slot:match("[12][sp]") or
(gender == "mp" or gender == "fp" or gender == "np") and cfind(slot, "3s")
) then
forms[slot] = nil
end
end
-- Impersonal in the passive, so remove all passive non-third person forms.
elseif subtypes.passimpers then
insert_if_not(categories, "Latin verbs with impersonal passive")
for slot in pairs(forms) do
if cfind(slot, "pasv") and (
slot:match("[12][sp]") or
cfind(slot, "3p")
) then
forms[slot] = nil
end
end
end
-- Handle certain irregularities in the imperative
if subtypes.noimp then
-- Some verbs have no imperatives
insert_if_not(categories, "Latin verbs with missing imperative")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
-- Remove all imperative forms
for slot in pairs(forms) do
if cfind(slot, "impr") then
forms[slot] = nil
end
end
end
-- Handle certain irregularities in the future
if subtypes.nofut then
-- Some verbs (e.g. soleō) have no future
insert_if_not(categories, "Latin verbs with missing future")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
-- Remove all future forms
for slot in pairs(forms) do
if cfind(slot, "fut") then -- handles futr = future and futp = future perfect
forms[slot] = nil
end
end
end
local pres_stem = typeinfo.pres_stem
-- Add the ancient future_passive_participle of certain verbs
-- if pres_stem == "lāb" then
-- forms["futr_pasv_ptc"] = "lābundus"
-- elseif pres_stem == "collāb" then
-- forms["futr_pasv_ptc"] = "collābundus"
-- elseif pres_stem == "illāb" then
-- forms["futr_pasv_ptc"] = "illābundus"
-- elseif pres_stem == "relāb" then
-- forms["futr_pasv_ptc"] = "relābundus"
-- end
-- Add the poetic present passive infinitive forms of certain verbs
if subtypes.p3inf then
local form = "pres_" .. (subtypes.depon and "actv" or "pasv") .. "_inf"
local noteindex = table_len(data.footnotes) + 1
local formval = forms[form]
if type(formval) ~= "table" then
formval = {formval}
end
for i = 1, table_len(formval) do
insert(formval, usub(formval[i], 1, -2) .. "ier")
end
forms[form] = formval
data.form_footnote_indices[form] = tostring(noteindex)
data.footnotes[noteindex] = "The present passive infinitive in ''-ier'' is a rare poetic form which is attested."
end
--[==[
if subtypes.old2sgimp then
local form = "2s_pres_" .. (subtypes.depon and "actv" or "pasv") .. "_impr"
local formval = forms[form]
if type(formval) ~= "table" then
formval = {formval}
end
local newvals = mw.clone(formval)
end]==]
-- Add the syncopated perfect forms, omitting the separately handled fourth conjugation cases.
if subtypes.poetsyncperf then
local noteindex, note = table_len(data.footnotes) + 1
if pres_stem and pres_stem:match("nōsc$") then
note = 'The verb "nōscō" and its compounds frequently drop the syllables "vi" and "ve" from their perfect, pluperfect and future perfect conjugations.'
else
note = "At least one rare poetic syncopated perfect form is attested."
end
-- infinitive
add_sync_perf(data, "perf_actv_inf", "sse", note, noteindex)
-- perfect actives
add_sync_perf(data, "2s_perf_actv_indc", "stī", note, noteindex)
add_sync_perf(data, "3s_perf_actv_indc", "t", note, noteindex)
add_sync_perf(data, "1p_perf_actv_indc", "mus", note, noteindex)
add_sync_perf(data, "2p_perf_actv_indc", "stis", note, noteindex)
add_sync_perf(data, "3p_perf_actv_indc", "runt", note, noteindex)
-- pluperfect actives
add_sync_perf(data, "1s_plup_actv_indc", "ram", note, noteindex)
add_sync_perf(data, "2s_plup_actv_indc", "rās", note, noteindex)
add_sync_perf(data, "3s_plup_actv_indc", "rat", note, noteindex)
add_sync_perf(data, "1p_plup_actv_indc", "rāmus", note, noteindex)
add_sync_perf(data, "2p_plup_actv_indc", "rātis", note, noteindex)
add_sync_perf(data, "3p_plup_actv_indc", "rant", note, noteindex)
-- future perfect actives
add_sync_perf(data, "1s_futp_actv_indc", "rō", note, noteindex)
add_sync_perf(data, "2s_futp_actv_indc", "ris", note, noteindex)
add_sync_perf(data, "3s_futp_actv_indc", "rit", note, noteindex)
add_sync_perf(data, "1p_futp_actv_indc", "rimus", note, noteindex)
add_sync_perf(data, "2p_futp_actv_indc", "ritis", note, noteindex)
add_sync_perf(data, "3p_futp_actv_indc", "rint", note, noteindex)
-- perfect subjunctives
add_sync_perf(data, "1s_perf_actv_subj", "rim", note, noteindex)
add_sync_perf(data, "2s_perf_actv_subj", "rīs", note, noteindex)
add_sync_perf(data, "3s_perf_actv_subj", "rit", note, noteindex)
add_sync_perf(data, "1p_perf_actv_subj", "rīmus", note, noteindex)
add_sync_perf(data, "2p_perf_actv_subj", "rītis", note, noteindex)
add_sync_perf(data, "3p_perf_actv_subj", "rint", note, noteindex)
-- pluperfect subjunctives
add_sync_perf(data, "1s_plup_actv_subj", "ssem", note, noteindex)
add_sync_perf(data, "2s_plup_actv_subj", "ssēs", note, noteindex)
add_sync_perf(data, "3s_plup_actv_subj", "sset", note, noteindex)
add_sync_perf(data, "1p_plup_actv_subj", "ssēmus", note, noteindex)
add_sync_perf(data, "2p_plup_actv_subj", "ssētis", note, noteindex)
add_sync_perf(data, "3p_plup_actv_subj", "ssent", note, noteindex)
end
if subtypes.syncfutractvptc then
-- TODO
end
-- Add category for sigmatic forms
if subtypes.sigm then
insert_if_not(categories, "Latin verbs with sigmatic forms")
-- Add subcategory for (very rare) passive sigmatic forms
if subtypes.sigmpasv then
insert_if_not(categories, "Latin verbs with passive sigmatic forms")
end
end
if subtypes.noinf then
-- Some verbs (e.g. inquam) have no infinitives
for slot in pairs(forms) do
if cfind(slot, "inf") then
forms[slot] = nil
end
end
end
if has_no_infinitive(forms) then
subtypes.noinf = true
insert_if_not(categories, "Latin verbs with missing infinitive")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
end
if subtypes.noger then
-- Some verbs (e.g. libet) have no gerund
forms["futr_pasv_ptc"] = nil
for _, case in ipairs(ger_cases) do
forms[case .. "_ger"] = nil
end
end
-- Catch verbs which don't have .noger explicitly specified.
if has_no_gerund(forms) then
subtypes.noger = true
insert_if_not(categories, "Latin verbs with missing gerund")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
end
if subtypes.imponly then
-- Some verbs only have imperatives
insert_if_not(categories, "Latin imperative-only verbs")
insert_if_not(categories, "คำกริยาบกพร่องภาษาละติน")
-- Remove all non-imperative forms
for slot in pairs(forms) do
if not cfind(slot, "impr") then
forms[slot] = nil
end
end
end
end
setmetatable(make_conjugation, make_conjugation_mt)
-- Functions for generating the inflection table
-- Convert FORM (one or more forms) to a string of links. If the form is empty
-- (see form_is_empty), the return value will be "—".
local function show_form(formval, accel)
if not formval then
return "—"
elseif type(formval) ~= "table" then
formval = {formval}
elseif table_len(formval) == 0 then
return "—"
end
for slot, form in ipairs(formval) do
if form_is_empty(form) then
form = "—"
elseif form:match("%[%[.-]]") then
-- Don't put accelerators on forms already containing links such as
-- the perfect passive infinitive and future active infinitive, or
-- the participles wrongly get tagged as infinitives as well as
-- participles.
form = make_link(form)
else
form = make_link(form, nil, nil, accel)
end
formval[slot] = form
end
return concat(formval, ",<br> ")
end
local parts_to_tags = {
['1s'] = {'1', 's'},
['2s'] = {'2', 's'},
['3s'] = {'3', 's'},
['1p'] = {'1', 'p'},
['2p'] = {'2', 'p'},
['3p'] = {'3', 'p'},
['actv'] = {'act'},
['pasv'] = {'pass'},
['pres'] = {'pres'},
['impf'] = {'impf'},
['futr'] = {'fut'},
['perf'] = {'perf'},
['plup'] = {'plup'},
['futp'] = {'futp'},
['perp'] = {'perf', 'potn'},
['sigm'] = {'sigm'},
['sigf'] = {'sigm', 'fut'},
['siga'] = {'sigm', 'aor'},
['indc'] = {'ind'},
['subj'] = {'sub'},
['impr'] = {'imp'},
['inf'] = {'inf'},
['ptc'] = {'part'},
['ger'] = {'ger'},
['sup'] = {'sup'},
['nom'] = {'nom'},
['gen'] = {'gen'},
['dat'] = {'dat'},
['acc'] = {'acc'},
['abl'] = {'abl'},
}
-- Call show_form() the forms in each non-generic slot (where a
-- generic slot is something like pres_actv_indc that covers a whole
-- row of slots), converting the forms to a string consisting of
-- comma-separated links with accelerators in them.
local function convert_forms_into_links(data)
local accel_lemma, forms = data.actual_lemma[1], data.forms
for slot in iter_slots(false, false) do
local slot_parts = split(slot, "_")
local tags = {}
for _, part in ipairs(slot_parts) do
for _, tag in ipairs(parts_to_tags[part]) do
insert(tags, tag)
end
end
-- put the case first for verbal nouns
local accel = {form = concat(tags, "|"), lemma = accel_lemma}
forms[slot] = show_form(forms[slot], accel)
end
end
function export.get_valid_forms(raw_forms)
local valid_forms = {}
if raw_forms then
if type(raw_forms) ~= "table" then
raw_forms = {raw_forms}
end
for _, subform in ipairs(raw_forms) do
if not form_is_empty(subform) then
insert(valid_forms, subform)
end
end
end
return valid_forms
end
function export.get_lemma_forms(data, do_linked)
local linked_prefix, forms = do_linked and "linked_" or "", data.forms
for _, slot in ipairs(potential_lemma_slots) do
local lemma_forms = export.get_valid_forms(forms[linked_prefix .. slot])
if table_len(lemma_forms) > 0 then
return lemma_forms
end
end
return nil
end
local function get_displayable_lemma(lemma_forms)
if not lemma_forms then
return "—"
end
local lemma_links = {}
for _, subform in ipairs(lemma_forms) do
insert(lemma_links, make_link(nil, subform, "term"))
end
return concat(lemma_links, ", ")
end
local tenses_display = {
["pres"] = "present",
["impf"] = "imperfect",
["futr"] = "future",
["perf"] = "perfect",
["plup"] = "pluperfect",
["futp"] = "future perfect",
["perp"] = "perfect potential",
["sigf"] = "sigmatic future",
["siga"] = "sigmatic aorist"
}
local voices_display = {
["actv"] = "active",
["pasv"] = "passive",
}
local moods_display = {
["indc"] = "indicative",
["subj"] = "subjunctive",
["impr"] = "imperative",
}
--[[
local nonfins = {
["inf"] = "infinitives",
["ptc"] = "participles",
}
--]]
--[[
local verbalnouns = {
["ger"] = "gerund",
["sup"] = "supine",
}
--]]
--[[
local cases = {
["nom"] = "nominative",
["gen"] = "genitive",
["dat"] = "dative",
["acc"] = "accusative",
["abl"] = "ablative",
}
--]]
local function make_row(data, prefixes, headspan, cellspan, rowslot, rowname, class, fn)
local row = {}
if rowname then
insert(row, ('\n! class="%s"%s | %s%s'):format(class, headspan == 1 and "" or (' colspan="%d"'):format(headspan), rowname, fn or ""))
end
local has_row, forms = false, data.forms
if not form_is_empty(forms[rowslot]) then
if type(forms[rowslot]) == "table" then
if table_len(forms[rowslot]) > 1 then
error("Row slot " .. rowslot .. " cannot have more than one value.")
end
forms[rowslot] = forms[rowslot][1]
end
insert(row, '\n! colspan="12" class="roa-compound-row" |' .. forms[rowslot])
has_row = true
else
for _, prefix in ipairs(prefixes) do
local slot = prefix .. (rowslot and "_" .. rowslot or "")
insert(row, ('\n| colspan="%d" | '):format(cellspan) .. forms[slot] .. (
data.form_footnote_indices[slot] == nil and "" or
'<sup class="roa-red-superscript">' .. data.form_footnote_indices[slot] .. "</sup>"
))
if not form_is_empty(forms[slot]) then
has_row = true
end
end
end
return has_row and concat(row) or false
end
local function make_group(data, _tenses, voice, mood, class)
local group = {}
for _, tense in ipairs(_tenses) do
local fn = ""
if tense == "sigf" or tense == "siga" then
fn = data.form_footnote_indices["sigm"]
fn = fn and ('<sup class="roa-red-superscript">' .. fn .. "</sup>") or ""
end
local row = make_row(data, persons, 1, 2, tense .. "_" .. voice .. "_" .. mood, tenses_display[tense], class, fn)
if row then
insert(group, row)
end
end
return #group > 0 and ('\n|-\n! rowspan="' .. tostring(#group) .. '" class="' .. class .. '" | ' .. voices_display[voice] .. concat(group, "\n|-")) or false
end
local function make_section(data, mood, _tenses)
local section, name = {}, moods_display[mood]
local class = "roa-" .. name .. "-left-rail"
for _, voice in ipairs(voices) do
local group = make_group(data, _tenses, voice, mood, class)
if group then
insert(section, group)
end
end
if #section == 0 then
return ""
end
return (([=[
|-
! colspan="2" rowspan="2" class="%CLASS" | %NAME
! colspan="6" class="%CLASS" | ''singular''
! colspan="6" class="%CLASS" | ''plural''
|-
! colspan="2" class="%CLASS" style="width:12.5%;" | [[first person|first]]
! colspan="2" class="%CLASS" style="width:12.5%;" | [[second person|second]]
! colspan="2" class="%CLASS" style="width:12.5%;" | [[third person|third]]
! colspan="2" class="%CLASS" style="width:12.5%;" | [[first person|first]]
! colspan="2" class="%CLASS" style="width:12.5%;" | [[second person|second]]
! colspan="2" class="%CLASS" style="width:12.5%;" | [[third person|third]]]=]):gsub("%%%u+", function(m)
return m == "%NAME" and name or m == "%CLASS" and class or m
end)) .. concat(section)
end
local function make_indc_rows(data)
return make_section(data, "indc", indc_tenses)
end
local function make_subj_rows(data)
return make_section(data, "subj", subj_tenses)
end
local function make_impr_rows(data)
return make_section(data, "impr", impr_tenses)
end
local function get_nonfin_cells(t)
local cells = {}
for _, f in ipairs{"inf", "ptc"} do
for _, v in ipairs(voices) do
insert(cells, t .. "_" .. v .. "_" .. f)
end
end
return cells
end
local function make_nonfin_rows(data)
local nonfin = {}
for _, t in ipairs(nonfin_tenses) do
local row = make_row(data, get_nonfin_cells(t), 2, 3, nil, tenses_display[t], "roa-nonfinite-header")
if row then
insert(nonfin, row)
end
end
if #nonfin == 0 then
return ""
end
return [=[
|-
! colspan="2" rowspan="2" class="roa-nonfinite-header" | non-finite forms
! colspan="6" class="roa-nonfinite-header" | infinitive
! colspan="6" class="roa-nonfinite-header" | participle
|-
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | active
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | passive
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | active
! colspan="3" class="roa-nonfinite-header" style="width:18.75%;" | passive
|-]=] .. concat(nonfin, "\n|-")
end
local function make_vn_rows(data)
local vn = make_row(data, {"gen_ger", "dat_ger", "acc_ger", "abl_ger", "acc_sup", "abl_sup"}, nil, 2, nil, nil, "roa-nonfinite-header")
if not vn then
return ""
end
return [=[
|-
! colspan="2" rowspan="3" class="roa-nonfinite-header" | verbal nouns
! colspan="8" class="roa-nonfinite-header" | gerund
! colspan="4" class="roa-nonfinite-header" | supine
|-
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | genitive
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | dative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | accusative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | ablative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | accusative
! colspan="2" class="roa-nonfinite-header" style="width:12.5%;" | ablative
|-]=] .. vn
end
local function make_footnotes(data)
local tbl, i = {}, 0
for k, v in pairs(data.footnotes) do
i = i + 1
tbl[i] = '<sup class="roa-red-superscript">' .. tostring(k) .. '</sup>' .. v .. '<br>'
end
return concat(tbl)
end
-- Make the table
local function make_table(data)
data.actual_lemma = export.get_lemma_forms(data)
convert_forms_into_links(data)
local title = data.title
return require("Module:TemplateStyles")("Module:roa-verb/style.css") .. [=[
<div class="NavFrame">
<div class="NavHead"> Conjugation of ]=] .. get_displayable_lemma(data.actual_lemma) .. (table_len(title) > 0 and " (" .. concat(title, ", ") .. ")" or "") .. [=[</div>
<div class="NavContent">
{| class="roa-inflection-table" data-toggle-category="inflection"
]=] .. make_indc_rows(data) .. make_subj_rows(data) .. make_impr_rows(data) .. make_nonfin_rows(data) .. make_vn_rows(data) .. [=[
|}</div></div>]=].. make_footnotes(data)
end
local function checkexist(data)
if NAMESPACE ~= 0 then
return
end
local outerbreak = false
local categories = data.categories
for _, conjugation in pairs(data.forms) do
if conjugation then
if type(conjugation) ~= "table" then
conjugation = {conjugation}
end
for _, conj in ipairs(conjugation) do
if not cfind(conj, " ") then
local title = (lang:makeEntryName(conj))
local t = mw.title.new(title)
if t and not t.exists then
insert_if_not(categories, "Latin verbs with red links in their inflection tables")
outerbreak = true
break
end
end
end
end
if outerbreak then
break
end
end
end
-- functions for creating external search hyperlinks
local function cleanup(x)
x:gsub("[[%]]", "")
:gsub(" ", "+")
return toNFC((ugsub(toNFD(x), "[^%w]+", "")))
end
local function flatten_values(t)
t = flatten(t)
local i = 1
while true do
local v = t[i]
if v == nil then
return t
elseif cfind(v, "<") then
remove(t, i)
else
t[i] = cleanup(v)
i = i + 1
end
end
end
local function link_google_books(verb, forms, domain)
local function partition_XS_into_N(XS, N)
local count = 0
local mensae = {}
for _, v in pairs(XS) do
if count % N == 0 then
mensae[#mensae+1] = {}
end
count = count + 1
mensae[#mensae][#(mensae[#mensae])+1] = v
end
return mensae
end
local function forms_N_to_link(fs, N, args, site)
return '[https://www.google.com/search?'..args..'&q='..site..'+%22'.. concat(fs, "%22+OR+%22") ..'%22 '..N..']'
end
local function make_links_txt(fs, N, site)
local args = site == "Books" and "tbm=bks&lr=lang_la" or ""
local links = {}
for k,v in pairs(partition_XS_into_N(fs, N)) do
links[#links+1] = forms_N_to_link(v,k,args,site=="Books" and "" or site)
end
return concat(links, ' - ')
end
return "Google "..domain.." forms of "..verb.." : "..make_links_txt(forms, 30, domain)
end
-- The main new entry point.
function export.show(frame)
local parent_args = frame:getParent().args
local data, typeinfo = export.make_data(parent_args)
if type(data) == "string" then -- json=1 specified
return data
end
local domain = frame:getParent().args['search']
-- Test code to compare existing module to new one.
if test_new_la_verb_module then
local m_new_la_verb = require("Module:User:Benwing2/la-verb")
local miscdata = {
title = data.title,
categories = data.categories,
}
local new_parent_args = frame:getParent().args
local newdata = m_new_la_verb.make_data(new_parent_args)
local newmiscdata = {
title = newdata.title,
categories = newdata.categories,
}
local all_verb_props = {"forms", "form_footnote_indices", "footnotes", "miscdata"}
local difconj = false
for _, prop in ipairs(all_verb_props) do
local table = prop == "miscdata" and miscdata or data[prop]
local newtable = prop == "miscdata" and newmiscdata or newdata[prop]
for key, val in pairs(table) do
local newval = newtable[key]
if not forms_equal(val, newval) then
-- Uncomment this to display the particular key and
-- differing forms.
-- error(key .. " " .. (val and concat_vals(val) or "nil") .. " || " .. (newval and concat_vals(newval) or "nil"))
difconj = true
break
end
end
if difconj then
break
end
-- Do the comparison the other way as well in case of extra keys
-- in the new table.
for key, newval in pairs(newtable) do
local val = table[key]
if not forms_equal(val, newval) then
-- Uncomment this to display the particular key and
-- differing forms.
-- error(key .. " " .. (val and concat_vals(val) or "nil") .. " || " .. (newval and concat_vals(newval) or "nil"))
difconj = true
break
end
end
if difconj then
break
end
end
track(difconj and "different-conj" or "same-conj")
end
if typeinfo.subtypes.suffix then
data.categories = {}
end
-- Check if the links to the verb forms exist
-- Has to happen after other categories are removed for suffixes
checkexist(data)
if domain == nil then
return make_table(data) .. m_utilities.format_categories(data.categories, lang)
end
local verb = data['forms']['1s_pres_actv_indc'] ~= nil and ('[[' .. toNFC((ugsub(toNFD(data['forms']['1s_pres_actv_indc']), '[^%w]+', ""))) .. '|' .. data['forms']['1s_pres_actv_indc'] .. ']]') or 'verb'
return link_google_books(verb, flatten_values(data['forms']), domain)
end
return export
9qjymqpjcyzaw2ukzj6ixdp0sd4hwxw
มอดูล:la-headword
828
152510
5776185
4864951
2026-11-02T01:18:38Z
Octahedron80
267
5776185
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายชนิด")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปชนิด" .. decl_class .. " ของคำนามภาษาละติน|ชนิด" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "ผันรูป" .. decl_class .. " ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปชนิด" .. decl_class .. " ภาษาละติน")
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin third declension|third declension]]")
local n = {label = "neuter"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin first declension|first]]")
insert(infl_classes, "[[ภาคผนวก:Latin second declension|second declension]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
8oujbv9nw2ksri651sywy924297qrr9
5776186
5776185
2026-11-02T01:19:33Z
Octahedron80
267
5776186
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปกลุ่ม" .. decl_class .. " ของคำนามภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "ผันรูป" .. decl_class .. " ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปกลุ่ม" .. decl_class .. " ภาษาละติน")
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin third declension|third declension]]")
local n = {label = "neuter"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin first declension|first]]")
insert(infl_classes, "[[ภาคผนวก:Latin second declension|second declension]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
9k2ynfkar9yqqpgh74fspmgwobztr4z
5776187
5776186
2026-11-02T01:23:59Z
Octahedron80
267
5776187
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "ผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน")
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin third declension|third declension]]")
local n = {label = "neuter"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin first declension|first]]")
insert(infl_classes, "[[ภาคผนวก:Latin second declension|second declension]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
45u660zoq4xsbskakcneai7m7vyki15
5776192
5776187
2026-11-02T01:43:02Z
Octahedron80
267
5776192
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin third declension|third declension]]")
local n = {label = "neuter"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:Latin first declension|first]]")
insert(infl_classes, "[[ภาคผนวก:Latin second declension|second declension]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
6pf6lg2hr3i4oifl3iqto9ak3pxf66x
5776194
5776192
2026-11-02T01:50:10Z
Octahedron80
267
5776194
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:Latin first conjugation|first conjugation]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:Latin second conjugation|second conjugation]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:Latin third conjugation|third conjugation]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:Latin third conjugation|third conjugation]] %s-variant"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:Latin fourth conjugation|fourth conjugation]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular conjugation]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "highly [[defective verb#English|defective]]")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]]")
local n = {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 1 ภาษาละติน|กลุ่มที่ 1]]")
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2 ภาษาละติน|กลุ่มที่ 2]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
au368z7md1xbdqrbetpp322iod32b2m
5776196
5776194
2026-11-02T02:13:01Z
Octahedron80
267
5776196
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 1 ภาษาละติน|กลุ่มที่ 1]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 2 ภาษาละติน|กลุ่มที่ 2]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]] รูปแปร %s"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 4 ภาษาละติน|กลุ่มที่ 4]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|ไม่ปรกติ]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "[[คำกริยาบกพร่อง|บกพร่อง]]อย่างสูง")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:Latin irregular verbs|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:Latin irregular verbs|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]]")
local n = {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 1 ภาษาละติน|กลุ่มที่ 1]]")
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2 ภาษาละติน|กลุ่มที่ 2]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
f4n551ugqs4fj5anl8iszn7mma6z6lq
5776197
5776196
2026-11-02T02:14:35Z
Octahedron80
267
5776197
Scribunto
text/plain
-- TODO: handle "-old" subtypes generally, appending "in Old Latin", where terms have additional OL inflections.
local export = {}
local pos_functions = {}
local string_utilities = "Module:string utilities"
local table_module = "Module:table"
local utilities_module = "Module:utilities"
local concat = table.concat
local form_is_empty = require("Module:la-utilities").form_is_empty
local insert = table.insert
local remove = table.remove
local umatch = mw.ustring.match
local lang = require("Module:languages").getByCode("la")
local NAMESPACE = mw.title.getCurrentTitle().namespace
local PAGENAME = mw.loadData("Module:headword/data").pagename
local legal_gender = {
["m"] = true,
["f"] = true,
["n"] = true,
["?"] = true,
["?!"] = true,
}
local declension_to_thai = {
["1"] = "ที่ 1",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}
local gender_names = {
["m"] = "เพศชาย",
["f"] = "เพศหญิง",
["n"] = "เพศกลาง",
["?"] = "ไม่ทราบเพศ",
["?!"] = "unattested gender",
}
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function get_plaintext(...)
get_plaintext = require(utilities_module).get_plaintext
return get_plaintext(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function invert(...)
invert = require(table_module).invert
return invert(...)
end
local function keys_to_list(...)
keys_to_list = require(table_module).keysToList
return keys_to_list(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities).pattern_escape
return pattern_escape(...)
end
local function serial_comma_join(...)
serial_comma_join = require(table_module).serialCommaJoin
return serial_comma_join(...)
end
local function split(...)
split = require(string_utilities).split
return split(...)
end
local function ulower(...)
ulower = require(string_utilities).lower
return ulower(...)
end
local function format(array, concatenater)
if #array == 0 then
return ""
end
local concatenated = concat(array, concatenater)
if concatenated == "" then
return ""
elseif concatenated:sub(-1) == "'" then
concatenated = concatenated .. " "
end
return "; ''" .. concatenated .. "''"
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function make_link(page, display, face, accel)
return require("Module:links").full_link({term = page, alt = display, lang = lang, accel = accel}, face)
end
-- The main entry point.
-- This is the only function that can be invoked from a template.
function export.show(frame)
local iargs = require("Module:parameters").process(frame.args, {
[1] = {required = true},
["def"] = true,
["suff_type"] = true,
})
local args = frame:getParent().args
local poscat = iargs[1]
-- หมวดหมู่เป็นภาษาไทย
local poscat_th = require("Module:th-utilities").th_pos(poscat)
local def = iargs.def
local suff_type = iargs.suff_type
local postype = nil
if suff_type then
postype = 'รูป' .. poscat_th .. 'เป็น' .. suff_type
else
postype = poscat_th
end
local data = {lang = lang, categories = {}, heads = {}, genders = {}, inflections = {}}
local infl_classes = {}
local title = {}
local postscript = {}
if poscat_th == "ปัจจัย" then
insert_if_not(data.categories, "ปัจจัยสร้าง" .. suff_type .. "ภาษาละติน")
end
if pos_functions[postype] then
local new_poscat = pos_functions[postype](def, args, data, infl_classes, title, postscript)
if new_poscat then
poscat_th = new_poscat
end
end
if NAMESPACE == 118 then -- Reconstruction:
data.pos_category = poscat_th .. "ที่สืบสร้าง"
data.nolink = true
else
data.pos_category = poscat_th
end
postscript = concat(postscript, ", ")
return
require("Module:headword").full_headword(data)
.. format(infl_classes, "/")
.. format(title, ", ")
.. (postscript ~= "" and " (" .. postscript .. ")" or "")
end
local function process_num_type(numtype, categories)
if numtype == "card" then
insert_if_not(categories, "Latin cardinal numbers")
elseif numtype == "ord" then
insert_if_not(categories, "Latin ordinal numbers")
elseif numtype == "frac" then
insert_if_not(categories, "Latin fractional numbers")
elseif numtype == "dist" then
insert_if_not(categories, "Latin distributive numbers")
elseif numtype == "mul" then
insert_if_not(categories, "Latin multiplicative numbers")
elseif numtype == "coll" then
insert_if_not(categories, "Latin collective numbers")
elseif numtype then
error("Unrecognized numeral type '" .. numtype .. "'")
end
end
local function normalize_derivation(deriv, deriv_label)
if not deriv then
return nil
end
local deriv1 = deriv[1]
if not deriv1 or deriv1 == "-" then
return deriv1
end
deriv.label = glossary_link(deriv_label)
return deriv
end
local function nouns(pos, def, args, data, infl_classes, title)
local is_num = pos == "เลข"
local is_pn = false
if pos == "คำวิสามานยนาม" then
is_pn = true
pos = "คำนาม"
end
local decldata = require("Module:la-nominal").do_generate_noun_forms(args, pos, "headword", def, is_num)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_noun_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local genders = decldata.overriding_genders
if #genders == 0 then
if decldata.gender then
genders = {ulower(decldata.gender)}
elseif not is_num then
error("No gender explicitly specified in headword template using g=, and can't infer gender from lemma spec")
end
end
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
local gender = g:match("^(.[\128-\191]*)%-[sp]$")
if not gender then
gender = g
end
if not legal_gender[gender] then
error("Gender “" .. gender .. "” is not a valid Latin gender.")
end
insert(data.genders, g)
insert_if_not(categories, decldata.pos .. gender_names[gender] .. "ผันรูปไม่ได้ภาษาละติน")
end
else
local is_irreg = false
local is_indecl = false
local is_decl = false
local has_multiple_decls = false
local has_multiple_variants = false
-- flatten declension specs
local decls = {}
for _, g in ipairs(genders) do
if not legal_gender[g] then
error("Gender “" .. g .. "” is not a valid Latin gender.")
elseif decldata.num == "pl" then
g = g .. "-p"
elseif decldata.num == "sg" then
g = g .. "-s"
end
insert(data.genders, g)
end
local function process_decl(decl_list, props)
local headword_decl = props.headword_decl
-- skip adjectival declensions
if headword_decl:match("+$") then
return
elseif props.decl == "irreg" then
is_irreg = true
headword_decl = headword_decl:match("^irreg/(.*)$") or headword_decl
local irreg_decls = split(headword_decl, ",")
if #irreg_decls > 1 then
has_multiple_decls = true
end
for _, d in ipairs(irreg_decls) do
if d == "indecl" or d == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, d)
end
else
if headword_decl == "indecl" or headword_decl == "0" then
is_indecl = true
else
is_decl = true
end
insert_if_not(decl_list, headword_decl)
end
end
for _, props in ipairs(decldata.propses) do
if props.headword_decl then
process_decl(decls, props)
else
local alternant_decls = {}
for _, alternant in ipairs(props) do
for _, single_props in ipairs(alternant) do
process_decl(alternant_decls, single_props)
end
end
if #alternant_decls > 1 then
has_multiple_decls = true
elseif #decls > 1 then
has_multiple_variants = true
end
for _, d in ipairs(alternant_decls) do
insert_if_not(decls, d)
end
end
end
if is_indecl and is_decl then
has_multiple_decls = true
end
if has_multiple_decls then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple declensions")
end
if has_multiple_variants then
insert_if_not(categories, "Latin " .. decldata.pos .. " with multiple variants of a single declension")
end
if is_irreg then
insert(title, glossary_link("ไม่ปรกติ"))
insert_if_not(categories, decldata.pos .. "ไม่ปรกติภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ไม่ปรกติภาษาละติน")
end
end
if is_indecl then
if is_decl then
insert(title, glossary_link("ผันรูปไม่ได้"))
else
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
end
insert_if_not(categories, decldata.pos .. "ผันรูปไม่ได้ภาษาละติน")
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "ผันรูปไม่ได้ภาษาละติน")
end
end
if #decls > 1 then
insert(title, "ผันรูปหลายแบบ")
end
for _, decl in ipairs(decls) do
if not (decl == "0" or decl == "indecl" or decl == "sgpl" or decl == "irreg") then
local decl_class = declension_to_thai[decl]
if not decl_class then
error("Internal error: declension '" .. decl .. "' not recognized")
end
insert(title, "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. decl_class .. " ภาษาละติน|กลุ่ม" .. decl_class .. "]]")
insert_if_not(categories, decldata.pos .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
for _, g in ipairs(genders) do
insert_if_not(categories, decldata.pos .. gender_names[g] .. "กลุ่ม" .. decl_class .. " ภาษาละติน") -- ละ "ผันรูปนาม"
end
end
end
local lemma_num = decldata.num == "pl" and "pl" or "sg"
if NAMESPACE == 118 then -- Reconstruction:
-- For reconstructed nouns:
if data.genders[1] == 'n' and lemma_num == 'sg' then
-- singular neuter nouns give a plural
local pl = decldata.forms["nom_pl"]
if pl and pl ~= "" and #pl > 0 then
pl.label = "plural"
insert(data.inflections, pl)
end
else
-- all others give an oblique
local obl = decldata.forms["acc_" .. lemma_num]
if obl and obl ~= "" and #obl > 0 then
obl.label = "oblique"
insert(data.inflections, obl)
end
end
else
local gen = decldata.forms["gen_" .. lemma_num]
if (decldata.unattested["gen_" .. lemma_num]) then
gen[1] = '*' .. gen[1]
data.nolink = true
end
if gen and gen ~= "" and gen ~= "—" and #gen > 0 then
if is_decl then
-- Skip displaying the genitive for nouns that are only
-- indeclinable. But we do display it for nouns like Abrahām
-- and Ādām that can be either indeclinable or declined.
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
end
else
insert(data.inflections, {label = "ไม่มีสัมพันธการก"})
insert_if_not(categories, "Latin " .. decldata.pos .. " with no genitive singular")
end
end
end
local m = normalize_derivation(decldata.m, "เพศชาย")
if m and m ~= "-" then
insert(data.inflections, m)
end
local f = normalize_derivation(decldata.f, "เพศหญิง")
if f and f ~= "-" then
insert(data.inflections, f)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return is_pn and decldata.pos == "คำนาม" and "คำวิสามานยนาม" or decldata.pos
end
pos_functions["คำนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำนาม", def, args, data, infl_classes, title)
end
pos_functions["คำวิสามานยนาม"] = function(def, args, data, infl_classes, title)
return nouns("คำวิสามานยนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นนาม"] = function(def, args, data, infl_classes, title)
return nouns("เลข", def, args, data, infl_classes, title)
end
function export.verb_title(title, typeinfo, lemma_forms)
local conj = typeinfo.conj
local irreg_processing = typeinfo.irreg
local subtypes = typeinfo.subtypes
local first_lemma = ""
if #lemma_forms > 0 then
first_lemma = require("Module:links").remove_links(lemma_forms[1])
end
if conj == "1st" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 1 ภาษาละติน|กลุ่มที่ 1]]")
elseif conj == "2nd" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 2 ภาษาละติน|กลุ่มที่ 2]]")
elseif conj == "3rd" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]]")
elseif conj == "3rd-io" then
insert(title, ("[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]] รูปแปร %s"):format(make_link(nil, "iō", "term")))
elseif conj == "4th" then
insert(title, "[[ภาคผนวก:การผันรูปกริยากลุ่มที่ 4 ภาษาละติน|กลุ่มที่ 4]]")
elseif conj == "irreg" then -- sum
insert(title, "[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|ไม่ปรกติ]]")
end
if subtypes.highlydef then -- āiō, inquam
insert(title, "[[คำกริยาบกพร่อง|บกพร่อง]]อย่างสูง")
end
if subtypes.suppl then -- sum, volō
insert(title, "[[suppletive#English|suppletive]]")
end
if subtypes["3only"] then -- decet
insert(title, "[[third person#English|third person]]-only")
elseif subtypes.impers then -- decet, advesperāscit (also nopass)
insert(title, "[[impersonal verb#English|impersonal]]")
end
if subtypes.depon then -- dēmōlior, calvor (also noperf)
insert(title, "[[deponent#English|deponent]]")
end
if subtypes.perfaspres then -- meminī, ōdī
insert(title, "no [[present tense#English|present]] stem")
-- If semidepon is set, only the active forms are affected.
local voice = subtypes.semidepon and "[[active voice#English|active]] " or ""
insert(title, ("[[perfect tense#English|perfect]] %sforms have [[present tense#English|present]] %smeaning"):format(voice, voice))
-- Clarify that deponency can only apply to the perfect.
if subtypes.semidepon then -- ōdī
insert(title, "[[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
if subtypes.optsemidepon then
insert(title, "optionally [[deponent#English|deponent]] in the [[perfect tense#English|perfect]]")
end
else
if subtypes.semidepon then -- fīdō, gaudeō
insert(title, "[[semi-deponent#English|semi-deponent]]")
end
if subtypes.optsemidepon then -- audeō, placeō, soleō, pudeō
insert(title, "optionally [[semi-deponent#English|semi-deponent]]")
end
end
if subtypes.imponly then -- cedo, apage
insert(title, "[[imperative mood#English|imperative]]-only")
end
if subtypes.nopass then -- coacēscō
insert(title, "no [[passive voice#English|passive]]")
end
local stems = {}
if subtypes.nopres then -- coepī; perfaspres already handles this above
insert(stems, "[[present tense#English|present]]")
end
if subtypes.noperf then
insert(stems, "[[perfect tense#English|perfect]]")
end
if subtypes.nosup or subtypes.supfutractvonly then
insert(stems, "[[supine#English|supine]]")
end
if #stems > 0 then
local extra = subtypes.supfutractvonly and " except in the [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]"
insert(title, ("no %s stem%s%s"):format(serial_comma_join(stems, {conj = "or"}), #stems > 1 and "s" or "", extra or ""))
end
if subtypes.nofut then -- soleō
insert(title, "no [[future tense#English|future]]")
end
if subtypes.pass3only then -- praefundō
insert(title, "[[third person#English|third person]]-only in the [[passive voice#English|passive]]")
elseif subtypes.passimpers then -- abambulō
local msg = "[[impersonal verb#English|impersonal]] in the [[passive voice#English|passive]]"
if subtypes.passimpersold then -- possum
msg = msg .. " in [[Old Latin]]"
end
insert(title, msg)
end
if subtypes.noimp then -- volō
insert(title, "no [[imperative mood#English|imperative]]")
end
if irreg_processing and umatch(first_lemma, "d[īū]cō$") then -- dīcō
insert(title, "[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|irregular]] short [[imperative mood#English|imperative]]")
end
if subtypes.nofutractvptc and not subtypes.nosup then -- fīō
insert(title, "no [[future tense#English|future]] [[active voice#English|active]] [[participle#English|participle]]")
end
if not (subtypes.nopres or subtypes.perfaspres or subtypes.imponly) then
if subtypes.noinf then -- inquam
insert(title, "no [[infinitive#English|infinitive]]")
end
if subtypes.noger then -- libet
insert(title, "no [[gerund#English|gerund]]")
end
end
if subtypes.shorta then -- dō
insert(title, ("[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|irregular]] short %s in most forms"):format(make_link(nil, "ă", "term")))
end
if irreg_processing then
if first_lemma:match("edō$") then -- edō
insert(title, "[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|irregular]] alternative forms")
elseif first_lemma:match("fīō$") then -- fīō
insert(title, "[[ภาคผนวก:คำกริยาไม่ปรกติภาษาละติน|irregular]] long " .. make_link(nil, "ī", "term"))
end
end
end
pos_functions["คำกริยา"] = function(def, args, data, infl_classes, title)
local m_la_verb = require("Module:la-verb")
local def1, def2
if def then
def1, def2 = def:match("^(.-):(.*)$")
end
local conjdata, typeinfo = m_la_verb.make_data(args, true, def1, def2)
local lemma_forms = conjdata.overriding_lemma
if not lemma_forms or #lemma_forms == 0 then
lemma_forms = m_la_verb.get_lemma_forms(conjdata, true)
end
data.heads = lemma_forms
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-verb}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = conjdata.id
local perf_only = false
local function insert_inflection(infl, label)
infl.label = label
insert(data.inflections, infl)
end
local inf = m_la_verb.get_valid_forms(conjdata.forms["pres_actv_inf"])
if #inf > 0 then
insert_inflection(inf, "present infinitive")
else
inf = m_la_verb.get_valid_forms(conjdata.forms["perf_actv_inf"])
if #inf > 0 then
perf_only = true
insert_inflection(inf, "perfect infinitive")
end
end
if not perf_only then
local perf = m_la_verb.get_valid_forms(conjdata.forms["1s_perf_actv_indc"])
if #perf == 0 then
perf = m_la_verb.get_valid_forms(conjdata.forms["3s_perf_actv_indc"])
end
if #perf > 0 then
insert_inflection(perf, "perfect active")
end
end
local subtypes = typeinfo.subtypes
if not (subtypes.depon or subtypes.semidepon) then
local sup = m_la_verb.get_valid_forms(conjdata.forms["acc_sup"])
if #sup > 0 then
insert_inflection(sup, "supine")
else
local fap = m_la_verb.get_valid_forms(conjdata.forms["futr_actv_ptc"])
if #fap > 0 then
insert_inflection(fap, "future active participle")
end
end
end
export.verb_title(title, typeinfo, lemma_forms)
end
pos_functions["รูปปัจจัยเป็นกริยา"] = pos_functions["คำกริยา"]
local function attested_form(decldata, index)
local form
if (decldata.unattested[index]) then
form = { { term = '*' .. decldata.forms[index][1], nolink = true } }
else
form = decldata.forms[index]
end
return form
end
local function degree_derivations(comp, sup, data)
local inflections = data.inflections
if not (comp or sup) then
return
elseif (not comp or comp == "-") and (not sup or sup == "-") then
insert(inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|เปรียบเทียบ]]ไม่ได้"})
insert_if_not(data.categories, "Latin uncomparable adverbs")
return
elseif comp == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นกว่า|ขั้นกว่า]]"})
elseif comp then
insert(inflections, comp)
end
if sup == "-" then
insert(inflections, {label = "ไม่มี[[ภาคผนวก:อภิธานศัพท์#ขั้นสุด|ขั้นสุด]]"})
elseif sup then
insert(inflections, sup)
end
end
local function adjectives(pos, def, args, data, infl_classes)
local is_num = pos == "เลข"
local decldata = require("Module:la-nominal").do_generate_adj_forms(args, pos, "headword", nil, def)
local lemma = decldata.overriding_lemma
-- TODO: modify the headword line if the slot isn't the expected one.
if #lemma == 0 then
for slot in require("Module:la-nominal").iter_potential_adj_lemma_slots() do
local potential_lemma = decldata.forms["linked_" .. slot]
if not form_is_empty(potential_lemma) then
if decldata.unattested[slot] then
potential_lemma = "*" .. potential_lemma
end
lemma = potential_lemma
break
end
end
end
data.heads = lemma
-- Since we always set data.heads to the lemma and specification of the lemma is mandatory in {{la-noun}}, there aren't
-- really any redundant heads.
data.no_redundant_head_cat = true
data.id = decldata.id
local categories = data.categories
if is_num then
process_num_type(decldata.num_type, categories)
end
if decldata.num == "pl" then
insert_if_not(categories, decldata.pos .. "พหูพจน์เท่านั้นภาษาละติน")
end
if decldata.indecl then
insert(data.inflections, {label = glossary_link("ผันรูปไม่ได้")})
if decldata.pos == "รูปกริยาขยาย" then
if lemma[1]:match("um$") then
insert_if_not(categories, "Latin perfect participles")
end
end
else
local lemma_num = decldata.num == "pl" and "pl" or "sg"
local masc = decldata.forms["nom_" .. lemma_num .. "_m"]
local fem = attested_form(decldata, "nom_" .. lemma_num .. "_f")
local neut = attested_form(decldata, "nom_" .. lemma_num .. "_n")
local gen = attested_form(decldata, "gen_" .. lemma_num .. "_m")
local acc = attested_form(decldata, "acc_" .. lemma_num .. "_m")
if decldata.pos == "รูปกริยาขยาย" then
local masc1 = masc[1]
if masc1:sub(-5) == "ūrus" then
insert_if_not(categories, "Latin future active participles")
elseif masc1:sub(-4) == "ndus" then
-- FIXME, should rename to "Latin gerundives")
insert_if_not(categories, "Latin future passive participles")
else
local masc1_final2 = masc1:sub(-2)
if masc1_final2 == "us" then
insert_if_not(categories, "Latin perfect participles")
elseif masc1_final2 == "ns" then
insert_if_not(categories, "Latin present participles")
else
error("Unrecognized participle ending: " .. masc1)
end
end
end
-- We display the inflections in three different ways to mimic the
-- old way of doing things:
--
-- 1. If masc and fem are different, show masc, fem and neut.
-- 2. Otherwise, if masc and neut are different, show masc and neut.
-- 3. Otherwise, show masc nominative and masc genitive.
if not form_is_empty(fem) and not deep_equals(masc, fem) then
fem.label = "เพศหญิง"
insert(data.inflections, fem)
if not form_is_empty(neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
end
elseif not form_is_empty(neut) and not deep_equals(masc, neut) then
neut.label = "เพศกลาง"
insert(data.inflections, neut)
elseif not form_is_empty(gen) then
gen.label = "สัมพันธการก"
insert(data.inflections, gen)
elseif not form_is_empty(acc) then
acc.label = "กรรมการก"
insert(data.inflections, acc)
end
insert(infl_classes, decldata.title)
end
local comp = normalize_derivation(decldata.comp, "ขั้นกว่า")
local sup = normalize_derivation(decldata.sup, "ขั้นสุด")
degree_derivations(comp, sup, data)
local adv = normalize_derivation(decldata.adv, "คำกริยาวิเศษณ์")
if adv and adv ~= "-" then
insert(data.inflections, adv)
end
for _, cat in ipairs(decldata.categories) do
insert_if_not(categories, cat)
end
for _, cat in ipairs(decldata.cat) do
insert_if_not(categories, cat .. "ภาษาละติน")
end
return decldata.pos
end
pos_functions["คำคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("คำคุณศัพท์", def, args, data, infl_classes, title)
end
pos_functions["รูปกริยาขยาย"] = function(def, args, data, infl_classes, title)
return adjectives("รูปกริยาขยาย", def, args, data, infl_classes, title)
end
pos_functions["ตัวกำหนด"] = function(def, args, data, infl_classes, title)
return adjectives("ตัวกำหนด", def, args, data, infl_classes, title)
end
pos_functions["คำสรรพนาม"] = function(def, args, data, infl_classes, title)
return adjectives("คำสรรพนาม", def, args, data, infl_classes, title)
end
pos_functions["รูปปัจจัยเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("ปัจจัย", def, args, data, infl_classes, title)
end
pos_functions["รูปเลขเป็นคุณศัพท์"] = function(def, args, data, infl_classes, title)
return adjectives("เลข", def, args, data, infl_classes, title)
end
pos_functions["คำกริยาวิเศษณ์"] = function(def, args, data)
local sublist = {sublist = "/"}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {alias_of = "comp"},
[3] = {alias_of = "sup"},
["head"] = {list = true, required = true},
["comp"] = sublist,
["sup"] = sublist,
["id"] = true,
})
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
local comp, sup = args.comp, args.sup
local irreg = false
if comp then
if comp[1] == "-" then
comp = "-"
elseif comp[1] == nil then
comp = nil
else
comp.label = glossary_link("ขั้นกว่า")
comp = args.comp
irreg = true
end
end
if sup then
if sup[1] == "-" then
sup = "-"
elseif sup[1] == nil then
sup = nil
else
sup.label = glossary_link("ขั้นสุด")
sup = args.sup
irreg = true
end
end
local categories = data.categories
if irreg then
insert_if_not(categories, "คำกริยาวิเศษณ์ไม่ปรกติภาษาละติน")
end
if not (comp or sup) then
local default_comp = {label = glossary_link("ขั้นกว่า")}
local default_sup = {label = glossary_link("ขั้นสุด")}
for _, head in ipairs(args.head) do
local stem = nil
for _, suff in ipairs{"iter", "nter", "ter", "er", "iē", "ē", "im", "ō"} do
stem = head:match("(.*)" .. pattern_escape(suff) .. "$")
if stem ~= nil then
if suff == "nter" then
stem = stem .. "nt"
suff = "er"
end
insert(default_comp, stem .. "ius")
insert(default_sup, stem .. "issimē")
break
end
end
if not stem then
error("Unrecognized adverb type, recognized types are “-ē”, “-er”, “-ter”, “-iter”, “-im”, or “-ō” or specify irregular forms or “-” if incomparable.")
end
end
comp = comp or default_comp
sup = sup or default_sup
end
degree_derivations(comp, sup, data)
end
pos_functions["รูปปัจจัยเป็นกริยาวิเศษณ์"] = pos_functions["คำกริยาวิเศษณ์"]
local function get_forms(forms)
if #forms == 0 then
return nil
end
local i, attested = 1, false
while true do
local form = forms[i]
if form == nil then
return forms, attested
elseif form == "-" then
remove(forms, i)
else
if not (attested or get_plaintext(form):sub(1, 1) == "*") then
attested = true
end
i = i + 1
end
end
end
local function degree(pos, deg, pos_func, other_arg, other_label, args, data, infl_classes)
local list = {list = true}
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
["head"] = list,
["positive"] = list,
[other_arg] = list,
["id"] = true,
})
data.no_redundant_head_cat = #args.head == 0
-- Set default manually so we can tell whether the user specified head=.
if #args.head == 0 then
args.head = {PAGENAME}
end
data.heads = args.head
data.id = args.id
insert(data.inflections, {label = deg})
if pos_func then
pos_func(args, data, infl_classes)
end
local positive, positive_attested = get_forms(args.positive)
if positive then
if not positive_attested then
insert(data.categories, pos .. deg .. "เท่านั้นภาษาละติน")
end
if #positive > 0 then
args.positive.label = "เชิงบวก"
insert(data.inflections, args.positive)
else
insert(data.inflections, {label = "ไม่มีรูปเชิงบวก"})
end
end
local other = get_forms(args[other_arg])
if other then
if #other > 0 then
args[other_arg].label = other_label
insert(data.inflections, args[other_arg])
else
insert(data.inflections, {label = "ไม่มีรูป" .. other_label})
end
end
-- If a lemma, return the primary part of speech ("adjectives" or
-- "adverbs"), so that the term is categorized in "Latin adjectives" or
-- "Latin adverbs". Otherwise, return nothing, so that the term goes in the
-- relevant non-lemma category (e.g. "Latin comparative adjectives"), and
-- into "Latin non-lemma forms".
if positive and not positive_attested then
return pos
end
end
local function comp_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 3 ภาษาละติน|กลุ่มที่ 3]]")
local n = {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local neuter = head:gsub("or$", "us")
insert(n, neuter)
end
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นกว่า", comp_adj, "sup", "ขั้นสุด", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นกว่า"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นกว่า", nil, "sup", "ขั้นสุด", args, data, infl_classes)
end
local function sup_adj(args, data, infl_classes)
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 1 ภาษาละติน|กลุ่มที่ 1]]")
insert(infl_classes, "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2 ภาษาละติน|กลุ่มที่ 2]]")
local f, n = {label = "เพศหญิง"}, {label = "เพศกลาง"}
for _, head in ipairs(args.head) do
local stem = head:gsub("us$", "")
insert(f, stem .. "a")
insert(n, stem .. "um")
end
insert(data.inflections, f)
insert(data.inflections, n)
end
pos_functions["คำคุณศัพท์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำคุณศัพท์", "ขั้นสุด", sup_adj, "comp", "ขั้นกว่า", args, data, infl_classes)
end
pos_functions["คำกริยาวิเศษณ์ขั้นสุด"] = function(def, args, data, infl_classes, title)
return degree("คำกริยาวิเศษณ์", "ขั้นสุด", nil, "comp", "ขั้นกว่า", args, data, infl_classes)
end
local function adpositions(pos, def, args, data, infl_classes, title, postscript)
local cases = invert(require("Module:la-utilities").cases)
args = require("Module:parameters").process(args, {
[1] = {alias_of = "head", list = false},
[2] = {list = true, set = keys_to_list(cases)},
["head"] = {list = true, required = true},
["id"] = true,
})
-- Case names are supplied in numbered arguments, optionally preceded by
-- headwords.
cases = args[2]
for i = 1, #cases do
for j = i + 1, #cases do
if cases[i] == cases[j] then
error("Duplicate case")
end
end
local case = cases[i]
local appendix_link = glossary_link(case)
if i == 1 then
appendix_link = "+ " .. appendix_link
end
insert(postscript, appendix_link)
insert_if_not(data.categories, "Latin " .. case .. " " .. pos)
end
data.heads = args.head
data.no_redundant_head_cat = true -- since head= is required
data.id = args.id
end
pos_functions["คำบุพบท"] = function(...)
return adpositions("คำบุพบท", ...)
end
pos_functions["คำปัจฉบท"] = function(...)
return adpositions("คำปัจฉบท", ...)
end
pos_functions["รูปกริยาเป็นนาม"] = function(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = "labōrandum"}, -- headword
[2] = true, -- gerundive
})
data.heads = {args[1]}
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
insert(data.inflections, {label = "[[ภาคผนวก:อภิธานศัพท์#กรรมการก|กรรมการก]]"})
local stem = args[1]:match("^(.*)um$")
if not stem then
error("Unrecognized gerund ending: " .. stem)
end
if args[2] == "-" then
insert(data.inflections, {label = "no [[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
else
insert(data.inflections, {[1] = args[2] or stem .. "us", label = "[[ภาคผนวก:อภิธานศัพท์#gerundive|gerundive]]"})
end
end
local function non_lemma_forms(def, args, data)
args = require("Module:parameters").process(args, {
[1] = {required = true, default = def}, -- headword or cases
["head"] = {list = true, require_index = true},
["g"] = {list = true},
["id"] = true,
})
local heads = {args[1]}
for _, head in ipairs(args.head) do
insert(heads, head)
end
data.heads = heads
data.no_redundant_head_cat = true -- since 1= is required and goes into data.heads
data.genders = args.g
data.id = args.id
end
pos_functions["รูปนาม"] = non_lemma_forms
pos_functions["รูปวิสามานยนาม"] = non_lemma_forms
pos_functions["รูปสรรพนาม"] = non_lemma_forms
pos_functions["รูปกริยา"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาเป็นนาม"] = non_lemma_forms
pos_functions["รูปคุณศัพท์"] = non_lemma_forms
pos_functions["รูปผันรูปกริยาขยาย"] = non_lemma_forms
pos_functions["รูปผันตัวกำหนด"] = non_lemma_forms
pos_functions["รูปเลข"] = non_lemma_forms
pos_functions["รูปปัจจัย"] = non_lemma_forms
return export
k7sdtqwb9k2cx12k3l90rkgnyi9p004
มอดูล:la-noun/data
828
243403
5776188
5632471
2026-11-02T01:31:33Z
Octahedron80
267
5776188
Scribunto
text/plain
local decl = {}
local la_utilities_module = "Module:la-utilities"
local string_char_module = "Module:string/char"
local table_deep_copy_module = "Module:table/deepCopy"
local table_insert_if_not_module = "Module:table/insertIfNot"
local pairs = pairs
local toNFD = mw.ustring.toNFD
local u = require(string_char_module)
local umatch = mw.ustring.match
local usub = mw.ustring.sub
local MACRON = u(0x304)
local BREVE = u(0x306)
local DIAER = u(0x308)
local accent = "[" .. MACRON .. BREVE .. DIAER .. "]*"
local function add_form(...)
add_form = require(la_utilities_module).add_form
return add_form(...)
end
local function deep_copy(...)
deep_copy = require(table_deep_copy_module)
return deep_copy(...)
end
local function insert_if_not(...)
insert_if_not = require(table_insert_if_not_module)
return insert_if_not(...)
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:Glossary#" .. anchor .. "|" .. text .. "]]"
end
decl["0"] = function(data, args)
local forms = data.forms
local stem = args[1]
forms["nom_sg"] = stem
forms["gen_sg"] = stem
forms["dat_sg"] = stem
forms["acc_sg"] = stem
forms["abl_sg"] = stem
forms["voc_sg"] = stem
forms["nom_pl"] = stem
forms["gen_pl"] = stem
forms["dat_pl"] = stem
forms["acc_pl"] = stem
forms["abl_pl"] = stem
forms["voc_pl"] = stem
-- with locative
if data.loc then
forms["loc_sg"] = stem
forms["loc_pl"] = stem
end
end
local function alt_gen_pl_in_um(stem, forms, types, subtitles, i_stem)
if types.genplum then
insert_if_not(subtitles, mw.getCurrentFrame():preprocess("alternative genitive plural in {{m|la|-um|id=genitive plural}}"))
add_form(forms, "gen_pl", stem, (i_stem and "i" or "") .. "um")
elseif types.not_genplum then
-- FIXME: not a contraction.
insert_if_not(subtitles, "uncontracted genitive plural")
end
end
local function dat_abl_pl_subtitle(dat_abl_loc_pl, loc)
return ("dative/ablative%s plural in ''-%s''"):format(loc and "/locative" or "", dat_abl_loc_pl)
end
decl["1"] = function(data, args)
local forms, types, loc, subtitles = data.forms, data.types, data.loc, data.subtitles
local stem = args[1]
-- normal 1st
local nom_sg = "a"
local gen_loc_sg = "ae"
local dat_sg = "ae"
local acc_sg = "am"
local abl_sg = "ā"
local voc_sg = "a"
local nom_voc_pl = "ae"
local gen_pl = "ārum"
local dat_abl_loc_pl = "īs"
local acc_pl = "ās"
-- ām
if types.am then
insert_if_not(subtitles, "nominative/vocative singular in ''-ām''")
nom_sg = "ām"
acc_sg = "ām"
abl_sg = {"ām", "ā"}
voc_sg = "ām"
-- all Greek
elseif types.Greek then
--Greek M
if types.M then
insert_if_not(subtitles, "เพศชาย")
end
insert_if_not(subtitles, "ชนิดกรีก")
--Greek Ma
if types.Ma then
insert_if_not(subtitles, "nominative singular in ''-ās''")
nom_sg = "ās"
acc_sg = "ān"
voc_sg = "ā"
-- Greek Me
elseif types.Me then
insert_if_not(subtitles, "nominative singular in ''-ēs''")
nom_sg = "ēs"
acc_sg = "ēn"
abl_sg = "ē"
voc_sg = "ē"
-- Greek
else
nom_sg = "ē"
gen_loc_sg = "ēs"
acc_sg = "ēn"
abl_sg = "ē"
voc_sg = "ē"
end
end
-- ābus
if types.abus then
dat_abl_loc_pl = "ābus"
insert_if_not(subtitles, dat_abl_pl_subtitle(dat_abl_loc_pl, loc))
elseif types.not_abus then
insert_if_not(subtitles, dat_abl_pl_subtitle(dat_abl_loc_pl, loc))
end
add_form(forms, "nom_sg", stem, nom_sg)
add_form(forms, "gen_sg", stem, gen_loc_sg)
add_form(forms, "dat_sg", stem, dat_sg)
add_form(forms, "acc_sg", stem, acc_sg)
add_form(forms, "abl_sg", stem, abl_sg)
add_form(forms, "voc_sg", stem, voc_sg)
add_form(forms, {"nom_pl", "voc_pl"}, stem, nom_voc_pl)
add_form(forms, "gen_pl", stem, gen_pl)
add_form(forms, {"dat_pl", "abl_pl"}, stem, dat_abl_loc_pl)
add_form(forms, "acc_pl", stem, acc_pl)
-- with -um genitive plural
alt_gen_pl_in_um(stem, forms, types, subtitles)
-- with locative
if loc then
add_form(forms, "loc_sg", stem, gen_loc_sg)
add_form(forms, "loc_pl", stem, dat_abl_loc_pl)
end
end
decl["2"] = function(data, args)
local forms, types, subtitles, notes = data.forms, data.types, data.subtitles, data.notes
local stem1, stem2 = args[1], args[2]
-- normal 2nd
forms["nom_sg"] = stem1 .. "us"
forms["gen_sg"] = stem1 .. "ī"
forms["dat_sg"] = stem1 .. "ō"
forms["acc_sg"] = stem1 .. "um"
forms["abl_sg"] = stem1 .. "ō"
forms["voc_sg"] = stem1 .. "e"
forms["nom_pl"] = stem1 .. "ī"
forms["gen_pl"] = stem1 .. "ōrum"
forms["dat_pl"] = stem1 .. "īs"
forms["acc_pl"] = stem1 .. "ōs"
forms["abl_pl"] = stem1 .. "īs"
forms["voc_pl"] = stem1 .. "ī"
-- all neuter
if types.N then
insert_if_not(subtitles, "เพศกลาง")
forms["nom_sg"] = stem1 .. "um"
forms["voc_sg"] = stem1 .. "um"
forms["nom_pl"] = stem1 .. "a"
forms["acc_pl"] = stem1 .. "a"
forms["voc_pl"] = stem1 .. "a"
-- neuter ium
if types.ium then
forms["nom_sg"] = stem1 .. "ium"
forms["gen_sg"] = {stem1 .. "iī", stem1 .. "ī"}
forms["dat_sg"] = stem1 .. "iō"
forms["acc_sg"] = stem1 .. "ium"
forms["abl_sg"] = stem1 .. "iō"
forms["voc_sg"] = stem1 .. "ium"
forms["nom_pl"] = stem1 .. "ia"
forms["gen_pl"] = stem1 .. "iōrum"
forms["dat_pl"] = stem1 .. "iīs"
forms["acc_pl"] = stem1 .. "ia"
forms["abl_pl"] = stem1 .. "iīs"
forms["voc_pl"] = stem1 .. "ia"
notes["gen_sg2"] = "Found in older Latin (until the Augustan Age)."
-- neuter us, plural a
elseif types.a then
insert_if_not(subtitles, "nominative/accusative/vocative plural in ''-a''")
forms["nom_sg"] = stem1 .. "us"
forms["acc_sg"] = stem1 .. "us"
forms["voc_sg"] = stem1 .. "us"
forms["nom_pl"] = stem1 .. "a"
forms["acc_pl"] = stem1 .. "a"
forms["voc_pl"] = stem1 .. "a"
-- -vom (aevom, etc.)
elseif types.vom then
insert_if_not(subtitles, "nominative singular in ''-om'' after ''v''")
forms["nom_sg"] = stem1 .. "om"
forms["acc_sg"] = stem1 .. "om"
forms["voc_sg"] = stem1 .. "om"
-- neuter Greek in -os (cētos)
elseif types.Greek and types.us then
insert_if_not(subtitles, "ชนิดกรีก")
insert_if_not(subtitles, "nominative/accusative/vocative in ''-os''")
forms["nom_sg"] = stem1 .. "os"
forms["acc_sg"] = stem1 .. "os"
forms["voc_sg"] = stem1 .. "os"
forms["nom_pl"] = stem1 .. "ē"
forms["gen_pl"] = stem1 .. "ōn"
forms["acc_pl"] = stem1 .. "ē"
forms["voc_pl"] = stem1 .. "ē"
-- neuter Greek
elseif types.Greek then
insert_if_not(subtitles, "ชนิดกรีก")
forms["nom_sg"] = stem1 .. "on"
forms["acc_sg"] = stem1 .. "on"
forms["voc_sg"] = stem1 .. "on"
-- neuter us
elseif types.us then
insert_if_not(subtitles, "nominative/accusative/vocative in ''-us''")
forms["nom_sg"] = stem1 .. "us"
forms["acc_sg"] = stem1 .. "us"
forms["voc_sg"] = stem1 .. "us"
forms["nom_pl"] = stem1 .. "ī"
forms["acc_pl"] = stem1 .. "ōs"
forms["voc_pl"] = stem1 .. "ī"
elseif types.not_Greek or types.not_us then
insert_if_not(subtitles, "nominative/accusative/vocative in ''-um''")
end
-- er
elseif types.er then
insert_if_not(subtitles, ("nominative singular in ''-%s''"):format(umatch(toNFD(stem1), "[eE]" .. accent .. "r$") and "er" or "r"))
forms["nom_sg"] = stem1
forms["gen_sg"] = stem2 .. "ī"
forms["dat_sg"] = stem2 .. "ō"
forms["acc_sg"] = stem2 .. "um"
forms["abl_sg"] = stem2 .. "ō"
forms["voc_sg"] = stem1
forms["nom_pl"] = stem2 .. "ī"
forms["gen_pl"] = stem2 .. "ōrum"
forms["dat_pl"] = stem2 .. "īs"
forms["acc_pl"] = stem2 .. "ōs"
forms["abl_pl"] = stem2 .. "īs"
forms["voc_pl"] = stem2 .. "ī"
-- ius
elseif types.ius then
forms["nom_sg"] = stem1 .. "ius"
forms["gen_sg"] = {stem1 .. "iī", stem1 .. "ī"}
forms["dat_sg"] = stem1 .. "iō"
forms["acc_sg"] = stem1 .. "ium"
forms["abl_sg"] = stem1 .. "iō"
if types.voci then
-- Only for proper names and fīlius, genius
forms["voc_sg"] = stem1 .. "ī"
else
forms["voc_sg"] = stem1 .. "ie"
end
forms["nom_pl"] = stem1 .. "iī"
forms["gen_pl"] = stem1 .. "iōrum"
forms["dat_pl"] = stem1 .. "iīs"
forms["acc_pl"] = stem1 .. "iōs"
forms["abl_pl"] = stem1 .. "iīs"
forms["voc_pl"] = stem1 .. "iī"
notes["gen_sg2"] = "Found in older Latin (until the Augustan Age)."
-- -vos (servos, etc.)
elseif types.vos then
insert_if_not(subtitles, "nominative singular in ''-os'' after ''v''")
forms["nom_sg"] = stem1 .. "os"
forms["acc_sg"] = stem1 .. "om"
-- Greek
elseif types.Greek then
insert_if_not(subtitles, "ชนิดกรีก")
forms["nom_sg"] = stem1 .. "os"
forms["acc_sg"] = stem1 .. "on"
if data.num == "pl" then
forms["nom_pl"] = stem1 .. "oe"
forms["voc_pl"] = stem1 .. "oe"
else
forms["nom_pl"] = {stem1 .. "ī", stem1 .. "oe"}
forms["voc_pl"] = {stem1 .. "ī", stem1 .. "oe"}
end
elseif types.not_Greek then
insert_if_not(subtitles, "ชนิดไม่ใช่กรีก")
end
-- with -um genitive plural
alt_gen_pl_in_um(stem2, forms, types, subtitles, types.ius or types.ium)
-- with locative
if data.loc then
if types.ius or types.ium then
forms["loc_sg"] = stem2 .. "iī"
forms["loc_pl"] = stem2 .. "iīs"
else
forms["loc_sg"] = stem2 .. "ī"
forms["loc_pl"] = stem2 .. "īs"
end
end
end
local acc_sg_i_stem_subtypes = {
acc_im = {
-- amussis, basis, buris, cucumis, gummis, mephitis, paraphrasis, poesis, ravis, sitis, tussis, (vis) [abl -ī];
-- cannabis, senapis, sinapis [abl -e, -ī]
acc_sg = {"im"},
title = {"accusative singular in ''-im''"},
},
acc_im_in = {
-- cities, rivers, gods, e.g. Bilbilis, Syrtis, Tiberis, Anubis, Osiris [abl -ī];
-- Baetis, Tigris [acc -e, -ī]
acc_sg = {"im", "in"},
title = {"accusative singular in ''-im'' or ''-in''"},
},
acc_im_in_em = {
-- e.g. tigris, river Līris
acc_sg = {"im", "in", "em"},
title = {"accusative singular in ''-im'', ''-in'' or ''-em''"},
},
acc_im_em = {
acc_sg = {"im", "em"},
title = {"accusative singular in ''-im'' or ''-em''"},
},
acc_im_occ_em = {
-- febris, pelvis, puppis, restis, securis, turris [abl -ī, -e]
acc_sg = {"im", "em"},
title = {"accusative singular in ''-im'' or occasionally ''-em''"},
},
acc_em_im = {
-- aqualis, clavis, lens, navis [abl -e, -ī];
-- cutis, restis [abl -e]
acc_sg = {"em", "im"},
title = {"accusative singular in ''-em'' or ''-im''"},
},
}
local abl_sg_i_stem_subtypes = {
abl_i = {
-- amussis, basis, buris, cucumis, gummis, mephitis, paraphrasis, poesis, ravis, sitis, tussis, (vis) [acc -im];
-- cities, rivers, gods, e.g. Bilbilis, Syrtis, Tiberis, Anubis, Osiris [acc -im or -in];
-- canalis "water pipe", months in -is or -er, nouns originally i-stem adjectives such as aedilis, affinis, bipennis, familiaris, sodalis, volucris, etc. [acc -em]
abl_sg = {"ī"},
title = {"ablative singular in ''-ī''"},
},
abl_i_e = {
-- febris, pelvis, puppis, restis, securis, turris [acc -im, -em]
abl_sg = {"ī", "e"},
title = {"ablative singular in ''-ī'' or ''-e''"},
},
abl_i_occ_e = {
-- ignis [acc -em]
abl_sg = {"ī", "e"},
title = {"ablative singular in ''-ī'' or occasionally ''-e''"},
},
abl_e_i = {
-- cannabis, senapis, sinapis [acc -im];
-- Baetis, Tigris [acc -im, -in];
-- aqualis, clavis, lens, navis [acc -em, -im];
-- finis, mugilis, occiput, pugil, rus, supellex, vectis [acc -em]
abl_sg = {"e", "ī"},
title = {"ablative singular in ''-e'' or ''-ī''"},
},
abl_e_occ_i = {
-- amnis, anguis, avis, civis, classis, fustis, imber, orbis, pars, postis, sors, unguis, vesper [acc -em]
abl_sg = {"e", "ī"},
title = {"ablative singular in ''-e'' or occasionally ''-ī''"},
},
}
local function extract_stem(form, ending)
local base = umatch(form, "^(.*)" .. ending .. "$")
if not base then
error("Form " .. form .. " should end in -" .. ending)
end
return base
end
local function count_vowels(stem)
return select(2, toNFD(stem):gsub("[aeiouyAEIOUY]", ""))
end
decl["3"] = function(data, args)
local forms, types, subtitles, notes = data.forms, data.types, data.subtitles, data.notes
local stem1, stem2 = args[1], args[2]
local function non_i_stem_type()
return (count_vowels(stem1) > count_vowels(stem2) and "parisyllabic" or "imparisyllabic") .. " non-i-stem"
end
--normal 3rd
forms["nom_sg"] = stem1
forms["gen_sg"] = stem2 .. "is"
forms["dat_sg"] = stem2 .. "ī"
forms["acc_sg"] = stem2 .. "em"
forms["abl_sg"] = stem2 .. "e"
forms["voc_sg"] = stem1
forms["nom_pl"] = stem2 .. "ēs"
forms["gen_pl"] = stem2 .. "um"
forms["dat_pl"] = stem2 .. "ibus"
forms["acc_pl"] = stem2 .. "ēs"
forms["abl_pl"] = stem2 .. "ibus"
forms["voc_pl"] = stem2 .. "ēs"
local acc_sg_i_stem_subtype = false
local not_acc_sg_i_stem_subtype = false
for subtype, _ in pairs(types) do
if acc_sg_i_stem_subtypes[subtype] then
acc_sg_i_stem_subtype = true
break
end
end
for acc_sg_subtype, _ in pairs(acc_sg_i_stem_subtypes) do
if types["not_" .. acc_sg_subtype] then
not_acc_sg_i_stem_subtype = true
break
end
end
local abl_sg_i_stem_subtype = false
local not_abl_sg_i_stem_subtype = false
for subtype, _ in pairs(types) do
if abl_sg_i_stem_subtypes[subtype] then
abl_sg_i_stem_subtype = true
break
end
end
for abl_sg_subtype, _ in pairs(abl_sg_i_stem_subtypes) do
if types["not_" .. abl_sg_subtype] then
not_abl_sg_i_stem_subtype = true
break
end
end
-- all Greek
if types.Greek then
insert_if_not(subtitles, "ชนิดกรีก")
-- Greek er
if types.er then
insert_if_not(subtitles, "variant with nominative singular in ''-ēr''")
stem1 = extract_stem(stem1, "ēr")
forms["nom_sg"] = stem1 .. "ēr"
forms["gen_sg"] = stem1 .. "eris"
forms["dat_sg"] = stem1 .. "erī"
forms["acc_sg"] = {stem1 .. "era", stem1 .. "erem"}
forms["abl_sg"] = stem1 .. "ere"
forms["voc_sg"] = stem1 .. "ēr"
forms["nom_pl"] = stem1 .. "erēs"
forms["gen_pl"] = stem1 .. "erum"
forms["dat_pl"] = stem1 .. "eribus"
forms["acc_pl"] = stem1 .. "erēs"
forms["abl_pl"] = stem1 .. "eribus"
forms["voc_pl"] = stem1 .. "erēs"
-- Greek on
elseif types.on then
insert_if_not(subtitles, "variant with nominative singular in ''-ōn''")
stem1 = extract_stem(stem1, "ōn")
forms["nom_sg"] = stem1 .. "ōn"
forms["gen_sg"] = {stem1 .. "ontis", stem1 .. "ontos"}
forms["dat_sg"] = stem1 .. "ontī"
forms["acc_sg"] = stem1 .. "onta"
forms["abl_sg"] = stem1 .. "onte"
forms["voc_sg"] = stem1 .. "ōn"
forms["nom_pl"] = stem1 .. "ontēs"
forms["gen_pl"] = {stem1 .. "ontum", stem1 .. "ontium"}
forms["dat_pl"] = stem1 .. "ontibus"
forms["acc_pl"] = {stem1 .. "ontēs", stem1 .. "ontās"}
forms["abl_pl"] = stem1 .. "ontibus"
forms["voc_pl"] = stem1 .. "ontēs"
-- Greek i-stem
elseif types.I then
insert_if_not(subtitles, "i-stem")
forms["gen_sg"] = {stem2 .. "is", stem2 .. "eōs", stem2 .. "ios"}
forms["acc_sg"] = {stem2 .. "im", stem2 .. "in", stem2 .. "em"}
forms["abl_sg"] = {stem2 .. "ī", stem2 .. "e"}
forms["voc_sg"] = {stem2 .. "is", stem2 .. "i"}
notes["acc_sg3"] = "Found sometimes in Medieval and New Latin."
notes["abl_sg2"] = "Found sometimes in Medieval and New Latin."
forms["nom_pl"] = {stem2 .. "ēs", stem2 .. "eis"}
forms["gen_pl"] = {stem2 .. "ium", stem2 .. "eōn"}
forms["acc_pl"] = {stem2 .. "ēs", stem2 .. "eis"}
forms["voc_pl"] = {stem2 .. "ēs", stem2 .. "eis"}
if types.poetic_esi then
forms["dat_pl"] = {stem2 .. "ibus", stem2 .. "esi"}
forms["abl_pl"] = {stem2 .. "ibus", stem2 .. "esi"}
notes["dat_pl2"] = "Primarily in poetry."
notes["abl_pl2"] = "Primarily in poetry."
end
-- normal Greek
else
insert_if_not(subtitles, "normal variant")
forms["gen_sg"] = stem2 .. "os"
if umatch(toNFD(stem2), "[yY]" .. accent .. "$") then
forms["acc_sg"] = stem2 .. "n"
else
forms["acc_sg"] = stem2 .. "a"
end
forms["nom_pl"] = stem2 .. "es"
forms["acc_pl"] = stem2 .. "as"
forms["voc_pl"] = stem2 .. "es"
if umatch(toNFD(stem1), "[iyIY]" .. accent .. "s$") then
-- Per Hiley, words in -is and -ys have a poetic vocative
-- without the -s, but otherwise the vocative is the same
-- as the nominative.
forms["voc_sg"] = {stem1, (stem1:gsub("s$", ""))}
notes["voc_sg2"] = "In poetry."
end
end
elseif types.not_Greek then
insert_if_not(subtitles, "ชนิดไม่ใช่กรีก")
end
-- polis
if types.polis then
stem1 = extract_stem(stem1, "polis")
insert_if_not(subtitles, "i-stem")
insert_if_not(subtitles, "ชนิดกรีกบางส่วน")
forms["nom_sg"] = stem1 .. "polis"
forms["gen_sg"] = stem1 .. "polis"
forms["dat_sg"] = stem1 .. "polī"
forms["acc_sg"] = {stem1 .. "polim", stem1 .. "polin"}
forms["abl_sg"] = stem1 .. "polī"
forms["voc_sg"] = {stem1 .. "polis", stem1 .. "polī"}
elseif types.not_polis then
insert_if_not(subtitles, non_i_stem_type())
end
-- all neuter
if types.N then
insert_if_not(subtitles, "เพศกลาง")
forms["acc_sg"] = stem1
-- neuter I stem
if types.I then
-- pure variety
if types.pure then
insert_if_not(subtitles, "pure i-stem")
forms["abl_sg"] = stem2 .. "ī"
forms["nom_pl"] = stem2 .. "ia"
forms["gen_pl"] = stem2 .. "ium"
forms["acc_pl"] = stem2 .. "ia"
forms["voc_pl"] = stem2 .. "ia"
-- non-pure variety (rare)
else
insert_if_not(subtitles, "i-stem")
forms["nom_pl"] = stem2 .. "a"
forms["gen_pl"] = {stem2 .. "ium", stem2 .. "um"}
forms["acc_pl"] = stem2 .. "a"
forms["voc_pl"] = stem2 .. "a"
end
-- normal neuter
else
insert_if_not(subtitles, non_i_stem_type())
forms["nom_pl"] = stem2 .. "a"
forms["acc_pl"] = stem2 .. "a"
forms["voc_pl"] = stem2 .. "a"
end
-- I stem
elseif types.I or acc_sg_i_stem_subtype or abl_sg_i_stem_subtype then
insert_if_not(subtitles, (types.not_N and "non-neuter " or "") .. (types.pure and "pure " or "") .. "i-stem")
forms["gen_pl"] = stem2 .. "ium"
-- Per Allen and Greenough, Hiley and others, the acc_pl in -īs
-- applied originally to all i-stem nouns, and was current as an
-- alternative form up through Caesar.
forms["acc_pl"] = {stem2 .. "ēs", stem2 .. "īs"}
-- pure variety
if types.pure then
forms["acc_sg"] = stem2 .. "im"
forms["abl_sg"] = stem2 .. "ī"
forms["nom_pl"] = stem2 .. "īs"
forms["acc_pl"] = stem2 .. "īs"
forms["voc_pl"] = stem2 .. "īs"
end
local num = data.num
for subtype, _ in pairs(types) do
local acc_sg_i_stem_props = acc_sg_i_stem_subtypes[subtype]
if acc_sg_i_stem_props then
forms["acc_sg"] = {}
for _, ending in ipairs(acc_sg_i_stem_props.acc_sg) do
insert_if_not(forms["acc_sg"], stem2 .. ending)
end
if num ~= "pl" then
for _, t in ipairs(acc_sg_i_stem_props.title) do
insert_if_not(subtitles, t)
end
end
break
end
end
-- FIXME: needs to be done for neuter nouns as well.
for subtype, _ in pairs(types) do
local abl_sg_i_stem_props = abl_sg_i_stem_subtypes[subtype]
if abl_sg_i_stem_props then
forms["abl_sg"] = {}
for _, ending in ipairs(abl_sg_i_stem_props.abl_sg) do
insert_if_not(forms["abl_sg"], stem2 .. ending)
end
if num ~= "pl" then
for _, t in ipairs(abl_sg_i_stem_props.title) do
insert_if_not(subtitles, t)
end
end
break
end
end
elseif types.not_N and types.not_I then
insert_if_not(subtitles, "ไม่ใช่เพศกลาง " .. non_i_stem_type())
elseif types.not_N then
insert_if_not(subtitles, "ไม่ใช่เพศกลาง")
elseif types.not_I then
insert_if_not(subtitles, non_i_stem_type())
end
-- with locative
if data.loc then
-- As far as I can tell, in general both dative singular and
-- ablative singular could be used for the third-declension locative,
-- with different time periods preferring different forms.
-- http://dcc.dickinson.edu/grammar/latin/3rd-declension-locative-case
-- mentions rūrī along with either Carthāginī or Carthāgine.
-- Wikipedia in https://en.wikipedia.org/wiki/Locative_case#Latin
-- says this:
--
-- In archaic times, the locative singular of third declension nouns
-- was still interchangeable between ablative and dative forms, but in
-- the Augustan Period the use of the ablative form became fixed.
-- Therefore, both forms "rūrī" and "rūre" may be encountered.
--
-- Lewis and Short confirm this.
local loc_sg = forms["dat_sg"]
if type(loc_sg) == "table" then
loc_sg = deep_copy(loc_sg)
else
loc_sg = {loc_sg}
end
local abl_sg = forms["abl_sg"]
if type(abl_sg) == "table" then
for _, form in ipairs(abl_sg) do
insert_if_not(loc_sg, deep_copy(form))
end
else
insert_if_not(loc_sg, abl_sg)
end
forms["loc_sg"] = loc_sg
forms["loc_pl"] = deep_copy(forms["abl_pl"])
--The following is what we used to have, but I simply cannot believe it.
--if types.Greek and not types.s then
-- forms["loc_pl"] = stem2 .. "ēs"
--end
end
end
decl["4"] = function(data, args)
local forms, types, subtitles, notes = data.forms, data.types, data.subtitles, data.notes
local stem = args[1]
-- normal 4th
forms["nom_sg"] = stem .. "us"
forms["gen_sg"] = stem .. "ūs"
forms["dat_sg"] = stem .. "uī"
forms["acc_sg"] = stem .. "um"
forms["abl_sg"] = stem .. "ū"
forms["voc_sg"] = stem .. "us"
forms["nom_pl"] = stem .. "ūs"
forms["gen_pl"] = stem .. "uum"
forms["dat_pl"] = stem .. "ibus"
forms["acc_pl"] = stem .. "ūs"
forms["abl_pl"] = stem .. "ibus"
forms["voc_pl"] = stem .. "ūs"
if types.echo then
insert_if_not(subtitles, "all cases except the genitive and accusative singular in ''-ō''")
for slot in pairs(forms) do
if slot ~= "gen_sg" then
forms[slot] = stem .. "ō"
end
end
add_form(forms, "acc_sg", stem, "ōn")
elseif types.Callisto then
insert_if_not(subtitles, "all cases except the genitive singular in ''-ō''")
for slot in pairs(forms) do
if slot ~= "gen_sg" then
forms[slot] = stem .. "ō"
end
end
end
-- neuter
if types.N then
insert_if_not(subtitles, "เพศกลาง")
forms["nom_sg"] = stem .. "ū̆"
forms["gen_sg"] = {stem .. "ūs", stem .. "ū"}
forms["dat_sg"] = {stem .. "ū", stem .. "uī"}
forms["acc_sg"] = stem .. "ū̆"
forms["voc_sg"] = stem .. "ū̆"
forms["nom_pl"] = stem .. "ua"
forms["acc_pl"] = stem .. "ua"
forms["voc_pl"] = stem .. "ua"
local note1 = "The length of the final vowel is uncertain in the nominative/accusative/vocative singular; [[w:Martianus Capella|Martianus Capella]] considers it to end with ''-ū'', while [[w:Servius the Grammarian|Servius]] considers it to end with ''-ŭ''."
local note2 = "According to [[w:Martianus Capella|Martianus Capella]]."
local note3 = "According to [[w:Servius the Grammarian|Servius]]."
local note4 = "According to both."
notes["nom_sg1"] = note1
notes["gen_sg1"] = note2
notes["gen_sg2"] = note3
notes["dat_sg1"] = note4
notes["dat_sg2"] = note2
notes["acc_sg1"] = note1
notes["voc_sg1"] = note1
end
-- ubus
if types.ubus then
insert_if_not(subtitles, "dative/ablative plural in ''-ubus''")
forms["dat_pl"] = stem .. "ubus"
forms["abl_pl"] = stem .. "ubus"
elseif types.not_ubus then
insert_if_not(subtitles, "dative/ablative plural in ''-ibus''")
end
-- with locative
if data.loc then
forms["loc_sg"] = forms["abl_sg"]
forms["loc_pl"] = forms["abl_pl"]
end
end
decl["5"] = function(data, args)
local forms, types = data.forms, data.types
local stem = args[1]
-- ies
if types.i then
stem = stem .. "i"
end
forms["nom_sg"] = stem .. "ēs"
forms["gen_sg"] = stem .. "eī"
forms["dat_sg"] = stem .. "eī"
forms["acc_sg"] = stem .. "em"
forms["abl_sg"] = stem .. "ē"
forms["voc_sg"] = stem .. "ēs"
forms["nom_pl"] = stem .. "ēs"
forms["gen_pl"] = stem .. "ērum"
forms["dat_pl"] = stem .. "ēbus"
forms["acc_pl"] = stem .. "ēs"
forms["abl_pl"] = stem .. "ēbus"
forms["voc_pl"] = stem .. "ēs"
-- ies
if types.i then
forms["gen_sg"] = stem .. "ēī"
forms["dat_sg"] = stem .. "ēī"
end
--with locative
if data.loc then
forms["loc_sg"] = stem .. "ē"
forms["loc_pl"] = stem .. "ēbus"
end
end
decl["sgpl"] = function(data, args)
local forms = data.forms
local stem1, stem2 = args[1], args[2]
insert_if_not(data.subtitles, "with a distinct plural")
forms["nom_sg"] = stem1
forms["gen_sg"] = stem1
forms["dat_sg"] = stem1
forms["acc_sg"] = stem1
forms["abl_sg"] = stem1
forms["voc_sg"] = stem1
forms["nom_pl"] = stem2
forms["gen_pl"] = stem2
forms["dat_pl"] = stem2
forms["acc_pl"] = stem2
forms["abl_pl"] = stem2
forms["voc_pl"] = stem2
-- with locative
if data.loc then
forms["loc_sg"] = stem1
forms["loc_pl"] = stem2
end
end
decl["indecl"] = function(data, args)
local forms = data.forms
local stem = args[1]
insert_if_not(data.subtitles, "used only in the nominative and accusative")
forms["nom_sg"] = stem
forms["gen_sg"] = "-"
forms["dat_sg"] = "-"
forms["acc_sg"] = stem
forms["abl_sg"] = "-"
forms["voc_sg"] = "-"
forms["nom_pl"] = "-"
forms["gen_pl"] = "-"
forms["dat_pl"] = "-"
forms["acc_pl"] = "-"
forms["abl_pl"] = "-"
forms["voc_pl"] = "-"
-- with locative (for multiword terms)
if data.loc then
forms["loc_sg"] = "-"
forms["loc_pl"] = "-"
end
data.num = "sg"
end
decl["irreg"] = function(data, args)
local forms, subtitles, notes = data.forms, data.subtitles, data.notes
local stem = args[1]
forms["nom_sg"] = "-"
forms["gen_sg"] = "-"
forms["dat_sg"] = "-"
forms["acc_sg"] = "-"
forms["abl_sg"] = "-"
forms["voc_sg"] = "-"
forms["nom_pl"] = "-"
forms["gen_pl"] = "-"
forms["dat_pl"] = "-"
forms["acc_pl"] = "-"
forms["abl_pl"] = "-"
forms["voc_pl"] = "-"
if stem == "bōs" then
data.decl = "3"
local stem1, stem2 = "bōs", "bov"
decl["3"](data, {stem1, stem2})
add_form(forms, "abl_sg", stem2, "īd")
notes["abl_sg" .. #forms["abl_sg"]] = "Old Latin."
add_form(forms, "gen_pl", "bo", "um", 1)
add_form(forms, "gen_pl", stem2, "erum")
add_form(forms, "dat_pl", "", "bōbus", 1)
add_form(forms, "dat_pl", "", "būbus", 2)
add_form(forms, "abl_pl", "", "bōbus", 1)
add_form(forms, "abl_pl", "", "būbus", 2)
elseif stem:match("^[dD]eus$") then
data.decl = "2"
local dD = stem:sub(1, 1)
forms["nom_sg"] = dD .. "eus"
forms["gen_sg"] = dD .. "eī"
forms["dat_sg"] = dD .. "eō"
forms["acc_sg"] = dD .. "eum"
forms["abl_sg"] = dD .. "eō"
forms["voc_sg"] = {dD .. "eus", dD .. "ee"}
notes["voc_sg1"] = "Rare during the Classical period."
notes["voc_sg2"] = "From Late Latin onwards."
if stem == "deus" then
forms["nom_pl"] = {"dī", "diī", "deī"}
forms["gen_pl"] = {"deōrum", "deum"}
forms["dat_pl"] = {"dīs", "diīs", "deīs"}
forms["acc_pl"] = "deōs"
forms["abl_pl"] = {"dīs", "diīs", "deīs"}
forms["voc_pl"] = {"dī", "diī", "deī"}
end
elseif stem == "domus" then
data.decl = "4,2"
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 4|การผันรูปนามกลุ่มที่ 4]]/[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2|2]]"
forms["nom_sg"] = "domus"
forms["gen_sg"] = {"domūs", "domī"}
forms["dat_sg"] = {"domuī", "domō", "domū"}
forms["acc_sg"] = "domum"
forms["abl_sg"] = {"domū", "domō"}
forms["voc_sg"] = "domus"
forms["loc_sg"] = "domī"
forms["nom_pl"] = "domūs"
forms["gen_pl"] = {"domuum", "domōrum"}
forms["dat_pl"] = "domibus"
forms["acc_pl"] = {"domūs", "domōs"}
forms["abl_pl"] = "domibus"
forms["voc_pl"] = "domūs"
forms["loc_pl"] = "-"
data.loc = true
elseif umatch(stem, "^[IÏJ]ēs[uū]" .. BREVE .. "?s$") then
local ij = usub(stem, 1, 1)
local uu = umatch(stem, "[uū]" .. BREVE .. "?")
forms["nom_sg"] = stem
forms["gen_sg"] = ij .. "ēsū"
forms["dat_sg"] = ij .. "ēsū"
forms["acc_sg"] = ij .. "ēs" .. uu .. "m"
forms["abl_sg"] = ij .. "ēsū"
forms["voc_sg"] = ij .. "ēsū"
data.num = "sg"
elseif stem == "Mōsēs" or stem == "Moesēs" or umatch(stem, "^M[oō]" .. BREVE .. "?[īȳ]sēs$") then
local mos = usub(stem, 1, -3)
forms["nom_sg"] = stem
forms["gen_sg"] = {mos .. "ī", mos .. "ēī", mos .. "is", mos .. "ēn", mos .. "eōs"}
forms["dat_sg"] = mos .. "ī"
forms["acc_sg"] = {mos .. "ēn", mos .. "em"}
forms["abl_sg"] = {mos .. "e", mos .. "ēn", mos .. "ī"}
forms["voc_sg"] = {mos .. "ēs", mos .. "ē"}
data.num = "sg"
elseif stem:match("^[ij]ūgerum$") then
data.decl = "2,3"
local ij = stem:sub(1, 1)
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2|การผันรูปนามกลุ่มที่ 2]]–[[ภาคผนวก:การผันรูปนามกลุ่มที่ 3|3]] ลูกผสม"
insert_if_not(subtitles, "เพศกลาง")
forms["nom_sg"] = ij .. "ūgerum"
forms["gen_sg"] = ij .. "ūgerī"
forms["dat_sg"] = ij .. "ūgerō"
forms["acc_sg"] = ij .. "ūgerum"
forms["abl_sg"] = ij .. "ūgerō"
forms["voc_sg"] = ij .. "ūgerum"
forms["nom_pl"] = ij .. "ūgera"
forms["gen_pl"] = ij .. "ūgerum"
forms["dat_pl"] = ij .. "ūgeribus"
forms["acc_pl"] = ij .. "ūgera"
forms["abl_pl"] = {ij .. "ūgeribus", ij .. "ūgerīs"}
forms["voc_pl"] = ij .. "ūgera"
notes["abl_pl2"] = "Once only, in:<br/>M. Terentius Varro, ''Res Rusticae'', bk I, ch. x"
elseif stem == "sūs" then
data.decl = "3"
forms["nom_sg"] = "sūs"
forms["gen_sg"] = "suis"
forms["dat_sg"] = "suī"
forms["acc_sg"] = "suem"
forms["abl_sg"] = "sue"
forms["voc_sg"] = "sūs"
forms["nom_pl"] = "suēs"
forms["gen_pl"] = "suum"
forms["dat_pl"] = {"suibus", "sūbus", "subus"}
forms["acc_pl"] = "suēs"
forms["abl_pl"] = {"suibus", "sūbus", "subus"}
forms["voc_pl"] = "suēs"
elseif stem == "ēthos" then
data.decl = "3"
insert_if_not(subtitles, glossary_link("ไม่ปรกติ"))
insert_if_not(subtitles, "ชนิดกรีก")
forms["nom_sg"] = "ēthos"
forms["gen_sg"] = "ētheos"
forms["acc_sg"] = "ēthos"
forms["voc_sg"] = "ēthos"
forms["nom_pl"] = {"ēthea", "ēthē"}
forms["dat_pl"] = {"ēthesi", "ēthesin"}
forms["acc_pl"] = {"ēthea", "ēthē"}
forms["abl_pl"] = {"ēthesi", "ēthesin"}
forms["voc_pl"] = {"ēthea", "ēthē"}
elseif stem == "Athōs" then
data.decl = "2"
insert_if_not(subtitles, glossary_link("ไม่ปรกติ") .. "อย่างสูง")
insert_if_not(subtitles, "ชนิดกรีก")
forms["nom_sg"] = "Athōs"
forms["gen_sg"] = "Athō"
forms["dat_sg"] = "Athō"
forms["acc_sg"] = {"Athō", "Athōn"}
forms["abl_sg"] = "Athō"
forms["voc_sg"] = "Athōs"
data.num = "sg"
elseif stem:match("^[uv]ēnum$") then
data.decl = "4,2"
local uv = stem:sub(1, 1)
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่มที่ 4|การผันรูปนามกลุ่มที่ 4]]/[[ภาคผนวก:การผันรูปนามกลุ่มที่ 2|2]]"
insert_if_not(subtitles, glossary_link("บกพร่อง"))
forms["dat_sg"] = {uv .. "ēnuī", uv .. "ēnō"}
forms["acc_sg"] = uv .. "ēnum"
data.num = "sg"
elseif stem:match("^[uv]īs$") then
data.decl = "3"
local uv = stem:sub(1, 1)
insert_if_not(subtitles, glossary_link("ไม่ปรกติ"))
insert_if_not(subtitles, glossary_link("บกพร่อง"))
forms["nom_sg"] = uv .. "īs"
forms["gen_sg"] = uv .. "īs"
forms["dat_sg"] = uv .. "ī"
forms["acc_sg"] = uv .. "im"
forms["abl_sg"] = uv .. "ī"
forms["voc_sg"] = uv .. "īs"
forms["nom_pl"] = uv .. "īrēs"
forms["gen_pl"] = uv .. "īrium"
forms["dat_pl"] = uv .. "īribus"
forms["acc_pl"] = {uv .. "īrēs", uv .. "īrīs"}
forms["abl_pl"] = uv .. "īribus"
forms["voc_pl"] = uv .. "īrēs"
else
error("Stem " .. stem .. " not recognized.")
end
end
return decl
-- For Vim, so we get 4-space tabs
-- vim: set ts=4 sw=4 noet:
3u69cva3jpxbr4w6goz6p919e4tngip
มอดูล:la-nominal
828
281070
5776189
5632467
2026-11-02T01:35:41Z
Octahedron80
267
5776189
Scribunto
text/plain
local export = {}
--[=[
Authorship: Ben Wing <benwing2>, with many ideas and a little code coming from
the old [[Module:la-decl-multi]] by KC Kenny Lau.
]=]
-- TODO:
-- (DONE) Eliminate specification of noteindex from la-adj/data
-- (DONE?) Finish autodetection of adjectives
-- (DONE) Remove old noun code
-- (DONE) Implement <.sufn>
-- (DONE) Look into adj voc=false
-- (DONE) Handle loc in adjectives
-- Error on bad subtypes
-- Make sure Google Books link still works.
-- (DONE) Make sure .sufn triggers insertion of 'with m optionally -> n in compounds' in title.
-- (DONE) Make sure title returned to la-adj lowercases the first letter even with a custom title.
--[=[
TERMINOLOGY:
-- "slot" = A particular case/number combination (for nouns) or
case/number/gender combination (for adjectives). Example slot names are
"abl_sg" (for noun) or "acc_pl_f" (for adjectives). Each slot is filled
with zero or more forms.
-- "form" = The declined Latin form representing the value of a given slot.
For example, rēge is a form, representing the value of the abl_sg slot of
the lemma rēx.
-- "lemma" = The dictionary form of a given Latin term. For nouns, it's
generally the nominative singular, but will be the nominative plural of
plurale tantum nouns (e.g. [[castra]]), and may occasionally be another
form (e.g. the genitive singular) if the nominative singular is missing.
For adjectives, it's generally the masculine nominative singular, but
will be the masculine nominative plural of plurale tantum adjectives
(e.g. [[dēnī]]).
-- "plurale tantum" (plural "pluralia tantum") = A noun or adjective that
exists only in the plural. Examples are castra "army camp", faucēs "throat",
and dēnī "ten each" (used for counting pluralia tantum nouns).
-- "singulare tantum" (plural "singularia tantum") = A noun or adjective that
exists only in the singular. Examples are geōlogia "geology" (and in
general most non-count nouns) and the adjective ūnus "one".
]=]
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local headword_data_module = "Module:headword/data"
local json_module = "Module:JSON"
local la_adj_data_module = "Module:la-adj/data"
local la_adj_table_module = "Module:la-adj/table"
local la_noun_data_module = "Module:la-noun/data"
local la_noun_table_module = "Module:la-noun/table"
local la_utilities_module = "Module:la-utilities"
local languages_module = "Module:languages"
local links_module = "Module:links"
local load_module = "Module:load"
local parameters_module = "Module:parameters"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local concat = table.concat
local insert = table.insert
local iter_adj_slots -- defined below
local iter_noun_slots -- defined below
local umatch = mw.ustring.match
local function add_indefinite_article(...)
add_indefinite_article = require(en_utilities_module).add_indefinite_article
return add_indefinite_article(...)
end
local function contains(...)
contains = require(table_module).contains
return contains(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function lcfirst(...)
lcfirst = require(string_utilities_module).lcfirst
return lcfirst(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function make_adj_table(...)
make_adj_table = require(la_adj_table_module).make_table
return make_adj_table(...)
end
local function make_noun_table(...)
make_noun_table = require(la_noun_table_module).make_table
return make_noun_table(...)
end
local function make_noun_table_sg(...)
make_noun_table_sg = require(la_noun_table_module).make_table_sg
return make_noun_table_sg(...)
end
local function make_noun_table_pl(...)
make_noun_table_pl = require(la_noun_table_module).make_table_pl
return make_noun_table_pl(...)
end
local function make_stem2(...)
make_stem2 = require(la_utilities_module).make_stem2
return make_stem2(...)
end
local function normalize_form(...)
normalize_form = require(la_utilities_module).normalize_form
return normalize_form(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function singularize(...)
singularize = require(en_utilities_module).singularize
return singularize(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function ucfirst(...)
ucfirst = require(string_utilities_module).ucfirst
return ucfirst(...)
end
local m_adj_decl
local function get_m_adj_decl()
m_adj_decl, get_m_adj_decl = require(la_adj_data_module), nil
return m_adj_decl
end
local m_noun_decl
local function get_m_noun_decl()
m_noun_decl, get_m_noun_decl = require(la_noun_data_module), nil
return m_noun_decl
end
local lang
local function get_lang()
lang, get_lang = require(languages_module).getByCode("la")
return lang
end
local namespace
local function get_namespace()
namespace, get_namespace = load_data(headword_data_module).page.namespace, nil
return namespace
end
local pagename
local function get_pagename()
pagename, get_pagename = load_data(headword_data_module).pagename, nil
return pagename
end
local ligatures = {
['Ae'] = 'Æ',
['ae'] = 'æ',
['Oe'] = 'Œ',
['oe'] = 'œ',
}
local cases = {
"nom", "gen", "acc", "dat", "abl", "voc", "loc"
}
local cases_n = #cases
local nums = {
"sg", "pl"
}
local nums_n = #nums
local genders = {
"m", "f", "n"
}
local genders_n = #genders
local declension_to_english = setmetatable({
["1"] = "ที่ 1",
["1&2"] = "ที่ 1 และ 2",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}, {
__index = function(t, k)
return rawget(t, k:match("^[^+-]*"))
end
})
local number_to_english = {
"one", "two", "three", "four", "five"
}
local linked_prefixes = {
"", "linked_"
}
function export.iter_potential_noun_lemma_slots()
local num, case = 1, 0
return function()
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
return cases[case] .. "_" .. nums[num]
end
end
local potential_noun_lemma_slots = {}
for slot in export.iter_potential_noun_lemma_slots() do
insert(potential_noun_lemma_slots, slot)
end
local linked_to_non_linked_noun_slots = {}
for _, slot in ipairs(potential_noun_lemma_slots) do
linked_to_non_linked_noun_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with a noun declension, where a slot
-- is a particular case/number combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg, linked_nom_pl), which aren't overridable.
function export.iter_noun_slots(overridable_only)
local case, num, linked_variant = 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num]
end
end
iter_noun_slots = export.iter_noun_slots
function export.iter_potential_adj_lemma_slots()
local num, case, gen = 1, 1, 0
return function()
gen = gen + 1
if gen > genders_n then
gen = 1
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
end
return cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
-- List of adjective slots for which we generate linked variants. Include
-- feminine and neuter variants because they will be needed if the adjective
-- is part of a multiword feminine or neuter noun.
local potential_adj_lemma_slots = {}
for slot in export.iter_potential_adj_lemma_slots() do
insert(potential_adj_lemma_slots, slot)
end
local linked_to_non_linked_adj_slots = {}
for _, slot in ipairs(potential_adj_lemma_slots) do
linked_to_non_linked_adj_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with an adjective declension, where a slot
-- is a particular case/number/gender combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg_m, linked_nom_pl_m, etc.), which aren't overridable.
function export.iter_adj_slots(overridable_only)
local case, num, gen, linked_variant = 1, 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
gen = gen + 1
if gen > genders_n then
gen = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
iter_adj_slots = export.iter_adj_slots
-- Iterate over all the "slots" associated with a noun or adjective declension (depending on
-- the value of IS_ADJ), where a slot is a particular case/number combination (in the case of
-- nouns) or case/number/gender combination (in the case of adjectives). If OVERRIDABLE_ONLY
-- is specified, only include overridable slots (not including linked_ variants).
local function iter_slots(is_adj, overridable_only)
if is_adj then
return iter_adj_slots(overridable_only)
end
return iter_noun_slots(overridable_only)
end
local function concat_forms_in_slot(forms)
if forms and forms ~= "" and forms ~= "—" and #forms > 0 then
local new_vals = {}
for _, v in ipairs(forms) do
insert(new_vals, (v:gsub("|", "<!>")))
end
return concat(new_vals, ",")
end
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function track(page)
debug_track("la-nominal/" .. page)
return true
end
local function set_union(sets)
local union = {}
for _, set in ipairs(sets) do
for key, _ in pairs(set) do
union[key] = true
end
end
return union
end
local function set_difference(set1, set2)
local diff = {}
for key, _ in pairs(set1) do
if not set2[key] then
diff[key] = true
end
end
return diff
end
-- If a form is set as '*', that means it is unattested
-- but should still be generated
-- TODO: handle asterisks in forms stored in the data
local function unattested_forms(data, args, is_adj)
for slot in iter_slots(is_adj) do
local arg = args[slot]
if arg ~= nil then
arg = arg:match("^*(.*)")
if arg then
data.unattested[slot] = true
args[slot] = arg ~= "" and arg or nil
end
end
end
end
-- Make a link only if the form is attested
local function link_if_attested(form, accel, is_unattested)
local data = {lang = lang or get_lang()}
if is_unattested then
data.alt = "*" .. form
else
data.term = form
data.accel = accel
end
return full_link(data)
end
local function process_form(slot, data, args, linked_to_non_linked)
local forms = data.forms
-- If nomf=1 passed, clear out all masculine and feminine forms.
if data.nomf and slot:match("%f[^%z_][mf]%f[%z_]") then
forms[slot] = nil
end
-- If noneut=1 passed, clear out all neuter forms.
if data.noneut and slot:match("%f[^%z_]n%f[%z_]") then
forms[slot] = nil
end
local val
if args[slot] then
val = args[slot]
data.user_specified[slot] = true
else
-- Overridding nom_sg/nom_sg_m etc. should override linked_nom_sg
-- so that the correct value gets displayed in the headword, which
-- uses linked_nom_sg.
local non_linked_equiv_slot = linked_to_non_linked[slot]
if non_linked_equiv_slot and args[non_linked_equiv_slot] then
val = args[non_linked_equiv_slot]
data.user_specified[slot] = true
else
val = forms[slot]
end
end
if val then
if type(val) == "string" then
val = split(val, "/", true, true)
end
local num = data.num
if (
(num == "pl" and slot:find("sg", nil, true)) or
(num == "sg" and slot:find("pl", nil, true))
) then
forms[slot] = nil
elseif val[1] == "" or val[1] == "-" or val[1] == "—" then
forms[slot] = "—"
if val[2] then
error("Cannot specify additional forms for " .. slot .. ' if it has been cancelled with "-"')
end
else
forms[slot] = val
end
end
end
local function process_noun_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args);
-- Process overrides and canonicalize forms.
for slot in iter_noun_slots() do
process_form(slot, data, args, linked_to_non_linked_noun_slots)
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num]
else
accel_lemma = data.forms["nom_sg"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_noun_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]$", "|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
local function process_adj_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args, true)
-- Process overrides and canonicalize forms.
for slot in iter_adj_slots() do
process_form(slot, data, args, linked_to_non_linked_adj_slots)
end
-- See if the masculine and feminine/neuter are the same across all slots.
-- If so, blank out the feminine/neuter so we use a table that combines
-- masculine and feminine, or masculine/feminine/neuter.
for _, gender in ipairs({"f", "n"}) do
local other_is_masc = true
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
if not deep_equals(data.forms[case .. "_" .. num .. "_" .. gender],
data.forms[case .. "_" .. num .. "_m"]) then
other_is_masc = false
break
end
end
if not other_is_masc then
break
end
end
if other_is_masc then
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
data.forms[case .. "_" .. num .. "_" .. gender] = nil
end
end
end
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma, accel_lemma_f
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num .. "_m"]
accel_lemma_f = data.forms["nom_" .. data.num .. "_f"]
else
accel_lemma = data.forms["nom_sg_m"]
accel_lemma_f = data.forms["nom_sg_f"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
if type(accel_lemma_f) == "table" then
accel_lemma_f = accel_lemma_f[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_adj_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]_", "|%1|")
if data.noneut then
-- If noneut=1, we're being asked to do a noun like
-- Aquītānus or Rōmānus that has masculine and feminine
-- variants, not an adjective. In that case, make the
-- accelerators correspond to nominal case/number forms
-- without the gender, and use the feminine as the
-- lemma for feminine forms.
if slot:find("_f", nil, true) then
data.accel[slot] = {form = accel_form:gsub("|f$", ""), lemma = accel_lemma_f}
else
data.accel[slot] = {form = accel_form:gsub("|m$", ""), lemma = accel_lemma}
end
else
if not data.forms.nom_sg_n and not data.forms.nom_pl_n then
-- use multipart tags if called for
accel_form = accel_form:gsub("|m$", "|m//f//n")
elseif not data.forms.nom_sg_f and not data.forms.nom_pl_f then
accel_form = accel_form:gsub("|m$", "|m//f")
end
-- use the order nom|m|s, which is more standard than nom|s|m
accel_form = accel_form:gsub("|(.-)|(.-)$", "|%2|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
end
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
-- Convert data.forms[slot] for all slots into displayable text. This is
-- an older function, still currently used for nouns but not for adjectives.
-- For adjectives, the adjective table module has special code to combine
-- adjacent slots, and needs the original forms plus other text that will
-- go into the displayable text for the slot; this is handled below by
-- partial_show_forms() and finish_show_form().
local function show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" then
for i, form in ipairs(val) do
local link = link_if_attested(form, data.accel[slot], data.unattested[slot])
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
val[i] = link .. '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>'
else
val[i] = link
end
end
-- FIXME, do we want this difference?
data.forms[slot] = concat(val, is_adj and ", " or "<br />")
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Generate the display form for a set of slots with identical content. We
-- verify that the slots are actually identical, and throw an assertion error
-- if not. The display form is as in show_forms() but combines together all the
-- accelerator forms for all the slots.
local function finish_show_form(data, slots, is_adj)
assert(#slots > 0)
local slot1 = slots[1]
local forms = data.forms[slot1]
local notetext = data.notetext[slot1]
for _, slot in ipairs(slots) do
if not deep_equals(data.forms[slot], forms) then
error("data.forms[" .. slot1 .. "] = " .. (concat_forms_in_slot(forms) or "nil") ..
", but data.forms[" .. slot .. "] = " .. (concat_forms_in_slot(data.forms[slot]) or "nil"))
end
assert(deep_equals(data.notetext[slot], notetext))
end
if not forms then
return "—"
else
local accel_forms = {}
local accel_lemma = data.accel[slot1].lemma
for _, slot in ipairs(slots) do
assert(data.accel[slot].lemma == accel_lemma)
insert(accel_forms, data.accel[slot].form)
end
local combined_accel_form = concat(accel_forms, "|;|")
local accel = {form = combined_accel_form, lemma = accel_lemma}
local formtext = {}
for i, form in ipairs(forms) do
insert(formtext, link_if_attested(form, accel, data.unattested[slot1]) .. notetext[i])
end
-- FIXME, do we want this difference?
return concat(formtext, is_adj and ", " or "<br />")
end
end
-- Used by the adjective table module. This does some of the work of
-- show_forms(); in particular, it converts all empty forms of any format
-- (nil, "", "—") to nil and, if the forms aren't empty, generates the footnote
-- text associated with each form.
local function partial_show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
data.notetext = {}
-- Store this function in DATA so that it can be called from the adjective
-- table module without needing to require this module, which will (or
-- could) lead to recursive module requiring.
data.finish_show_form = finish_show_form
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if not val or val == "" or val == "—" then
data.forms[slot] = nil
else
local notetext = {}
for i in ipairs(val) do
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
insert(notetext, '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>')
else
insert(notetext, "")
end
end
data.notetext[slot] = notetext
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Given an ending (or possibly a full regex matching the entire lemma, if
-- a regex group is present), return the base minus the ending, or nil if
-- the ending doesn't match.
local function extract_base(lemma, ending)
if ending:find("(", nil, true) then
return umatch(lemma, ending)
end
return umatch(lemma, "^(.*)" .. ending .. "$")
end
-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- subtypes, where each pair consists of a single ending spec and a list of
-- subtypes), check each ending in turn against LEMMA. If it matches, return
-- the pair BASE, STEM2, SUBTYPES where BASE is the remainder of LEMMA minus
-- the ending, STEM2 is as passed in, and SUBTYPES is the subtypes associated
-- with the ending. But don't return SUBTYPES if any of the subtypes in the
-- list is specifically canceled in SPECIFIED_SUBTYPES (a set, i.e. a table
-- where the keys are strings and the value is always true); instead, consider
-- the next ending in turn. If no endings match, throw an error if DECLTYPE is
-- non-nil, mentioning the DECLTYPE (the user-specified declension); but if
-- DECLTYPE is nil, just return nil, nil, nil.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
local function get_noun_subtype_by_ending(lemma, stem2, decltype, specified_subtypes,
endings_and_subtypes)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local subtypes = ending_and_subtypes[2]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
-- In addition, M or F as a subtype is canceled by N, and
-- vice-versa, but M doesn't cancel F or vice-versa; instead,
-- we simply ignore the conflicting gender specification when
-- constructing the combination of specified and inferred subtypes.
-- The reason for this is that neuters have distinct declensions
-- from masculines and feminines, but masculines and feminines have
-- the same declension, and various nouns in Latin that are
-- normally masculine are exceptionally feminine and vice-versa
-- (nauta, agricola, fraxinus, malus "apple tree", manus, rēs,
-- etc.).
--
-- In addition, sg as a subtype is canceled by pl and vice-versa.
-- It's also possible to specify both, which will override sg but
-- not cancel it (in the sense that it won't prevent the relevant
-- rule from matching). For example, there's a rule specifying that
-- lemmas beginning with a capital letter and ending in -ius take
-- the ius.voci.sg subtypes. Specifying such a lemma with the
-- subtype both will result in the ius.voci.both subtypes, whereas
-- specifying such a lemma with the subtype pl will cause this rule
-- not to match, and it will fall through to a less specific rule
-- that returns just the ius subtype, which will be combined with
-- the explicitly specified pl subtype to produce ius.pl.
if specified_subtypes["-" .. subtype] or
subtype == "N" and (specified_subtypes.M or specified_subtypes.F) or
(subtype == "M" or subtype == "F") and specified_subtypes.N or
subtype == "sg" and specified_subtypes.pl or
subtype == "pl" and specified_subtypes.sg then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
local base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending == stem2 then
return base, stem2, subtypes
end
else
local base = extract_base(lemma, ending)
if base then
return base, stem2, subtypes
end
end
end
end
if decltype then
error("Unrecognized ending for declension-" .. decltype .. " noun: " .. lemma)
end
return nil, nil, nil
end
-- Autodetect the subtype of a noun given all the information specified by the
-- user: lemma, stem2, declension type and specified subtypes. Three values are
-- returned: the lemma base (i.e. the stem of the lemma, as required by the
-- declension functions), the new stem2 and the autodetected subtypes. Note
-- that this will not detect a given subtype if the explicitly specified
-- subtypes are incompatible (i.e. if -SUBTYPE is specified for any subtype
-- that would be returned; or if M or F is specified when N would be returned,
-- and vice-versa; or if pl is specified when sg would be returned, and
-- vice-versa).
--
-- NOTE: This function has intimate knowledge of the way that the declension
-- functions handle subtypes, particularly for the third declension.
local function detect_noun_subtype(lemma, stem2, typ, subtypes)
local base, _
if typ == "1" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ām", {"F", "am"}},
{"ās", {"M", "Greek", "Ma"}},
{"ēs", {"M", "Greek", "Me"}},
{"ē", {"F", "Greek"}},
{"ae", {"F", "pl"}},
{"a", {"F"}},
})
elseif typ == "2" then
local detected_subtypes
lemma, stem2, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(.*r)$", {"M", "er"}},
{"^(.*v)os$", {"M", "vos"}},
{"^(.*v)om$", {"N", "vom"}},
-- If the lemma ends in -os and the user said N or -M, then the
-- following won't apply, and the second (neuter) -os will applly.
{"os", {"M", "Greek"}},
{"os", {"N", "Greek", "us"}},
{"on", {"N", "Greek"}},
-- -ius beginning with a capital letter is assumed a proper name,
-- and takes the voci subtype (vocative in -ī) along with the ius
-- subtype and sg-only. Other nouns in -ius just take the ius
-- subtype. Explicitly specify "sg" so that if .pl is given,
-- this rule won't apply.
{"^(%u.*)ius$", {"M", "ius", "voci", "sg"}},
{"ius", {"M", "ius"}},
{"ium", {"N", "ium"}},
-- If the lemma ends in -us and the user said N or -M, then the
-- following won't apply, and the second (neuter) -us will applly.
{"us", {"M"}},
{"us", {"N", "us"}},
{"um", {"N"}},
{"iī", {"M", "ius", "pl"}},
{"ia", {"N", "ium", "pl"}},
-- If the lemma ends in -ī and the user said N or -M, then the
-- following won't apply, and the second (neuter) -ī will applly.
{"ī", {"M", "pl"}},
{"ī", {"N", "us", "pl"}},
{"oe", {"M", "Greek", "pl"}},
{"a", {"N", "pl"}},
})
stem2 = stem2 or lemma
return lemma, stem2, detected_subtypes
elseif typ == "3" then
if subtypes.pl then
if subtypes.Greek then
base = lemma:match("^(.*)erēs$")
if base then
return base .. "ēr", base .. "er", {"er"}
end
base = lemma:match("^(.*)ontēs$")
if base then
return base .. "ōn", base .. "ont", {"on"}
end
base = lemma:match("^(.*)es$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural Greek noun: " .. lemma)
end
base = lemma:match("^(.*)ia$")
if base then
return base, stem2 or base, {"N", "I", "pure"}
end
base = lemma:match("^(.*)a$")
if base then
return base, stem2 or base, {"N"}
end
base = lemma:match("^(.*)ēs$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural noun: " .. lemma)
end
stem2 = stem2 or make_stem2(lemma)
local detected_subtypes
if subtypes.Greek then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"is", ""}, {"I"}},
{"ēr", {"er"}},
{"ōn", {"on"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
end
if not subtypes.N then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"^(%u.*pol)is$", ""}, {"F", "polis", "sg", "loc"}},
{{"tūdō", "tūdin"}, {"F"}},
{{"tās", "tāt"}, {"F"}},
{{"tūs", "tūt"}, {"F"}},
{{"tiō", "tiōn"}, {"F"}},
{{"siō", "siōn"}, {"F"}},
{{"xiō", "xiōn"}, {"F"}},
{{"gō", "gin"}, {"F"}},
{{"or", "ōr"}, {"M"}},
{{"tr[iī]x", "trīc"}, {"F"}},
{{"is", ""}, {"I"}},
{{"^(%l.*)ēs$", ""}, {"I"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"us", "or"}, {"N"}},
{{"us", "er"}, {"N"}},
{{"ma", "mat"}, {"N"}},
{{"men", "min"}, {"N"}},
{{"^(%u.*)e$", ""}, {"N", "sg"}},
{{"e", ""}, {"N", "I", "pure"}},
{{"al", "āl"}, {"N", "I", "pure"}},
{{"ar", "ār"}, {"N", "I", "pure"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
elseif typ == "4" then
if subtypes.echo or subtypes.Callisto then
base = lemma:match("^(.*)ō$")
if not base then
error("Declension-4 noun of subtype .echo or .Callisto should end in -ō: " .. lemma)
end
if subtypes.Callisto then
return base, nil, {"F", "sg"}
else
return base, nil, {"F"}
end
end
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", {"M"}},
{"ū̆", {"N"}},
{"ūs", {"M", "pl"}},
{"ua", {"N", "pl"}},
})
elseif typ == "5" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"iēs", {"F", "i"}},
{"iēs", {"F", "i", "pl"}},
{"ēs", {"F"}},
{"ēs", {"F", "pl"}},
})
elseif typ == "sgpl" then
return lemma, stem2, {}
elseif typ == "irreg" and lemma == "domus" then
-- [[domus]] auto-sets data.loc = true, but we need to know this
-- before declining the noun so we can propagate it to other segments.
return lemma, nil, {"loc"}
elseif typ == "indecl" or typ == "irreg" and (
lemma == "Deus" or umatch(lemma, "^[IJ]ēs[uū]s$") or
lemma == "Athōs" or lemma == "vēnum"
) then
-- Indeclinable nouns, and certain irregular nouns, set data.num = "sg",
-- but we need to know this before declining the noun so we can
-- propagate it to other segments.
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", {"both"}},
{"", {"sg"}},
{"", {"pl"}},
})
else
return lemma, nil, {}
end
end
-- Given ENDINGS_AND_SUBTYPES (a list of four-tuples of ENDING, RETTYPE,
-- SUBTYPES, PROCESS_RETVAL), check each ENDING in turn against LEMMA and
-- STEM2. If it matches, return a four-tuple BASE, STEM2, RETTYPE, NEW_SUBTYPES
-- where BASE is normally the remainder of LEMMA minus the ending, STEM2 is
-- as passed in, RETTYPE is as passed in, and NEW_SUBTYPES is the same as
-- SUBTYPES minus any subtypes beginning with a hyphen. If no endings match,
-- throw an error if DECLTYPPE is non-nil, mentioning the DECLTYPE
-- (user-specified declension); but if DECLTYPE is nil, just return the tuple
-- nil, nil, nil, nil.
--
-- In order for a given entry to match, ENDING must match and also the subtypes
-- in SUBTYPES (a list) must not be incompatible with the passed-in
-- user-specified subtypes SPECIFIED_SUBTYPES (a set, i.e. a table where the
-- keys are strings and the value is always true). "Incompatible" means that
-- a given SUBTYPE is specified in either one and -SUBTYPE in the other, or
-- that "pl" is found in SPECIFIED_SUBTYPES and not in SUBTYPES.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
--
-- If PROCESS_STEM2 is given and the returned STEM2 would be nil, call
-- process_stem2(BASE) to get the STEM2 to return.
local function get_adj_type_and_subtype_by_ending(lemma, stem2, decltype,
specified_subtypes, endings_and_subtypes, process_stem2)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local rettype = ending_and_subtypes[2]
local subtypes = ending_and_subtypes[3]
local process_retval = ending_and_subtypes[4]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
if specified_subtypes["-" .. subtype] then
not_this_subtype = true
break
end
-- A subtype is canceled if the user specified SUBTYPE and
-- -SUBTYPE is given in the to-be-returned subtypes.
local must_not_be_present = subtype:match("^%-(.*)$")
if must_not_be_present and specified_subtypes[must_not_be_present] then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
local base
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending ~= stem2 then
base = nil
end
else
base = extract_base(lemma, ending)
end
if base then
-- Remove subtypes of the form -SUBTYPE from the subtypes
-- to be returned.
local new_subtypes = {}
for _, subtype in ipairs(subtypes) do
if subtype:sub(1, 1) ~= "-" then
insert(new_subtypes, subtype)
end
end
if process_retval then
base, stem2 = process_retval(base, stem2)
end
if process_stem2 then
stem2 = stem2 or process_stem2(base)
end
return base, stem2, rettype, new_subtypes
end
end
end
if not decltype then
return nil, nil, nil, nil
elseif decltype == "" then
error("Unrecognized ending for adjective: " .. lemma)
else
error("Unrecognized ending for declension-" .. decltype .. " adjective: " .. lemma)
end
end
-- Autodetect the type and subtype of an adjective given all the information
-- specified by the user: lemma, stem2, declension type and specified subtypes.
-- Four values are returned: the lemma base (i.e. the stem of the lemma, as
-- required by the declension functions), the value of stem2 to pass to the
-- declension function, the declension type and the autodetected subtypes.
-- Note that this will not detect a given subtype if -SUBTYPE is specified for
-- any subtype that would be returned, or if SUBTYPE is specified and -SUBTYPE
-- is among the subtypes that would be returned (such subtypes are filtered out
-- of the returned subtypes).
local function detect_adj_type_and_subtype(lemma, stem2, typ, subtypes)
local function base_as_stem2(base, stem2)
return base, base
end
local function constant_base(baseval)
return function(base, stem2)
return baseval, nil
end
end
local function decl12_stem2(base)
return base
end
local function decl3_stem2(base)
return make_stem2(base)
end
local decl12_entries = {
{"us", "1&2+", {}},
{"a", "1&2+", {}},
{"um", "1&2+", {}},
{"ī", "1&2+", {"pl"}},
{"ae", "1&2+", {"pl"}},
{"a", "1&2+", {"pl"}},
-- Nearly all -os adjectives are greekA
{"os", "1&2+", {"greekA", "-greekE"}},
{"os", "1&2+", {"greekE", "-greekA"}},
{"ē", "1&2+", {"greekE", "-greekA"}},
{"on", "1&2+", {"greekA", "-greekE"}},
{"on", "1&2+", {"greekE", "-greekA"}},
{"^(.*er)$", "1&2+", {"er"}},
{"^(.*ur)$", "1&2+", {"er"}},
{"^(h)ic$", "1&2+", {"ic"}},
}
local decl3_entries = {
{"^(.*er)$", "3-3+", {"I"}},
{"^(.*er)$", "3-3+", {}},
{"is", "3-2+", {"I"}},
{"is", "3-2+", {}},
{"e", "3-2+", {"I"}},
{"e", "3-2+", {}},
{"^(.*[ij])or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(.*[ij])or$", "3-C+", {}},
{"^(min)or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(min)or$", "3-C+", {}},
{"^(.*trīx)$", "3-def+", {"trix", "abl_i_e", "-abl_e_i"}},
{"^(.*trīx)$", "3-def+", {"trix"}},
{"^(.*tor)$", "3-def+", {"tor", "abl_i_e", "-abl_e_i"}},
{"^(.*tor)$", "3-def+", {"tor"}},
-- Detect -ēs as 3-1 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like quadripēs, volucripēs).
-- Essentially, for declension-3 adjectives, we require that
-- .pl is given if the lemma is plural.
--
-- Most 3-1 adjectives are i-stem (e.g. audāx) so we require -I
-- to be given with non-i-stem adjectives. The first entry below
-- will apply when -I isn't given, the second when it is given.
{"^(.*ēs)$", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"I"}},
{"^(.*ēs)$", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"par"}},
{"^(.*[ij])ōrēs$", "3-C+", {"pl"}},
{"^(min)ōrēs$", "3-C+", {"pl"}},
-- If .pl with -ēs, we don't know if the adjective is 3-1, 3-2
-- or 3-3. Since 3-2 is probably the most common, we infer it
-- (as well as the fact that these adjectives *are* in a sense
-- 3-2 since they have a distinct neuter in -(i)a. Note that
-- we have two entries here; the first one will apply unless
-- -I is given, and will infer an i-stem adjective; the second
-- one will apply otherwise (and infer a non-i-stem 3-1 adjective).
{"ēs", "3-2+", {"pl", "I"}, base_as_stem2},
{"ēs", "3-1+", {"pl", "par"}, base_as_stem2},
-- Same for neuters.
{"ia", "3-2+", {"pl", "I"}, base_as_stem2},
{"a", "3-1+", {"pl", "par"}, base_as_stem2},
-- As above for -ēs but for miscellaneous singulars.
{"", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"I"}},
{"", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"par"}},
}
local function get_subtypes_and_endings(typ, f)
local subtypes_and_endings = {}
for _, v in ipairs(decl3_entries) do
if f(v) then
v = deep_copy(v)
v[2] = typ
insert(subtypes_and_endings, v)
end
end
return subtypes_and_endings
end
local function compare(spec)
return spec[2] == typ
end
if typ == "+" then
local base, new_stem2, rettype, new_subtypes = get_adj_type_and_subtype_by_ending(lemma, stem2, nil, subtypes, decl12_entries, decl12_stem2)
if base then
return base, new_stem2, rettype, new_subtypes
else
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
end
elseif typ == "3+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
elseif typ == "1&2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl12_entries, decl12_stem2)
elseif typ == "1-1+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"a", typ, {}},
{"ae", typ, {"pl"}},
})
elseif typ == "2-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", typ, {}},
{"um", typ, {}},
{"ī", typ, {"pl"}},
{"a", typ, {"pl"}},
{"os", typ, {"greek"}},
{"on", typ, {"greek"}},
{"oe", typ, {"greek", "pl"}},
})
elseif typ == "3-1+" then
-- Use the specs from `decl3_entries`, but add the two i-stem 3-2+ specs when plural-only, as they're the same when 3-1+.
local subtypes_and_endings = get_subtypes_and_endings(typ, function(spec)
local spec1, spec2 = spec[1], spec[2]
return spec2 == typ or (spec2 == "3-2+" and spec1 == "ēs" or spec1 == "ia")
end)
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-def+" then
-- Like 3-1+ but defective in gender
local subtypes_and_endings = get_subtypes_and_endings(typ, compare)
insert(subtypes_and_endings, {"", typ, {"I"}})
insert(subtypes_and_endings, {"", typ, {"par"}})
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"is", typ, {}},
{"e", typ, {}},
-- Detect -ēs as 3-2 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like isoscelēs). Essentially,
-- for declension-3 adjectives, we require that .pl is given
-- if the lemma is plural.
{"ēs", typ, {}},
{"ēs", typ, {"pl"}},
{"ia", typ, {"pl"}},
}, decl3_stem2)
elseif typ == "3-3+" or typ == "3-P+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ēs", typ, {"pl"}, base_as_stem2},
{"ia", typ, {"pl"}, base_as_stem2},
{"", typ, {}},
}, decl3_stem2)
elseif typ == "3-C+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, get_subtypes_and_endings(typ, compare), decl3_stem2)
elseif typ == "irreg+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(duo)$", typ, {"pl"}},
{"^(ambō)$", typ, {"pl"}},
{"^(mīll?ia)$", typ, {"N", "pl"}, constant_base("mīlle")},
-- match ea
{"^(ea)$", typ, {}, constant_base("is")},
-- match id
{"^(id)$", typ, {}, constant_base("is")},
-- match plural eī, iī
{"^([ei]ī)$", typ, {"pl"}, constant_base("is")},
-- match plural ea, eae
{"^(eae?)$", typ, {"pl"}, constant_base("is")},
-- match eadem
{"^(eadem)$", typ, {}, constant_base("īdem")},
-- match īdem, idem
{"^([īi]dem)$", typ, {}, constant_base("īdem")},
-- match plural īdem
{"^(īdem)$", typ, {"pl"}},
-- match plural eadem, eaedem
{"^(eae?dem)$", typ, {"pl"}, constant_base("īdem")},
-- match illa, ipsa, ista; it doesn't matter if we overmatch because
-- we'll get an error as we use the stem itself in the returned base
{"^(i[lps][lst])a$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match illud, istud; as above, it doesn't matter if we overmatch
{"^(i[ls][lt])ud$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match ipsum
{"^(ipsum)$", typ, {}, constant_base("ipse")},
-- match plural illī, ipsī, istī; as above, it doesn't matter if we
-- overmatch
{"^(i[lps][lst])ī$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- match plural illa, illae, ipsa, ipsae, ista, istae; as above, it
-- doesn't matter if we overmatch
{"^(i[lps][lst])ae?$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- Detect quī as non-plural unless .pl specified.
{"^(quī)$", typ, {}},
-- Otherwise detect quī as plural.
{"^(quī)$", typ, {"pl"}},
-- Same for quae.
{"^(quae)$", typ, {}, constant_base("quī")},
{"^(quae)$", typ, {"pl"}, constant_base("quī")},
{"^(quid)$", typ, {}, constant_base("quis")},
{"^(quod)$", typ, {}, constant_base("quī")},
{"^(qui[cd]quid)$", typ, {}, constant_base("quisquis")},
{"^(quīquī)$", typ, {"pl"}, constant_base("quisquis")},
{"^(quaequae)$", typ, {"pl"}, constant_base("quisquis")},
-- match all remaining lemmas in lemma form
{"", typ, {}},
})
elseif typ == "indecl+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", typ, {"both"}},
{"", typ, {"sg"}},
{"", typ, {"pl"}},
})
else -- 0+
return lemma, nil, typ, {}
end
end
-- Parse a segment (e.g. "lūna<1>", "aegis/aegid<3.Greek>", "bōs<irreg.F>",
-- bonus<+>", or "[[vetus]]/veter<3+.-I>"), consisting of a lemma (or optionally
-- a lemma/stem) and declension+subtypes, where a + in the declension indicates
-- an adjective. Brackets can be present to indicate links, for use in
-- {{la-noun}} and {{la-adj}}. The return value is a table, e.g.:
-- {
-- decl = "1",
-- is_adj = false,
-- orig_lemma = "lūna",
-- lemma = "lūna",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"lūn"}
-- }
--
-- or
--
-- {
-- decl = "3",
-- is_adj = false,
-- orig_lemma = "aegis",
-- lemma = "aegis",
-- stem2 = "aegid",
-- gender = nil,
-- types = {["Greek"] = true},
-- args = {"aegis", "aegid"}
-- }
--
-- or
--
-- {
-- decl = "irreg",
-- is_adj = false,
-- orig_lemma = "bōs",
-- lemma = "bōs",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"bōs"}
-- }
-- or
--
-- {
-- decl = "1&2+",
-- is_adj = true,
-- orig_lemma = "bonus",
-- lemma = "bonus",
-- stem2 = nil,
-- gender = nil,
-- types = {},
-- args = {"bon"}
-- }
--
-- or
--
-- {
-- decl = "3-1+",
-- is_adj = true,
-- orig_lemma = "[[vetus]]",
-- lemma = "vetus",
-- stem2 = "veter",
-- gender = nil,
-- types = {},
-- args = {"vetus", "veter"}
-- }
local function parse_segment(segment)
local stem_part, spec_part = segment:match("^(.*)<(.-)>$")
local stems = split(stem_part, "/", true, true)
local specs = split(spec_part, ".", true, true)
local types = {}
local num = nil
local loc = false
local args = {}
local decl
for j, spec in ipairs(specs) do
if j == 1 then
decl = spec
else
local begins_with_hyphen
begins_with_hyphen, spec = spec:match("^(%-?)(.*)$")
spec = begins_with_hyphen .. spec:gsub("%-", "_")
types[spec] = true
end
end
local orig_lemma = stems[1]
if not orig_lemma or orig_lemma == "" then
orig_lemma = pagename or get_pagename()
end
local lemma = remove_links(orig_lemma)
local stem2 = stems[2]
if stem2 == "" then
stem2 = nil
end
if #stems > 2 then
error("Too many stems, at most 2 should be given: " .. stem_part)
end
local base, detected_subtypes
local is_adj = false
local gender = nil
if decl:find("+", nil, true) then
base, stem2, decl, detected_subtypes = detect_adj_type_and_subtype(lemma, stem2, decl, types)
is_adj = true
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
else
types[subtype] = true
end
end
else
base, stem2, detected_subtypes = detect_noun_subtype(lemma, stem2, decl, types)
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
elseif (subtype == "M" or subtype == "F" or subtype == "N") and
(types.M or types.F or types.N) then
-- if gender already specified, don't create conflicting gender spec
elseif (subtype == "sg" or subtype == "pl" or subtype == "both") and
(types.sg or types.pl or types.both) then
-- if number restriction already specified, don't create conflicting
-- number restriction spec
else
types[subtype] = true
end
end
if not types.pl and not types.both and umatch(lemma, "^%u") then
types.sg = true
end
end
if types.loc then
loc = true
types.loc = nil
end
if types.M then
gender = "M"
elseif types.F then
gender = "F"
elseif types.N then
gender = "N"
end
if types.pl then
num = "pl"
types.pl = nil
elseif types.sg then
num = "sg"
types.sg = nil
end
args[1] = base
args[2] = stem2
return {
decl = decl,
is_adj = is_adj,
gender = gender,
orig_lemma = orig_lemma,
lemma = lemma,
stem2 = stem2,
types = types,
num = num,
loc = loc,
args = args,
}
end
-- Parse a segment run (i.e. a string with zero or more segments [see
-- parse_segment] and optional surrounding text, e.g. "foenum<2>-graecum<2>"
-- or "[[pars]]/part<3.abl-e-occ-i> [[oratio|ōrātiōnis]]"). The segment run
-- currently cannot contain any alternants (e.g. "((epulum<2.sg>,epulae<1>))").
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments
-- has a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of per-word properties, where each element is an
-- object {
-- decl = DECL (declension),
-- types = TYPES (set describing the subtypes of a given word),
-- }
-- }
-- Each element in PARSED_SEGMENTS is as returned by parse_segment() but will
-- have an additional .orig_prefix field indicating the text before the segment
-- (including bracketed links) and corresponding .prefix field indicating the text
-- with bracketed links resolved. If there is trailing text, the last element will
-- have only .orig_prefix and .prefix fields containing that trailing text.
local function parse_segment_run(segment_run)
local loc = nil
local num = nil
local is_adj = nil
-- If the segment run begins with a hyphen, include the hyphen in the
-- set of allowed characters for a declined segment. This way, e.g. the
-- suffix [[-cen]] can be declared as {{la-ndecl|-cen/-cin<3>}} rather than
-- {{la-ndecl|-cen/cin<3>}}, which is less intuitive.
local is_suffix = segment_run:sub(1, 1) == "-"
local segments = {}
local propses = {}
-- We want to not break up a bracketed link followed by <> even if it has a space or
-- hyphen in it. So we do an outer capturing split to find the bracketed links followed
-- by <>, then do inner capturing splits on all the remaining text to find the other
-- declined terms.
local bracketed_segments = split(segment_run, "(%[%[[^%[%]]-%]%]<.->)")
for i, bracketed_segment in ipairs(bracketed_segments) do
if i % 2 == 0 then
insert(segments, bracketed_segment)
else
for _, subsegment in ipairs(split(bracketed_segment, is_suffix and "([^<> ,]+<.->)" or "([^<> ,%-]+<.->)")) do
insert(segments, subsegment)
end
end
end
local parsed_segments = {}
local gender = nil
for i = 2, (#segments - 1), 2 do
local parsed_segment = parse_segment(segments[i])
-- Overall locative is true if any segments call for locative.
loc = loc or parsed_segment.loc
-- The first specified value for num is used becomes the overall value.
num = num or parsed_segment.num
if is_adj == nil then
is_adj = parsed_segment.is_adj
else
is_adj = is_adj and parsed_segment.is_adj
end
gender = gender or parsed_segment.gender
parsed_segment.orig_prefix = segments[i - 1]
parsed_segment.prefix = remove_links(segments[i - 1])
parsed_segment.props = {
decl = parsed_segment.decl,
types = parsed_segment.types,
gender = parsed_segment.gender,
num = parsed_segment.num,
}
insert(parsed_segments, parsed_segment)
insert(propses, parsed_segment.props)
end
if segments[#segments] ~= "" then
insert(parsed_segments, {
orig_prefix = segments[#segments],
prefix = remove_links(segments[#segments]),
})
end
return {
segments = parsed_segments,
loc = loc,
num = num,
is_adj = is_adj,
gender = gender,
propses = propses,
}
end
-- Parse an alternant, e.g. "((epulum<2.sg>,epulae<1>))",
-- "((Serapis<3>,Serapis/Serapid<3>))" or
-- "((rēs<5>pūblica<1>,rēspūblica<1>))". The return value is a table of the form
-- {
-- alternants = PARSED_ALTERNANTS (a list of segment runs, each of which is a
-- list of parsed segments as returned by parse_segment_run()),
-- loc = LOC (a boolean indicating whether any of the individual segment runs
-- has a locative),
-- num = NUM (the overall number restriction, one of "sg", "pl" or "both"),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all non-constant alternants are adjectives, false
-- if all nouns, nil if only constant alternants; conflicting alternants
-- cause an error),
-- propses = PROPSES (list of lists of per-word property objecs),
-- }
local function parse_alternant(alternant)
local parsed_alternants = {}
local alternant_spec = alternant:match("^%(%((.-)%)%)$")
local alternants = split(alternant_spec, ",", true, true)
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i, alternant in ipairs(alternants) do
local parsed_run = parse_segment_run(alternant)
insert(parsed_alternants, parsed_run)
loc = loc or parsed_run.loc
-- First time through, set the overall num to the num of the first run,
-- even if nil. After that, if we ever see a run with a different value
-- of num, set the overall num to "both". That way, if all alternants
-- don't specify a num, we get an unspecified num, but if some do and
-- some don't, we get both, because an unspecified num defaults to
-- both.
if i == 1 then
num = parsed_run.num
elseif num ~= parsed_run.num then
-- FIXME, this needs to be rethought to allow for
-- adjective alternants.
num = "both"
end
gender = gender or parsed_run.gender
if is_adj == nil then
is_adj = parsed_run.is_adj
elseif parsed_run.is_adj ~= nil and parsed_run.is_adj ~= is_adj then
error("Saw both noun and adjective alternants; not allowed")
end
insert(propses, parsed_run.propses)
end
return {
alternants = parsed_alternants,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Parse a segment run (see parse_segment_run()). Unlike for
-- parse_segment_run(), this can contain alternants such as
-- "((epulum<2.sg>,epulae<1>))" or "((Serapis<3.sg>,Serapis/Serapid<3.sg>))"
-- embedded in it to indicate words composed of multiple declensions.
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments has
-- a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of either per-word property objects or lists of
-- lists of such objects),
-- }.
-- Each element in PARSED_SEGMENTS is one of three types:
--
-- 1. A regular segment, as returned by parse_segment() but with additional
-- .prefix and .orig_prefix fields indicating the text before the segment, as per
-- the return value of parse_segment_run().
-- 2. A raw-text segment, i.e. a table with only .prefix and .orig_prefix fields
-- containing the raw text.
-- 3. An alternating segment, as returned by parse_alternant().
-- Note that each alternant is a segment run rather than a single parsed
-- segment to allow for alternants like "((rēs<5>pūblica<1>,rēspūblica<1>))".
-- The parsed segment runs in PARSED_SEGMENT_RUNS are tables as returned by
-- parse_segment_run() (of the same form as the overall return value of
-- parse_segment_run_allowing_alternants()).
local function parse_segment_run_allowing_alternants(segment_run)
if segment_run:find(" ", nil, true) then
track("has-space")
end
if segment_run:find("((", nil, true) then
track("has-alternant")
end
local alternating_segments = split(segment_run, "(%(%(.-%)%))")
local parsed_segments = {}
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i = 1, #alternating_segments do
local alternating_segment = alternating_segments[i]
if alternating_segment ~= "" then
local this_is_adj
if i % 2 == 1 then
local parsed_run = parse_segment_run(alternating_segment)
for _, parsed_segment in ipairs(parsed_run.segments) do
insert(parsed_segments, parsed_segment)
end
loc = loc or parsed_run.loc
num = num or parsed_run.num
gender = gender or parsed_run.gender
this_is_adj = parsed_run.is_adj
for _, props in ipairs(parsed_run.propses) do
insert(propses, props)
end
else
local parsed_alternating_segment = parse_alternant(alternating_segment)
insert(parsed_segments, parsed_alternating_segment)
loc = loc or parsed_alternating_segment.loc
num = num or parsed_alternating_segment.num
gender = gender or parsed_alternating_segment.gender
this_is_adj = parsed_alternating_segment.is_adj
insert(propses, parsed_alternating_segment.propses)
end
if is_adj == nil then
is_adj = this_is_adj
elseif this_is_adj ~= nil then
is_adj = is_adj and this_is_adj
end
end
end
if #parsed_segments > 1 then
track("multiple-segments")
end
return {
segments = parsed_segments,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Combine each form in FORMS (a list of forms associated with a slot) with each
-- form in NEW_FORMS (either a single string for a single form, or a list of
-- forms) by concatenating EXISTING_FORM .. PREFIX .. NEW_FORM. Also combine
-- NOTES (a table specifying the footnotes associated with each existing form,
-- i.e. a map from form indices to lists of footnotes) with NEW_NOTES (new
-- footnotes associated with the new forms, in the same format as NOTES). Return
-- a pair NEW_FORMS, NEW_NOTES where either or both of FORMS and NOTES (but not
-- the sublists in NOTES) may be destructively modified to generate the return
-- values.
local function append_form(forms, notes, new_forms, new_notes, prefix)
if forms == nil then
return
end
new_forms = new_forms or ""
notes = notes or {}
new_notes = new_notes or {}
prefix = prefix or ""
if type(new_forms) == "table" and #new_forms == 1 then
new_forms = new_forms[1]
end
if type(new_forms) == "string" then
-- If there's only one new form, destructively modify the existing
-- forms and notes for this new form and its footnotes.
for i = 1, #forms do
forms[i] = forms[i] .. prefix .. new_forms
if new_notes[1] then
if not notes[i] then
notes[i] = new_notes[1]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[1]) do
insert(combined_notes, note)
end
notes[i] = combined_notes
end
end
end
return forms, notes
else
-- If there are multiple new forms, we need to loop over all
-- combinations of new and old forms. In that case, use new tables
-- for the combined forms and notes.
local ret_forms = {}
local ret_notes = {}
for i=1, #forms do
for j=1, #new_forms do
insert(ret_forms, forms[i] .. prefix .. new_forms[j])
if new_notes[j] then
if not notes[i] then
-- We are constructing a linearized matrix of size
-- NI x NJ where J is in the inner loop. If I and J
-- are zero-based, the linear index of (I, J) is
-- I * NJ + J. However, we are one-based, so the
-- same formula won't work. Instead, we effectively
-- need to convert to zero-based indices, compute
-- the zero-based linear index, and then convert it
-- back to a one-based index, i.e.
--
-- (I - 1) * NJ + (J - 1) + 1
--
-- i.e. (I - 1) * NJ + J.
ret_notes[(i - 1) * #new_forms + j] = new_notes[j]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[j]) do
insert(combined_notes, note)
end
ret_notes[(i - 1) * #new_forms + j] = combined_notes
end
end
end
end
return ret_forms, ret_notes
end
end
-- Destructively modify any forms in FORMS (a map from a slot to a form or a
-- list of forms) by converting sequences of ae, oe, Ae or Oe to the
-- appropriate ligatures.
local function apply_ligatures(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
forms[slot] = forms[slot]:gsub("[AaOo]e", ligatures)
elseif type(forms[slot]) == "table" then
for i = 1, #forms[slot] do
forms[slot][i] = forms[slot][i]:gsub("[AaOo]e", ligatures)
end
end
end
end
-- Modify any forms in FORMS (a map from a slot to a form or a list of forms) by
-- converting final m to optional n or m.
local function apply_sufn(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
if forms[slot]:sub(-1) == "m" then
forms[slot] = {forms[slot]:gsub("m$", "n"), forms[slot]}
end
elseif type(forms[slot]) == "table" then
-- See if there are any final m's.
local final_m
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
final_m = true
break
end
end
if final_m then
local newval = {}
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
insert(newval, (forms[slot][i]:gsub("m$", "n")))
end
insert(newval, forms[slot][i])
end
forms[slot] = newval
end
end
end
end
-- If NUM == "sg", copy the singular forms to the plural ones; vice-versa if
-- NUM == "pl". This should allow for the equivalent of plural
-- "alpha and omega" formed from two singular nouns, and for the equivalent of
-- plural "St. Vincent and the Grenadines" formed from a singular noun and a
-- plural noun. (These two examples actually occur in Russian, at least.)
local function propagate_number_restrictions(forms, num, is_adj)
if num == "sg" or num == "pl" then
for slot in iter_slots(is_adj) do
if slot:find(num, nil, true) then
local other_num_slot = num == "sg" and slot:gsub("sg", "pl") or slot:gsub("pl", "sg")
forms[other_num_slot] = type(forms[slot]) == "table" and deep_copy(forms[slot]) or forms[slot]
end
end
end
end
local function join_sentences(sentences, joiner)
-- Lowercase the first letter of all but the first sentence, and remove the
-- final period from all but the last sentence. Then join together with the
-- joiner (e.g. " and " or " or ").
-- FIXME: Should we join three or more as e.g. "foo, bar and baz"?
local sentences_to_join = {}
for i, sentence in ipairs(sentences) do
if i < #sentences then
sentence = sentence:gsub("%.$", "")
end
if i > 1 then
sentence = lcfirst(sentence)
end
insert(sentences_to_join, sentence)
end
return concat(sentences_to_join, joiner)
end
-- Construct the declension of a parsed segment run of the form returned by
-- parse_segment_run() or parse_segment_run_allowing_alternants(). Return value
-- is a table
-- {
-- forms = FORMS (keyed by slot, list of forms for that slot),
-- notes = NOTES (keyed by slot, map from form indices to lists of footnotes),
-- title = TITLE (list of titles for each segment in the run),
-- categories = CATEGORIES (combined categories for all segments),
-- }
local function decline_segment_run(parsed_run, pos, is_adj)
local declensions = {
-- For each possible slot (e.g. "abl_sg"), list of possible forms.
forms = {},
-- Keyed by slot (e.g. "abl_sg"). Value is a table indicating the footnotes
-- corresponding to the forms for that slot. Each such table maps indices
-- (the index of the corresponding form) to a list of one or more
-- footnotes.
notes = {},
title = {},
unattested = {},
subtitleses = {},
orig_titles = {},
categories = {},
footnotes = {},
-- May be set true if declining a 1-1 adjective
loc = false,
noneut = false,
nomf = false,
}
for slot in iter_slots(is_adj) do
declensions.forms[slot] = {""}
end
for i, seg in ipairs(parsed_run.segments) do
local decl = seg.decl
if decl then -- not an alternant, not a constant segment
seg.loc = parsed_run.loc
seg.num = seg.num or parsed_run.num
seg.gender = seg.gender or parsed_run.gender
local data, potential_lemma_slots
if seg.is_adj then
if not (m_adj_decl or get_m_adj_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_adj_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
gender = seg.gender,
loc = seg.loc,
noneut = false,
nomf = false,
pos = is_adj and pos or "คำคุณศัพท์",
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_adj_decl or get_m_adj_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
if data.loc then
declensions.loc = true
end
if data.noneut then
declensions.noneut = true
end
if data.nomf then
declensions.nomf = true
end
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg+" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. english .. " ภาษาละติน|การผันรูปนามกลุ่ม" .. english .. "]]"
elseif apparent_decl == "irreg+" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl+" or apparent_decl == "0+" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize adjective declension " .. apparent_decl)
end
data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with ''idem'', ''quīdam''
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
else
if not (m_noun_decl or get_m_noun_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_noun_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
loc = seg.loc,
pos = pos,
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_noun_decl or get_m_noun_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
seg.props.headword_decl = apparent_decl
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:Latin " .. english .. " declension|" .. english .. "-declension]]"
elseif apparent_decl == "irreg" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl" or apparent_decl == "0" or apparent_decl == "sgpl" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize noun declension " .. apparent_decl)
end
data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with 1st-declension ''-ābus'' ending where
-- we want a common prefix to be extracted out if possible
-- in the alternant title-generating code.
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
end
-- Generate linked variants of slots that may be the lemma.
-- If the form is the same as the lemma (with links removed),
-- substitute the original lemma (with links included).
for _, slot in ipairs(potential_lemma_slots) do
local forms = data.forms[slot]
if forms then
local linked_forms = {}
if type(forms) ~= "table" then
forms = {forms}
end
for _, form in ipairs(forms) do
if form == seg.lemma then
insert(linked_forms, seg.orig_lemma)
else
insert(linked_forms, form)
end
end
data.forms["linked_" .. slot] = linked_forms
end
end
if seg.types.lig then
apply_ligatures(data.forms, is_adj)
end
if seg.types.sufn then
apply_sufn(data.forms, is_adj)
end
propagate_number_restrictions(data.forms, seg.num, is_adj)
for slot in iter_slots(is_adj) do
-- 1. Select the forms to append to the existing ones.
local new_forms
if is_adj then
if not seg.is_adj then
error("Can't decline noun '" .. seg.lemma .. "' when overall term is an adjective")
end
new_forms = data.forms[slot]
if not new_forms and slot:find("_[fn]$") then
new_forms = data.forms[slot:gsub("_[fn]$", "_m")]
end
elseif seg.is_adj then
if not seg.gender then
error("Declining modifying adjective " .. seg.lemma .. " but don't know gender of associated noun")
end
-- Select the appropriately gendered equivalent of the case/number
-- combination. Some adjectives won't have feminine or neuter
-- variants, though (e.g. 3-1 and 3-2 adjectives don't have a
-- distinct feminine), so in that case select the masculine.
new_forms = data.forms[slot .. "_" .. mw.ustring.lower(seg.gender)]
or data.forms[slot .. "_m"]
else
new_forms = data.forms[slot]
end
-- 2. Extract the new footnotes in the format we require, which is
-- different from the format passed in by the declension functions.
local new_notes = {}
if type(new_forms) == "string" and data.notes[slot .. "1"] then
new_notes[1] = {data.notes[slot .. "1"]}
elseif new_forms then
for j = 1, #new_forms do
if data.notes[slot .. j] then
new_notes[j] = {data.notes[slot .. j]}
end
end
end
-- 3. Append new forms and footnotes to the existing ones.
new_forms = normalize_form(new_forms)
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot], new_forms,
new_notes, slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
end
for slot, v in pairs(data.unattested) do
if v then
declensions.unattested[slot] = true
end
end
if not seg.types.nocat and (is_adj or not seg.is_adj) then
for _, cat in ipairs(data.categories) do
insert_if_not(declensions.categories, cat)
end
end
if data.footnote then
insert(declensions.footnotes, data.footnote)
end
if seg.prefix ~= "" and seg.prefix ~= "-" and seg.prefix ~= " " then
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
insert(declensions.title, data.title)
elseif seg.alternants then
local seg_declensions = nil
local seg_titles = {}
local seg_subtitleses = {}
local seg_stems_seen = {}
local seg_unattested = {}
local seg_categories = {}
local seg_footnotes = {}
-- If all alternants have exactly one non-constant segment and all are
-- of the same declension, we use special code that displays the
-- differences in the subtitles. Otherwise we use more general code
-- that displays the full title and subtitles of each segment,
-- separating segment combined titles by "and" and the segment-run
-- combined titles by "or".
local title_the_hard_way = false
local alternant_decl = nil
local alternant_decl_title = nil
for _, this_parsed_run in ipairs(seg.alternants) do
local num_non_constant_segments = 0
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
if not alternant_decl then
alternant_decl = segment.decl
elseif alternant_decl ~= segment.decl then
title_the_hard_way = true
num_non_constant_segments = 500
break
end
num_non_constant_segments = num_non_constant_segments + 1
end
end
if num_non_constant_segments ~= 1 then
title_the_hard_way = true
break
end
end
if not title_the_hard_way then
-- If using the special-purpose code, find the subtypes that are
-- not present in a given alternant but are present in at least
-- one other, and record "negative" variants of these subtypes
-- so that the declension-construction code can record subtitles
-- for these negative variants (so we can construct text like
-- "i-stem or imparisyllabic non-i-stem").
local subtypeses = {}
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
insert(subtypeses, segment.types)
insert_if_not(seg_stems_seen, segment.stem2)
end
end
end
local union = set_union(subtypeses)
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
local neg_subtypes = set_difference(union, segment.types)
for neg_subtype, _ in pairs(neg_subtypes) do
segment.types["not_" .. neg_subtype] = true
end
end
end
end
end
for _, this_parsed_run in ipairs(seg.alternants) do
this_parsed_run.loc = seg.loc
this_parsed_run.num = this_parsed_run.num or seg.num
this_parsed_run.gender = this_parsed_run.gender or seg.gender
local this_declensions = decline_segment_run(this_parsed_run, pos, is_adj)
if this_declensions.noneut then
declensions.noneut = true
end
if this_declensions.nomf then
declensions.nomf = true
end
-- If there's a number restriction on the segment run, blank
-- out the forms outside the restriction. This allows us to
-- e.g. construct heteroclites that decline one way in the
-- singular and a different way in the plural.
if this_parsed_run.num == "sg" or this_parsed_run.num == "pl" then
for slot in iter_slots(is_adj) do
if this_parsed_run.num == "sg" and slot:find("pl", nil, true) or
this_parsed_run.num == "pl" and slot:find("sg", nil, true) then
this_declensions.forms[slot] = {}
this_declensions.notes[slot] = nil
end
end
end
if not seg_declensions then
seg_declensions = this_declensions
else
for slot in iter_slots(is_adj) do
-- For a given slot, combine the existing and new forms.
-- We do this by checking to see whether a new form is
-- already present and not adding it if so; in the
-- process, we keep a map from indices in the new forms
-- to indices in the combined forms, for use in
-- combining footnotes below.
local curforms = seg_declensions.forms[slot] or {}
local newforms = this_declensions.forms[slot] or {}
local newform_index_to_new_index = {}
for newj, form in ipairs(newforms) do
local did_break = false
for j = 1, #curforms do
if curforms[j] == form then
newform_index_to_new_index[newj] = j
did_break = true
break
end
end
if not did_break then
insert(curforms, form)
newform_index_to_new_index[newj] = #curforms
end
end
seg_declensions.forms[slot] = curforms
-- Now combine the footnotes. Keep in mind that
-- each form may have its own set of footnotes, and
-- in some cases we didn't add a form from the new
-- list of forms because it already occurred in the
-- existing list of forms; in that case, we combine
-- footnotes from the two sources.
local curnotes = seg_declensions.notes[slot]
local newnotes = this_declensions.notes[slot]
if newnotes then
if not curnotes then
curnotes = {}
end
for index, notes in pairs(newnotes) do
local combined_index = newform_index_to_new_index[index]
if not curnotes[combined_index] then
curnotes[combined_index] = notes
else
local combined = mw.clone(curnotes[combined_index])
for _, note in ipairs(newnotes) do
insert_if_not(combined, note)
end
curnotes[combined_index] = combined
end
end
end
end
end
for slot, v in pairs(this_declensions.unattested) do
if v then
seg_unattested[slot] = true
end
end
for _, cat in ipairs(this_declensions.categories) do
insert_if_not(seg_categories, cat)
end
for _, footnote in ipairs(this_declensions.footnotes) do
insert_if_not(seg_footnotes, footnote)
end
insert_if_not(seg_titles, this_declensions.title)
for _, subtitles in ipairs(this_declensions.subtitleses) do
insert(seg_subtitleses, subtitles)
end
if not alternant_decl_title then
alternant_decl_title = this_declensions.orig_titles[1]
end
end
-- If overall run is singular, copy singular to plural, and
-- vice-versa. See propagate_number_restrictions() for rationale;
-- also, this should eliminate cases of empty forms, which will
-- cause the overall set of forms for that slot to be empty.
propagate_number_restrictions(seg_declensions.forms, parsed_run.num, is_adj)
for slot in iter_slots(is_adj) do
local new_forms = normalize_form(seg_declensions.forms[slot])
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
new_forms, seg_declensions.notes[slot], nil)
end
end
for slot, v in pairs(seg_unattested) do
if v then
declensions.unattested[slot] = true
end
end
if is_adj or not seg.is_adj then
for _, cat in ipairs(seg_categories) do
insert_if_not(declensions.categories, cat)
end
end
for _, footnote in ipairs(seg_footnotes) do
insert_if_not(declensions.footnotes, footnote)
end
local title_to_insert
if title_the_hard_way then
title_to_insert = join_sentences(seg_titles, " or ")
else
-- Special-purpose title-generation code, for the common
-- situation where each alternant has single-segment runs and
-- all segments belong to the same declension.
--
-- 1. Find the initial subtitles common to all segments.
local first_subtitles = seg_subtitleses[1]
local num_common_subtitles = #first_subtitles
for j = 2, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
for k = 1, num_common_subtitles do
if not deep_equals(first_subtitles[k], this_subtitles[k]) then
num_common_subtitles = k - 1
break
end
end
end
-- 2. Construct the portion of the text based on the common subtitles.
local common_subtitles = {}
for j = 1, num_common_subtitles do
if type(first_subtitles[j]) == "table" then
insert(common_subtitles, concat(first_subtitles[j]))
else
insert(common_subtitles, first_subtitles[j])
end
end
local common_subtitle_portion = concat(common_subtitles, ", ")
local non_common_subtitle_portion
-- 3. Special-case the situation where there's one non-common
-- subtitle in each segment and a common prefix or suffix to
-- all of them.
local common_prefix, common_suffix
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
if #this_subtitles ~= num_common_subtitles + 1 or
type(this_subtitles[num_common_subtitles + 1]) ~= "table" or
#this_subtitles[num_common_subtitles + 1] ~= 2 then
break
end
if j == 1 then
common_prefix = this_subtitles[num_common_subtitles + 1][1]
common_suffix = this_subtitles[num_common_subtitles + 1][2]
else
local this_prefix = this_subtitles[num_common_subtitles + 1][1]
local this_suffix = this_subtitles[num_common_subtitles + 1][2]
if this_prefix ~= common_prefix then
common_prefix = nil
end
if this_suffix ~= common_suffix then
common_suffix = nil
end
if not common_prefix and not common_suffix then
break
end
end
end
if common_prefix or common_suffix then
if common_prefix and common_suffix then
error("Something is wrong, first non-common subtitle is actually common to all segments")
end
if common_prefix then
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][2])
end
non_common_subtitle_portion = common_prefix .. concat(non_common_parts, " or ")
else
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][1])
end
non_common_subtitle_portion = concat(non_common_parts, " or ") .. common_suffix
end
else
-- 4. Join the subtitles that differ from segment to segment.
-- Record whether there are any such differing subtitles.
-- If some segments have differing subtitles and others don't,
-- we use the text "otherwise" for the segments without
-- differing subtitles.
local saw_non_common_subtitles = false
local non_common_subtitles = {}
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
local this_non_common_subtitles = {}
for k = num_common_subtitles + 1, #this_subtitles do
if type(this_subtitles[k]) == "table" then
insert(this_non_common_subtitles, concat(this_subtitles[k]))
else
insert(this_non_common_subtitles, this_subtitles[k])
end
end
if #this_non_common_subtitles > 0 then
insert(non_common_subtitles, concat(this_non_common_subtitles, ", "))
saw_non_common_subtitles = true
else
insert(non_common_subtitles, "otherwise")
end
end
non_common_subtitle_portion =
saw_non_common_subtitles and concat(non_common_subtitles, " or ") or ""
end
-- 5. Combine the common and non-common subtitle portions.
local subtitle_portions = {}
if common_subtitle_portion ~= "" then
insert(subtitle_portions, common_subtitle_portion)
end
if non_common_subtitle_portion ~= "" then
insert(subtitle_portions, non_common_subtitle_portion)
end
if #seg_stems_seen > 1 then
insert(subtitle_portions,
(number_to_english[#seg_stems_seen] or "" .. #seg_stems_seen) .. " different stems"
)
end
local subtitle_portion = concat(subtitle_portions, "; ")
if subtitle_portion ~= "" then
title_to_insert = alternant_decl_title .. " (" .. subtitle_portion .. ")"
else
title_to_insert = alternant_decl_title
end
end
-- Don't insert blank title (happens e.g. with "((ali))quis<irreg+>").
if title_to_insert ~= "" then
insert(declensions.title, title_to_insert)
end
else
for slot in iter_slots(is_adj) do
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
end
-- First title is uppercase, remainder have an indefinite article, joined
-- using "with".
local titles = {}
for i, title in ipairs(declensions.title) do
if i == 1 then
insert(titles, ucfirst(title))
else
insert(titles, add_indefinite_article(title))
end
end
declensions.title = concat(titles, " with ")
return declensions
end
local function construct_title(args_title, declensions_title, generate_type, parsed_run)
if args_title then
declensions_title = args_title:gsub("<1>", "[[ภาคผนวก:Latin first declension|first declension]]")
declensions_title = declensions_title:gsub("<1&2>", "[[ภาคผนวก:Latin first declension|first]]/[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<2>", "[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<3>", "[[ภาคผนวก:Latin third declension|third declension]]")
declensions_title = declensions_title:gsub("<4>", "[[ภาคผนวก:Latin fourth declension|fourth declension]]")
declensions_title = declensions_title:gsub("<5>", "[[ภาคผนวก:Latin fifth declension|fifth declension]]")
if generate_type == "headword" then
declensions_title = lcfirst((declensions_title:gsub("%.$", "")))
else
declensions_title = ucfirst(declensions_title)
end
else
local post_text_parts = {}
if parsed_run.loc then
insert(post_text_parts, ", with locative")
end
-- FIXME: Hierosolyma has -a/-ōrum (2nd neut. pl. with sg. meaning) and -a/-ae (1st fem. sg.)
-- alternants, but because it has singular and plural forms overall, it's not made clear that
-- each alternant is restricted to one number.
if parsed_run.num == "sg" then
insert(post_text_parts, ", เอกพจน์เท่านั้น")
elseif parsed_run.num == "pl" then
insert(post_text_parts, ", พหูพจน์เท่านั้น")
end
local post_text = concat(post_text_parts)
if generate_type == "headword" then
declensions_title = lcfirst(declensions_title) .. post_text
else
declensions_title = ucfirst(declensions_title) .. post_text .. "."
end
end
return declensions_title
end
function export.do_generate_noun_forms(parent_args, pos, generate_type, def)
local params = {
[1] = {required = true, default = def or "aqua<1>"},
footnote = true,
title = true,
num = true,
json = {type = "boolean"},
}
for slot in iter_noun_slots() do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.id = true
params.cat = list
params.m = sublist
params.f = sublist
params.n = sublist
params.adj = sublist
params.dim = sublist
params.aug = sublist
params.g = list
params.indecl = {type = "boolean"}
end
if pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if args.title then
track("overriding-title")
end
local parsed_run = parse_segment_run_allowing_alternants(args[1])
parsed_run.loc = parsed_run.loc or not not (args.loc_sg or args.loc_pl)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, false)
if not parsed_run.loc then
declensions.forms.loc_sg = nil
declensions.forms.loc_pl = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
gender = parsed_run.gender,
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
overriding_lemma = args.lemma,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
m = args.m,
f = args.f,
overriding_genders = args.g,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_noun_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_noun_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.do_generate_adj_forms(parent_args, pos, generate_type, degree, def)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, default = def or "bonus"},
footnote = true,
title = true,
num = true,
noneut = boolean,
nomf = boolean,
json = boolean,
}
for slot in iter_adj_slots() do
params[slot] = true
end
for slot in iter_noun_slots(true) do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.adv = sublist
params.id = true
params.cat = list
params.indecl = boolean
if degree == "ขั้นกว่า" or degree == "ขั้นสุด" then
params.positive = sublist
end
if degree ~= "ขั้นกว่า" then
params.comp = sublist
end
if degree ~= "ขั้นสุด" then
params.sup = sublist
end
end
local set_pos
if not pos then
params.pos = {set = {"คำคุณศัพท์", "ตัวกำหนด", "รูปกริยาขยาย", "คำสรรพนาม"}, default = "คำคุณศัพท์"}
set_pos = true
elseif pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if set_pos then
pos = pluralize(args.pos)
end
for slot in iter_noun_slots(true) do
local arg = args[slot]
if arg ~= nil then
for i = 1, genders_n do
local slot_g = slot .. "_" .. genders[i]
local arg_g = args[slot_g]
if arg_g == nil then
args[slot_g] = arg
end
end
args[slot] = nil
end
end
if args.title then
track("overriding-title")
end
local segment_run = args[1]
if not segment_run:match("[<(]") then
-- If the segment run doesn't have any explicit declension specs or alternants,
-- add a default declension spec of <+> to it (or <0+> for indeclinable
-- adjectives). This allows the majority of adjectives to just specify
-- the lemma.
segment_run = segment_run .. (args.indecl and "<0+>" or "<+>")
end
local parsed_run = parse_segment_run_allowing_alternants(segment_run)
parsed_run.loc = parsed_run.loc or not not (
args.loc_sg_m or args.loc_sg_f or args.loc_sg_n or args.loc_pl_m or args.loc_pl_f or args.loc_pl_n
)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, true)
if not parsed_run.loc then
declensions.forms.loc_sg_m = nil
declensions.forms.loc_sg_f = nil
declensions.forms.loc_sg_n = nil
declensions.forms.loc_pl_m = nil
declensions.forms.loc_pl_f = nil
declensions.forms.loc_pl_n = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
accel = {},
loc = declensions.loc,
noneut = args.noneut or declensions.noneut,
nomf = args.nomf or declensions.nomf,
overriding_lemma = args.lemma,
positive = args.positive,
comp = args.comp,
sup = args.sup,
adv = args.adv,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_adj_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_adj_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.show_noun(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_noun_forms(parent_args, "คำนาม")
if type(data) == "string" then -- JSON
return data
end
show_forms(data, false)
local num = data.num
if num == "sg" then
return make_noun_table_sg(data)
elseif num == "pl" then
return make_noun_table_pl(data)
end
return make_noun_table(data)
end
function export.show_adj(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_adj_forms(parent_args)
if type(data) == "string" then -- JSON
return data
end
partial_show_forms(data, true)
return make_adj_table(data)
end
return export
k9y975o6ni5mij7dwu998p6ij09zmvx
5776190
5776189
2026-11-02T01:37:07Z
Octahedron80
267
5776190
Scribunto
text/plain
local export = {}
--[=[
Authorship: Ben Wing <benwing2>, with many ideas and a little code coming from
the old [[Module:la-decl-multi]] by KC Kenny Lau.
]=]
-- TODO:
-- (DONE) Eliminate specification of noteindex from la-adj/data
-- (DONE?) Finish autodetection of adjectives
-- (DONE) Remove old noun code
-- (DONE) Implement <.sufn>
-- (DONE) Look into adj voc=false
-- (DONE) Handle loc in adjectives
-- Error on bad subtypes
-- Make sure Google Books link still works.
-- (DONE) Make sure .sufn triggers insertion of 'with m optionally -> n in compounds' in title.
-- (DONE) Make sure title returned to la-adj lowercases the first letter even with a custom title.
--[=[
TERMINOLOGY:
-- "slot" = A particular case/number combination (for nouns) or
case/number/gender combination (for adjectives). Example slot names are
"abl_sg" (for noun) or "acc_pl_f" (for adjectives). Each slot is filled
with zero or more forms.
-- "form" = The declined Latin form representing the value of a given slot.
For example, rēge is a form, representing the value of the abl_sg slot of
the lemma rēx.
-- "lemma" = The dictionary form of a given Latin term. For nouns, it's
generally the nominative singular, but will be the nominative plural of
plurale tantum nouns (e.g. [[castra]]), and may occasionally be another
form (e.g. the genitive singular) if the nominative singular is missing.
For adjectives, it's generally the masculine nominative singular, but
will be the masculine nominative plural of plurale tantum adjectives
(e.g. [[dēnī]]).
-- "plurale tantum" (plural "pluralia tantum") = A noun or adjective that
exists only in the plural. Examples are castra "army camp", faucēs "throat",
and dēnī "ten each" (used for counting pluralia tantum nouns).
-- "singulare tantum" (plural "singularia tantum") = A noun or adjective that
exists only in the singular. Examples are geōlogia "geology" (and in
general most non-count nouns) and the adjective ūnus "one".
]=]
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local headword_data_module = "Module:headword/data"
local json_module = "Module:JSON"
local la_adj_data_module = "Module:la-adj/data"
local la_adj_table_module = "Module:la-adj/table"
local la_noun_data_module = "Module:la-noun/data"
local la_noun_table_module = "Module:la-noun/table"
local la_utilities_module = "Module:la-utilities"
local languages_module = "Module:languages"
local links_module = "Module:links"
local load_module = "Module:load"
local parameters_module = "Module:parameters"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local concat = table.concat
local insert = table.insert
local iter_adj_slots -- defined below
local iter_noun_slots -- defined below
local umatch = mw.ustring.match
local function add_indefinite_article(...)
add_indefinite_article = require(en_utilities_module).add_indefinite_article
return add_indefinite_article(...)
end
local function contains(...)
contains = require(table_module).contains
return contains(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function lcfirst(...)
lcfirst = require(string_utilities_module).lcfirst
return lcfirst(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function make_adj_table(...)
make_adj_table = require(la_adj_table_module).make_table
return make_adj_table(...)
end
local function make_noun_table(...)
make_noun_table = require(la_noun_table_module).make_table
return make_noun_table(...)
end
local function make_noun_table_sg(...)
make_noun_table_sg = require(la_noun_table_module).make_table_sg
return make_noun_table_sg(...)
end
local function make_noun_table_pl(...)
make_noun_table_pl = require(la_noun_table_module).make_table_pl
return make_noun_table_pl(...)
end
local function make_stem2(...)
make_stem2 = require(la_utilities_module).make_stem2
return make_stem2(...)
end
local function normalize_form(...)
normalize_form = require(la_utilities_module).normalize_form
return normalize_form(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function singularize(...)
singularize = require(en_utilities_module).singularize
return singularize(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function ucfirst(...)
ucfirst = require(string_utilities_module).ucfirst
return ucfirst(...)
end
local m_adj_decl
local function get_m_adj_decl()
m_adj_decl, get_m_adj_decl = require(la_adj_data_module), nil
return m_adj_decl
end
local m_noun_decl
local function get_m_noun_decl()
m_noun_decl, get_m_noun_decl = require(la_noun_data_module), nil
return m_noun_decl
end
local lang
local function get_lang()
lang, get_lang = require(languages_module).getByCode("la")
return lang
end
local namespace
local function get_namespace()
namespace, get_namespace = load_data(headword_data_module).page.namespace, nil
return namespace
end
local pagename
local function get_pagename()
pagename, get_pagename = load_data(headword_data_module).pagename, nil
return pagename
end
local ligatures = {
['Ae'] = 'Æ',
['ae'] = 'æ',
['Oe'] = 'Œ',
['oe'] = 'œ',
}
local cases = {
"nom", "gen", "acc", "dat", "abl", "voc", "loc"
}
local cases_n = #cases
local nums = {
"sg", "pl"
}
local nums_n = #nums
local genders = {
"m", "f", "n"
}
local genders_n = #genders
local declension_to_english = setmetatable({
["1"] = "ที่ 1",
["1&2"] = "ที่ 1 และ 2",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}, {
__index = function(t, k)
return rawget(t, k:match("^[^+-]*"))
end
})
local number_to_english = {
"one", "two", "three", "four", "five"
}
local linked_prefixes = {
"", "linked_"
}
function export.iter_potential_noun_lemma_slots()
local num, case = 1, 0
return function()
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
return cases[case] .. "_" .. nums[num]
end
end
local potential_noun_lemma_slots = {}
for slot in export.iter_potential_noun_lemma_slots() do
insert(potential_noun_lemma_slots, slot)
end
local linked_to_non_linked_noun_slots = {}
for _, slot in ipairs(potential_noun_lemma_slots) do
linked_to_non_linked_noun_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with a noun declension, where a slot
-- is a particular case/number combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg, linked_nom_pl), which aren't overridable.
function export.iter_noun_slots(overridable_only)
local case, num, linked_variant = 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num]
end
end
iter_noun_slots = export.iter_noun_slots
function export.iter_potential_adj_lemma_slots()
local num, case, gen = 1, 1, 0
return function()
gen = gen + 1
if gen > genders_n then
gen = 1
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
end
return cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
-- List of adjective slots for which we generate linked variants. Include
-- feminine and neuter variants because they will be needed if the adjective
-- is part of a multiword feminine or neuter noun.
local potential_adj_lemma_slots = {}
for slot in export.iter_potential_adj_lemma_slots() do
insert(potential_adj_lemma_slots, slot)
end
local linked_to_non_linked_adj_slots = {}
for _, slot in ipairs(potential_adj_lemma_slots) do
linked_to_non_linked_adj_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with an adjective declension, where a slot
-- is a particular case/number/gender combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg_m, linked_nom_pl_m, etc.), which aren't overridable.
function export.iter_adj_slots(overridable_only)
local case, num, gen, linked_variant = 1, 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
gen = gen + 1
if gen > genders_n then
gen = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
iter_adj_slots = export.iter_adj_slots
-- Iterate over all the "slots" associated with a noun or adjective declension (depending on
-- the value of IS_ADJ), where a slot is a particular case/number combination (in the case of
-- nouns) or case/number/gender combination (in the case of adjectives). If OVERRIDABLE_ONLY
-- is specified, only include overridable slots (not including linked_ variants).
local function iter_slots(is_adj, overridable_only)
if is_adj then
return iter_adj_slots(overridable_only)
end
return iter_noun_slots(overridable_only)
end
local function concat_forms_in_slot(forms)
if forms and forms ~= "" and forms ~= "—" and #forms > 0 then
local new_vals = {}
for _, v in ipairs(forms) do
insert(new_vals, (v:gsub("|", "<!>")))
end
return concat(new_vals, ",")
end
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function track(page)
debug_track("la-nominal/" .. page)
return true
end
local function set_union(sets)
local union = {}
for _, set in ipairs(sets) do
for key, _ in pairs(set) do
union[key] = true
end
end
return union
end
local function set_difference(set1, set2)
local diff = {}
for key, _ in pairs(set1) do
if not set2[key] then
diff[key] = true
end
end
return diff
end
-- If a form is set as '*', that means it is unattested
-- but should still be generated
-- TODO: handle asterisks in forms stored in the data
local function unattested_forms(data, args, is_adj)
for slot in iter_slots(is_adj) do
local arg = args[slot]
if arg ~= nil then
arg = arg:match("^*(.*)")
if arg then
data.unattested[slot] = true
args[slot] = arg ~= "" and arg or nil
end
end
end
end
-- Make a link only if the form is attested
local function link_if_attested(form, accel, is_unattested)
local data = {lang = lang or get_lang()}
if is_unattested then
data.alt = "*" .. form
else
data.term = form
data.accel = accel
end
return full_link(data)
end
local function process_form(slot, data, args, linked_to_non_linked)
local forms = data.forms
-- If nomf=1 passed, clear out all masculine and feminine forms.
if data.nomf and slot:match("%f[^%z_][mf]%f[%z_]") then
forms[slot] = nil
end
-- If noneut=1 passed, clear out all neuter forms.
if data.noneut and slot:match("%f[^%z_]n%f[%z_]") then
forms[slot] = nil
end
local val
if args[slot] then
val = args[slot]
data.user_specified[slot] = true
else
-- Overridding nom_sg/nom_sg_m etc. should override linked_nom_sg
-- so that the correct value gets displayed in the headword, which
-- uses linked_nom_sg.
local non_linked_equiv_slot = linked_to_non_linked[slot]
if non_linked_equiv_slot and args[non_linked_equiv_slot] then
val = args[non_linked_equiv_slot]
data.user_specified[slot] = true
else
val = forms[slot]
end
end
if val then
if type(val) == "string" then
val = split(val, "/", true, true)
end
local num = data.num
if (
(num == "pl" and slot:find("sg", nil, true)) or
(num == "sg" and slot:find("pl", nil, true))
) then
forms[slot] = nil
elseif val[1] == "" or val[1] == "-" or val[1] == "—" then
forms[slot] = "—"
if val[2] then
error("Cannot specify additional forms for " .. slot .. ' if it has been cancelled with "-"')
end
else
forms[slot] = val
end
end
end
local function process_noun_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args);
-- Process overrides and canonicalize forms.
for slot in iter_noun_slots() do
process_form(slot, data, args, linked_to_non_linked_noun_slots)
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num]
else
accel_lemma = data.forms["nom_sg"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_noun_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]$", "|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
local function process_adj_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args, true)
-- Process overrides and canonicalize forms.
for slot in iter_adj_slots() do
process_form(slot, data, args, linked_to_non_linked_adj_slots)
end
-- See if the masculine and feminine/neuter are the same across all slots.
-- If so, blank out the feminine/neuter so we use a table that combines
-- masculine and feminine, or masculine/feminine/neuter.
for _, gender in ipairs({"f", "n"}) do
local other_is_masc = true
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
if not deep_equals(data.forms[case .. "_" .. num .. "_" .. gender],
data.forms[case .. "_" .. num .. "_m"]) then
other_is_masc = false
break
end
end
if not other_is_masc then
break
end
end
if other_is_masc then
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
data.forms[case .. "_" .. num .. "_" .. gender] = nil
end
end
end
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma, accel_lemma_f
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num .. "_m"]
accel_lemma_f = data.forms["nom_" .. data.num .. "_f"]
else
accel_lemma = data.forms["nom_sg_m"]
accel_lemma_f = data.forms["nom_sg_f"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
if type(accel_lemma_f) == "table" then
accel_lemma_f = accel_lemma_f[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_adj_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]_", "|%1|")
if data.noneut then
-- If noneut=1, we're being asked to do a noun like
-- Aquītānus or Rōmānus that has masculine and feminine
-- variants, not an adjective. In that case, make the
-- accelerators correspond to nominal case/number forms
-- without the gender, and use the feminine as the
-- lemma for feminine forms.
if slot:find("_f", nil, true) then
data.accel[slot] = {form = accel_form:gsub("|f$", ""), lemma = accel_lemma_f}
else
data.accel[slot] = {form = accel_form:gsub("|m$", ""), lemma = accel_lemma}
end
else
if not data.forms.nom_sg_n and not data.forms.nom_pl_n then
-- use multipart tags if called for
accel_form = accel_form:gsub("|m$", "|m//f//n")
elseif not data.forms.nom_sg_f and not data.forms.nom_pl_f then
accel_form = accel_form:gsub("|m$", "|m//f")
end
-- use the order nom|m|s, which is more standard than nom|s|m
accel_form = accel_form:gsub("|(.-)|(.-)$", "|%2|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
end
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
-- Convert data.forms[slot] for all slots into displayable text. This is
-- an older function, still currently used for nouns but not for adjectives.
-- For adjectives, the adjective table module has special code to combine
-- adjacent slots, and needs the original forms plus other text that will
-- go into the displayable text for the slot; this is handled below by
-- partial_show_forms() and finish_show_form().
local function show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" then
for i, form in ipairs(val) do
local link = link_if_attested(form, data.accel[slot], data.unattested[slot])
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
val[i] = link .. '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>'
else
val[i] = link
end
end
-- FIXME, do we want this difference?
data.forms[slot] = concat(val, is_adj and ", " or "<br />")
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Generate the display form for a set of slots with identical content. We
-- verify that the slots are actually identical, and throw an assertion error
-- if not. The display form is as in show_forms() but combines together all the
-- accelerator forms for all the slots.
local function finish_show_form(data, slots, is_adj)
assert(#slots > 0)
local slot1 = slots[1]
local forms = data.forms[slot1]
local notetext = data.notetext[slot1]
for _, slot in ipairs(slots) do
if not deep_equals(data.forms[slot], forms) then
error("data.forms[" .. slot1 .. "] = " .. (concat_forms_in_slot(forms) or "nil") ..
", but data.forms[" .. slot .. "] = " .. (concat_forms_in_slot(data.forms[slot]) or "nil"))
end
assert(deep_equals(data.notetext[slot], notetext))
end
if not forms then
return "—"
else
local accel_forms = {}
local accel_lemma = data.accel[slot1].lemma
for _, slot in ipairs(slots) do
assert(data.accel[slot].lemma == accel_lemma)
insert(accel_forms, data.accel[slot].form)
end
local combined_accel_form = concat(accel_forms, "|;|")
local accel = {form = combined_accel_form, lemma = accel_lemma}
local formtext = {}
for i, form in ipairs(forms) do
insert(formtext, link_if_attested(form, accel, data.unattested[slot1]) .. notetext[i])
end
-- FIXME, do we want this difference?
return concat(formtext, is_adj and ", " or "<br />")
end
end
-- Used by the adjective table module. This does some of the work of
-- show_forms(); in particular, it converts all empty forms of any format
-- (nil, "", "—") to nil and, if the forms aren't empty, generates the footnote
-- text associated with each form.
local function partial_show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
data.notetext = {}
-- Store this function in DATA so that it can be called from the adjective
-- table module without needing to require this module, which will (or
-- could) lead to recursive module requiring.
data.finish_show_form = finish_show_form
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if not val or val == "" or val == "—" then
data.forms[slot] = nil
else
local notetext = {}
for i in ipairs(val) do
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
insert(notetext, '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>')
else
insert(notetext, "")
end
end
data.notetext[slot] = notetext
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Given an ending (or possibly a full regex matching the entire lemma, if
-- a regex group is present), return the base minus the ending, or nil if
-- the ending doesn't match.
local function extract_base(lemma, ending)
if ending:find("(", nil, true) then
return umatch(lemma, ending)
end
return umatch(lemma, "^(.*)" .. ending .. "$")
end
-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- subtypes, where each pair consists of a single ending spec and a list of
-- subtypes), check each ending in turn against LEMMA. If it matches, return
-- the pair BASE, STEM2, SUBTYPES where BASE is the remainder of LEMMA minus
-- the ending, STEM2 is as passed in, and SUBTYPES is the subtypes associated
-- with the ending. But don't return SUBTYPES if any of the subtypes in the
-- list is specifically canceled in SPECIFIED_SUBTYPES (a set, i.e. a table
-- where the keys are strings and the value is always true); instead, consider
-- the next ending in turn. If no endings match, throw an error if DECLTYPE is
-- non-nil, mentioning the DECLTYPE (the user-specified declension); but if
-- DECLTYPE is nil, just return nil, nil, nil.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
local function get_noun_subtype_by_ending(lemma, stem2, decltype, specified_subtypes,
endings_and_subtypes)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local subtypes = ending_and_subtypes[2]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
-- In addition, M or F as a subtype is canceled by N, and
-- vice-versa, but M doesn't cancel F or vice-versa; instead,
-- we simply ignore the conflicting gender specification when
-- constructing the combination of specified and inferred subtypes.
-- The reason for this is that neuters have distinct declensions
-- from masculines and feminines, but masculines and feminines have
-- the same declension, and various nouns in Latin that are
-- normally masculine are exceptionally feminine and vice-versa
-- (nauta, agricola, fraxinus, malus "apple tree", manus, rēs,
-- etc.).
--
-- In addition, sg as a subtype is canceled by pl and vice-versa.
-- It's also possible to specify both, which will override sg but
-- not cancel it (in the sense that it won't prevent the relevant
-- rule from matching). For example, there's a rule specifying that
-- lemmas beginning with a capital letter and ending in -ius take
-- the ius.voci.sg subtypes. Specifying such a lemma with the
-- subtype both will result in the ius.voci.both subtypes, whereas
-- specifying such a lemma with the subtype pl will cause this rule
-- not to match, and it will fall through to a less specific rule
-- that returns just the ius subtype, which will be combined with
-- the explicitly specified pl subtype to produce ius.pl.
if specified_subtypes["-" .. subtype] or
subtype == "N" and (specified_subtypes.M or specified_subtypes.F) or
(subtype == "M" or subtype == "F") and specified_subtypes.N or
subtype == "sg" and specified_subtypes.pl or
subtype == "pl" and specified_subtypes.sg then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
local base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending == stem2 then
return base, stem2, subtypes
end
else
local base = extract_base(lemma, ending)
if base then
return base, stem2, subtypes
end
end
end
end
if decltype then
error("Unrecognized ending for declension-" .. decltype .. " noun: " .. lemma)
end
return nil, nil, nil
end
-- Autodetect the subtype of a noun given all the information specified by the
-- user: lemma, stem2, declension type and specified subtypes. Three values are
-- returned: the lemma base (i.e. the stem of the lemma, as required by the
-- declension functions), the new stem2 and the autodetected subtypes. Note
-- that this will not detect a given subtype if the explicitly specified
-- subtypes are incompatible (i.e. if -SUBTYPE is specified for any subtype
-- that would be returned; or if M or F is specified when N would be returned,
-- and vice-versa; or if pl is specified when sg would be returned, and
-- vice-versa).
--
-- NOTE: This function has intimate knowledge of the way that the declension
-- functions handle subtypes, particularly for the third declension.
local function detect_noun_subtype(lemma, stem2, typ, subtypes)
local base, _
if typ == "1" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ām", {"F", "am"}},
{"ās", {"M", "Greek", "Ma"}},
{"ēs", {"M", "Greek", "Me"}},
{"ē", {"F", "Greek"}},
{"ae", {"F", "pl"}},
{"a", {"F"}},
})
elseif typ == "2" then
local detected_subtypes
lemma, stem2, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(.*r)$", {"M", "er"}},
{"^(.*v)os$", {"M", "vos"}},
{"^(.*v)om$", {"N", "vom"}},
-- If the lemma ends in -os and the user said N or -M, then the
-- following won't apply, and the second (neuter) -os will applly.
{"os", {"M", "Greek"}},
{"os", {"N", "Greek", "us"}},
{"on", {"N", "Greek"}},
-- -ius beginning with a capital letter is assumed a proper name,
-- and takes the voci subtype (vocative in -ī) along with the ius
-- subtype and sg-only. Other nouns in -ius just take the ius
-- subtype. Explicitly specify "sg" so that if .pl is given,
-- this rule won't apply.
{"^(%u.*)ius$", {"M", "ius", "voci", "sg"}},
{"ius", {"M", "ius"}},
{"ium", {"N", "ium"}},
-- If the lemma ends in -us and the user said N or -M, then the
-- following won't apply, and the second (neuter) -us will applly.
{"us", {"M"}},
{"us", {"N", "us"}},
{"um", {"N"}},
{"iī", {"M", "ius", "pl"}},
{"ia", {"N", "ium", "pl"}},
-- If the lemma ends in -ī and the user said N or -M, then the
-- following won't apply, and the second (neuter) -ī will applly.
{"ī", {"M", "pl"}},
{"ī", {"N", "us", "pl"}},
{"oe", {"M", "Greek", "pl"}},
{"a", {"N", "pl"}},
})
stem2 = stem2 or lemma
return lemma, stem2, detected_subtypes
elseif typ == "3" then
if subtypes.pl then
if subtypes.Greek then
base = lemma:match("^(.*)erēs$")
if base then
return base .. "ēr", base .. "er", {"er"}
end
base = lemma:match("^(.*)ontēs$")
if base then
return base .. "ōn", base .. "ont", {"on"}
end
base = lemma:match("^(.*)es$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural Greek noun: " .. lemma)
end
base = lemma:match("^(.*)ia$")
if base then
return base, stem2 or base, {"N", "I", "pure"}
end
base = lemma:match("^(.*)a$")
if base then
return base, stem2 or base, {"N"}
end
base = lemma:match("^(.*)ēs$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural noun: " .. lemma)
end
stem2 = stem2 or make_stem2(lemma)
local detected_subtypes
if subtypes.Greek then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"is", ""}, {"I"}},
{"ēr", {"er"}},
{"ōn", {"on"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
end
if not subtypes.N then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"^(%u.*pol)is$", ""}, {"F", "polis", "sg", "loc"}},
{{"tūdō", "tūdin"}, {"F"}},
{{"tās", "tāt"}, {"F"}},
{{"tūs", "tūt"}, {"F"}},
{{"tiō", "tiōn"}, {"F"}},
{{"siō", "siōn"}, {"F"}},
{{"xiō", "xiōn"}, {"F"}},
{{"gō", "gin"}, {"F"}},
{{"or", "ōr"}, {"M"}},
{{"tr[iī]x", "trīc"}, {"F"}},
{{"is", ""}, {"I"}},
{{"^(%l.*)ēs$", ""}, {"I"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"us", "or"}, {"N"}},
{{"us", "er"}, {"N"}},
{{"ma", "mat"}, {"N"}},
{{"men", "min"}, {"N"}},
{{"^(%u.*)e$", ""}, {"N", "sg"}},
{{"e", ""}, {"N", "I", "pure"}},
{{"al", "āl"}, {"N", "I", "pure"}},
{{"ar", "ār"}, {"N", "I", "pure"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
elseif typ == "4" then
if subtypes.echo or subtypes.Callisto then
base = lemma:match("^(.*)ō$")
if not base then
error("Declension-4 noun of subtype .echo or .Callisto should end in -ō: " .. lemma)
end
if subtypes.Callisto then
return base, nil, {"F", "sg"}
else
return base, nil, {"F"}
end
end
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", {"M"}},
{"ū̆", {"N"}},
{"ūs", {"M", "pl"}},
{"ua", {"N", "pl"}},
})
elseif typ == "5" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"iēs", {"F", "i"}},
{"iēs", {"F", "i", "pl"}},
{"ēs", {"F"}},
{"ēs", {"F", "pl"}},
})
elseif typ == "sgpl" then
return lemma, stem2, {}
elseif typ == "irreg" and lemma == "domus" then
-- [[domus]] auto-sets data.loc = true, but we need to know this
-- before declining the noun so we can propagate it to other segments.
return lemma, nil, {"loc"}
elseif typ == "indecl" or typ == "irreg" and (
lemma == "Deus" or umatch(lemma, "^[IJ]ēs[uū]s$") or
lemma == "Athōs" or lemma == "vēnum"
) then
-- Indeclinable nouns, and certain irregular nouns, set data.num = "sg",
-- but we need to know this before declining the noun so we can
-- propagate it to other segments.
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", {"both"}},
{"", {"sg"}},
{"", {"pl"}},
})
else
return lemma, nil, {}
end
end
-- Given ENDINGS_AND_SUBTYPES (a list of four-tuples of ENDING, RETTYPE,
-- SUBTYPES, PROCESS_RETVAL), check each ENDING in turn against LEMMA and
-- STEM2. If it matches, return a four-tuple BASE, STEM2, RETTYPE, NEW_SUBTYPES
-- where BASE is normally the remainder of LEMMA minus the ending, STEM2 is
-- as passed in, RETTYPE is as passed in, and NEW_SUBTYPES is the same as
-- SUBTYPES minus any subtypes beginning with a hyphen. If no endings match,
-- throw an error if DECLTYPPE is non-nil, mentioning the DECLTYPE
-- (user-specified declension); but if DECLTYPE is nil, just return the tuple
-- nil, nil, nil, nil.
--
-- In order for a given entry to match, ENDING must match and also the subtypes
-- in SUBTYPES (a list) must not be incompatible with the passed-in
-- user-specified subtypes SPECIFIED_SUBTYPES (a set, i.e. a table where the
-- keys are strings and the value is always true). "Incompatible" means that
-- a given SUBTYPE is specified in either one and -SUBTYPE in the other, or
-- that "pl" is found in SPECIFIED_SUBTYPES and not in SUBTYPES.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
--
-- If PROCESS_STEM2 is given and the returned STEM2 would be nil, call
-- process_stem2(BASE) to get the STEM2 to return.
local function get_adj_type_and_subtype_by_ending(lemma, stem2, decltype,
specified_subtypes, endings_and_subtypes, process_stem2)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local rettype = ending_and_subtypes[2]
local subtypes = ending_and_subtypes[3]
local process_retval = ending_and_subtypes[4]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
if specified_subtypes["-" .. subtype] then
not_this_subtype = true
break
end
-- A subtype is canceled if the user specified SUBTYPE and
-- -SUBTYPE is given in the to-be-returned subtypes.
local must_not_be_present = subtype:match("^%-(.*)$")
if must_not_be_present and specified_subtypes[must_not_be_present] then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
local base
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending ~= stem2 then
base = nil
end
else
base = extract_base(lemma, ending)
end
if base then
-- Remove subtypes of the form -SUBTYPE from the subtypes
-- to be returned.
local new_subtypes = {}
for _, subtype in ipairs(subtypes) do
if subtype:sub(1, 1) ~= "-" then
insert(new_subtypes, subtype)
end
end
if process_retval then
base, stem2 = process_retval(base, stem2)
end
if process_stem2 then
stem2 = stem2 or process_stem2(base)
end
return base, stem2, rettype, new_subtypes
end
end
end
if not decltype then
return nil, nil, nil, nil
elseif decltype == "" then
error("Unrecognized ending for adjective: " .. lemma)
else
error("Unrecognized ending for declension-" .. decltype .. " adjective: " .. lemma)
end
end
-- Autodetect the type and subtype of an adjective given all the information
-- specified by the user: lemma, stem2, declension type and specified subtypes.
-- Four values are returned: the lemma base (i.e. the stem of the lemma, as
-- required by the declension functions), the value of stem2 to pass to the
-- declension function, the declension type and the autodetected subtypes.
-- Note that this will not detect a given subtype if -SUBTYPE is specified for
-- any subtype that would be returned, or if SUBTYPE is specified and -SUBTYPE
-- is among the subtypes that would be returned (such subtypes are filtered out
-- of the returned subtypes).
local function detect_adj_type_and_subtype(lemma, stem2, typ, subtypes)
local function base_as_stem2(base, stem2)
return base, base
end
local function constant_base(baseval)
return function(base, stem2)
return baseval, nil
end
end
local function decl12_stem2(base)
return base
end
local function decl3_stem2(base)
return make_stem2(base)
end
local decl12_entries = {
{"us", "1&2+", {}},
{"a", "1&2+", {}},
{"um", "1&2+", {}},
{"ī", "1&2+", {"pl"}},
{"ae", "1&2+", {"pl"}},
{"a", "1&2+", {"pl"}},
-- Nearly all -os adjectives are greekA
{"os", "1&2+", {"greekA", "-greekE"}},
{"os", "1&2+", {"greekE", "-greekA"}},
{"ē", "1&2+", {"greekE", "-greekA"}},
{"on", "1&2+", {"greekA", "-greekE"}},
{"on", "1&2+", {"greekE", "-greekA"}},
{"^(.*er)$", "1&2+", {"er"}},
{"^(.*ur)$", "1&2+", {"er"}},
{"^(h)ic$", "1&2+", {"ic"}},
}
local decl3_entries = {
{"^(.*er)$", "3-3+", {"I"}},
{"^(.*er)$", "3-3+", {}},
{"is", "3-2+", {"I"}},
{"is", "3-2+", {}},
{"e", "3-2+", {"I"}},
{"e", "3-2+", {}},
{"^(.*[ij])or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(.*[ij])or$", "3-C+", {}},
{"^(min)or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(min)or$", "3-C+", {}},
{"^(.*trīx)$", "3-def+", {"trix", "abl_i_e", "-abl_e_i"}},
{"^(.*trīx)$", "3-def+", {"trix"}},
{"^(.*tor)$", "3-def+", {"tor", "abl_i_e", "-abl_e_i"}},
{"^(.*tor)$", "3-def+", {"tor"}},
-- Detect -ēs as 3-1 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like quadripēs, volucripēs).
-- Essentially, for declension-3 adjectives, we require that
-- .pl is given if the lemma is plural.
--
-- Most 3-1 adjectives are i-stem (e.g. audāx) so we require -I
-- to be given with non-i-stem adjectives. The first entry below
-- will apply when -I isn't given, the second when it is given.
{"^(.*ēs)$", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"I"}},
{"^(.*ēs)$", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"par"}},
{"^(.*[ij])ōrēs$", "3-C+", {"pl"}},
{"^(min)ōrēs$", "3-C+", {"pl"}},
-- If .pl with -ēs, we don't know if the adjective is 3-1, 3-2
-- or 3-3. Since 3-2 is probably the most common, we infer it
-- (as well as the fact that these adjectives *are* in a sense
-- 3-2 since they have a distinct neuter in -(i)a. Note that
-- we have two entries here; the first one will apply unless
-- -I is given, and will infer an i-stem adjective; the second
-- one will apply otherwise (and infer a non-i-stem 3-1 adjective).
{"ēs", "3-2+", {"pl", "I"}, base_as_stem2},
{"ēs", "3-1+", {"pl", "par"}, base_as_stem2},
-- Same for neuters.
{"ia", "3-2+", {"pl", "I"}, base_as_stem2},
{"a", "3-1+", {"pl", "par"}, base_as_stem2},
-- As above for -ēs but for miscellaneous singulars.
{"", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"I"}},
{"", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"par"}},
}
local function get_subtypes_and_endings(typ, f)
local subtypes_and_endings = {}
for _, v in ipairs(decl3_entries) do
if f(v) then
v = deep_copy(v)
v[2] = typ
insert(subtypes_and_endings, v)
end
end
return subtypes_and_endings
end
local function compare(spec)
return spec[2] == typ
end
if typ == "+" then
local base, new_stem2, rettype, new_subtypes = get_adj_type_and_subtype_by_ending(lemma, stem2, nil, subtypes, decl12_entries, decl12_stem2)
if base then
return base, new_stem2, rettype, new_subtypes
else
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
end
elseif typ == "3+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
elseif typ == "1&2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl12_entries, decl12_stem2)
elseif typ == "1-1+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"a", typ, {}},
{"ae", typ, {"pl"}},
})
elseif typ == "2-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", typ, {}},
{"um", typ, {}},
{"ī", typ, {"pl"}},
{"a", typ, {"pl"}},
{"os", typ, {"greek"}},
{"on", typ, {"greek"}},
{"oe", typ, {"greek", "pl"}},
})
elseif typ == "3-1+" then
-- Use the specs from `decl3_entries`, but add the two i-stem 3-2+ specs when plural-only, as they're the same when 3-1+.
local subtypes_and_endings = get_subtypes_and_endings(typ, function(spec)
local spec1, spec2 = spec[1], spec[2]
return spec2 == typ or (spec2 == "3-2+" and spec1 == "ēs" or spec1 == "ia")
end)
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-def+" then
-- Like 3-1+ but defective in gender
local subtypes_and_endings = get_subtypes_and_endings(typ, compare)
insert(subtypes_and_endings, {"", typ, {"I"}})
insert(subtypes_and_endings, {"", typ, {"par"}})
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"is", typ, {}},
{"e", typ, {}},
-- Detect -ēs as 3-2 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like isoscelēs). Essentially,
-- for declension-3 adjectives, we require that .pl is given
-- if the lemma is plural.
{"ēs", typ, {}},
{"ēs", typ, {"pl"}},
{"ia", typ, {"pl"}},
}, decl3_stem2)
elseif typ == "3-3+" or typ == "3-P+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ēs", typ, {"pl"}, base_as_stem2},
{"ia", typ, {"pl"}, base_as_stem2},
{"", typ, {}},
}, decl3_stem2)
elseif typ == "3-C+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, get_subtypes_and_endings(typ, compare), decl3_stem2)
elseif typ == "irreg+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(duo)$", typ, {"pl"}},
{"^(ambō)$", typ, {"pl"}},
{"^(mīll?ia)$", typ, {"N", "pl"}, constant_base("mīlle")},
-- match ea
{"^(ea)$", typ, {}, constant_base("is")},
-- match id
{"^(id)$", typ, {}, constant_base("is")},
-- match plural eī, iī
{"^([ei]ī)$", typ, {"pl"}, constant_base("is")},
-- match plural ea, eae
{"^(eae?)$", typ, {"pl"}, constant_base("is")},
-- match eadem
{"^(eadem)$", typ, {}, constant_base("īdem")},
-- match īdem, idem
{"^([īi]dem)$", typ, {}, constant_base("īdem")},
-- match plural īdem
{"^(īdem)$", typ, {"pl"}},
-- match plural eadem, eaedem
{"^(eae?dem)$", typ, {"pl"}, constant_base("īdem")},
-- match illa, ipsa, ista; it doesn't matter if we overmatch because
-- we'll get an error as we use the stem itself in the returned base
{"^(i[lps][lst])a$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match illud, istud; as above, it doesn't matter if we overmatch
{"^(i[ls][lt])ud$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match ipsum
{"^(ipsum)$", typ, {}, constant_base("ipse")},
-- match plural illī, ipsī, istī; as above, it doesn't matter if we
-- overmatch
{"^(i[lps][lst])ī$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- match plural illa, illae, ipsa, ipsae, ista, istae; as above, it
-- doesn't matter if we overmatch
{"^(i[lps][lst])ae?$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- Detect quī as non-plural unless .pl specified.
{"^(quī)$", typ, {}},
-- Otherwise detect quī as plural.
{"^(quī)$", typ, {"pl"}},
-- Same for quae.
{"^(quae)$", typ, {}, constant_base("quī")},
{"^(quae)$", typ, {"pl"}, constant_base("quī")},
{"^(quid)$", typ, {}, constant_base("quis")},
{"^(quod)$", typ, {}, constant_base("quī")},
{"^(qui[cd]quid)$", typ, {}, constant_base("quisquis")},
{"^(quīquī)$", typ, {"pl"}, constant_base("quisquis")},
{"^(quaequae)$", typ, {"pl"}, constant_base("quisquis")},
-- match all remaining lemmas in lemma form
{"", typ, {}},
})
elseif typ == "indecl+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", typ, {"both"}},
{"", typ, {"sg"}},
{"", typ, {"pl"}},
})
else -- 0+
return lemma, nil, typ, {}
end
end
-- Parse a segment (e.g. "lūna<1>", "aegis/aegid<3.Greek>", "bōs<irreg.F>",
-- bonus<+>", or "[[vetus]]/veter<3+.-I>"), consisting of a lemma (or optionally
-- a lemma/stem) and declension+subtypes, where a + in the declension indicates
-- an adjective. Brackets can be present to indicate links, for use in
-- {{la-noun}} and {{la-adj}}. The return value is a table, e.g.:
-- {
-- decl = "1",
-- is_adj = false,
-- orig_lemma = "lūna",
-- lemma = "lūna",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"lūn"}
-- }
--
-- or
--
-- {
-- decl = "3",
-- is_adj = false,
-- orig_lemma = "aegis",
-- lemma = "aegis",
-- stem2 = "aegid",
-- gender = nil,
-- types = {["Greek"] = true},
-- args = {"aegis", "aegid"}
-- }
--
-- or
--
-- {
-- decl = "irreg",
-- is_adj = false,
-- orig_lemma = "bōs",
-- lemma = "bōs",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"bōs"}
-- }
-- or
--
-- {
-- decl = "1&2+",
-- is_adj = true,
-- orig_lemma = "bonus",
-- lemma = "bonus",
-- stem2 = nil,
-- gender = nil,
-- types = {},
-- args = {"bon"}
-- }
--
-- or
--
-- {
-- decl = "3-1+",
-- is_adj = true,
-- orig_lemma = "[[vetus]]",
-- lemma = "vetus",
-- stem2 = "veter",
-- gender = nil,
-- types = {},
-- args = {"vetus", "veter"}
-- }
local function parse_segment(segment)
local stem_part, spec_part = segment:match("^(.*)<(.-)>$")
local stems = split(stem_part, "/", true, true)
local specs = split(spec_part, ".", true, true)
local types = {}
local num = nil
local loc = false
local args = {}
local decl
for j, spec in ipairs(specs) do
if j == 1 then
decl = spec
else
local begins_with_hyphen
begins_with_hyphen, spec = spec:match("^(%-?)(.*)$")
spec = begins_with_hyphen .. spec:gsub("%-", "_")
types[spec] = true
end
end
local orig_lemma = stems[1]
if not orig_lemma or orig_lemma == "" then
orig_lemma = pagename or get_pagename()
end
local lemma = remove_links(orig_lemma)
local stem2 = stems[2]
if stem2 == "" then
stem2 = nil
end
if #stems > 2 then
error("Too many stems, at most 2 should be given: " .. stem_part)
end
local base, detected_subtypes
local is_adj = false
local gender = nil
if decl:find("+", nil, true) then
base, stem2, decl, detected_subtypes = detect_adj_type_and_subtype(lemma, stem2, decl, types)
is_adj = true
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
else
types[subtype] = true
end
end
else
base, stem2, detected_subtypes = detect_noun_subtype(lemma, stem2, decl, types)
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
elseif (subtype == "M" or subtype == "F" or subtype == "N") and
(types.M or types.F or types.N) then
-- if gender already specified, don't create conflicting gender spec
elseif (subtype == "sg" or subtype == "pl" or subtype == "both") and
(types.sg or types.pl or types.both) then
-- if number restriction already specified, don't create conflicting
-- number restriction spec
else
types[subtype] = true
end
end
if not types.pl and not types.both and umatch(lemma, "^%u") then
types.sg = true
end
end
if types.loc then
loc = true
types.loc = nil
end
if types.M then
gender = "M"
elseif types.F then
gender = "F"
elseif types.N then
gender = "N"
end
if types.pl then
num = "pl"
types.pl = nil
elseif types.sg then
num = "sg"
types.sg = nil
end
args[1] = base
args[2] = stem2
return {
decl = decl,
is_adj = is_adj,
gender = gender,
orig_lemma = orig_lemma,
lemma = lemma,
stem2 = stem2,
types = types,
num = num,
loc = loc,
args = args,
}
end
-- Parse a segment run (i.e. a string with zero or more segments [see
-- parse_segment] and optional surrounding text, e.g. "foenum<2>-graecum<2>"
-- or "[[pars]]/part<3.abl-e-occ-i> [[oratio|ōrātiōnis]]"). The segment run
-- currently cannot contain any alternants (e.g. "((epulum<2.sg>,epulae<1>))").
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments
-- has a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of per-word properties, where each element is an
-- object {
-- decl = DECL (declension),
-- types = TYPES (set describing the subtypes of a given word),
-- }
-- }
-- Each element in PARSED_SEGMENTS is as returned by parse_segment() but will
-- have an additional .orig_prefix field indicating the text before the segment
-- (including bracketed links) and corresponding .prefix field indicating the text
-- with bracketed links resolved. If there is trailing text, the last element will
-- have only .orig_prefix and .prefix fields containing that trailing text.
local function parse_segment_run(segment_run)
local loc = nil
local num = nil
local is_adj = nil
-- If the segment run begins with a hyphen, include the hyphen in the
-- set of allowed characters for a declined segment. This way, e.g. the
-- suffix [[-cen]] can be declared as {{la-ndecl|-cen/-cin<3>}} rather than
-- {{la-ndecl|-cen/cin<3>}}, which is less intuitive.
local is_suffix = segment_run:sub(1, 1) == "-"
local segments = {}
local propses = {}
-- We want to not break up a bracketed link followed by <> even if it has a space or
-- hyphen in it. So we do an outer capturing split to find the bracketed links followed
-- by <>, then do inner capturing splits on all the remaining text to find the other
-- declined terms.
local bracketed_segments = split(segment_run, "(%[%[[^%[%]]-%]%]<.->)")
for i, bracketed_segment in ipairs(bracketed_segments) do
if i % 2 == 0 then
insert(segments, bracketed_segment)
else
for _, subsegment in ipairs(split(bracketed_segment, is_suffix and "([^<> ,]+<.->)" or "([^<> ,%-]+<.->)")) do
insert(segments, subsegment)
end
end
end
local parsed_segments = {}
local gender = nil
for i = 2, (#segments - 1), 2 do
local parsed_segment = parse_segment(segments[i])
-- Overall locative is true if any segments call for locative.
loc = loc or parsed_segment.loc
-- The first specified value for num is used becomes the overall value.
num = num or parsed_segment.num
if is_adj == nil then
is_adj = parsed_segment.is_adj
else
is_adj = is_adj and parsed_segment.is_adj
end
gender = gender or parsed_segment.gender
parsed_segment.orig_prefix = segments[i - 1]
parsed_segment.prefix = remove_links(segments[i - 1])
parsed_segment.props = {
decl = parsed_segment.decl,
types = parsed_segment.types,
gender = parsed_segment.gender,
num = parsed_segment.num,
}
insert(parsed_segments, parsed_segment)
insert(propses, parsed_segment.props)
end
if segments[#segments] ~= "" then
insert(parsed_segments, {
orig_prefix = segments[#segments],
prefix = remove_links(segments[#segments]),
})
end
return {
segments = parsed_segments,
loc = loc,
num = num,
is_adj = is_adj,
gender = gender,
propses = propses,
}
end
-- Parse an alternant, e.g. "((epulum<2.sg>,epulae<1>))",
-- "((Serapis<3>,Serapis/Serapid<3>))" or
-- "((rēs<5>pūblica<1>,rēspūblica<1>))". The return value is a table of the form
-- {
-- alternants = PARSED_ALTERNANTS (a list of segment runs, each of which is a
-- list of parsed segments as returned by parse_segment_run()),
-- loc = LOC (a boolean indicating whether any of the individual segment runs
-- has a locative),
-- num = NUM (the overall number restriction, one of "sg", "pl" or "both"),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all non-constant alternants are adjectives, false
-- if all nouns, nil if only constant alternants; conflicting alternants
-- cause an error),
-- propses = PROPSES (list of lists of per-word property objecs),
-- }
local function parse_alternant(alternant)
local parsed_alternants = {}
local alternant_spec = alternant:match("^%(%((.-)%)%)$")
local alternants = split(alternant_spec, ",", true, true)
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i, alternant in ipairs(alternants) do
local parsed_run = parse_segment_run(alternant)
insert(parsed_alternants, parsed_run)
loc = loc or parsed_run.loc
-- First time through, set the overall num to the num of the first run,
-- even if nil. After that, if we ever see a run with a different value
-- of num, set the overall num to "both". That way, if all alternants
-- don't specify a num, we get an unspecified num, but if some do and
-- some don't, we get both, because an unspecified num defaults to
-- both.
if i == 1 then
num = parsed_run.num
elseif num ~= parsed_run.num then
-- FIXME, this needs to be rethought to allow for
-- adjective alternants.
num = "both"
end
gender = gender or parsed_run.gender
if is_adj == nil then
is_adj = parsed_run.is_adj
elseif parsed_run.is_adj ~= nil and parsed_run.is_adj ~= is_adj then
error("Saw both noun and adjective alternants; not allowed")
end
insert(propses, parsed_run.propses)
end
return {
alternants = parsed_alternants,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Parse a segment run (see parse_segment_run()). Unlike for
-- parse_segment_run(), this can contain alternants such as
-- "((epulum<2.sg>,epulae<1>))" or "((Serapis<3.sg>,Serapis/Serapid<3.sg>))"
-- embedded in it to indicate words composed of multiple declensions.
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments has
-- a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of either per-word property objects or lists of
-- lists of such objects),
-- }.
-- Each element in PARSED_SEGMENTS is one of three types:
--
-- 1. A regular segment, as returned by parse_segment() but with additional
-- .prefix and .orig_prefix fields indicating the text before the segment, as per
-- the return value of parse_segment_run().
-- 2. A raw-text segment, i.e. a table with only .prefix and .orig_prefix fields
-- containing the raw text.
-- 3. An alternating segment, as returned by parse_alternant().
-- Note that each alternant is a segment run rather than a single parsed
-- segment to allow for alternants like "((rēs<5>pūblica<1>,rēspūblica<1>))".
-- The parsed segment runs in PARSED_SEGMENT_RUNS are tables as returned by
-- parse_segment_run() (of the same form as the overall return value of
-- parse_segment_run_allowing_alternants()).
local function parse_segment_run_allowing_alternants(segment_run)
if segment_run:find(" ", nil, true) then
track("has-space")
end
if segment_run:find("((", nil, true) then
track("has-alternant")
end
local alternating_segments = split(segment_run, "(%(%(.-%)%))")
local parsed_segments = {}
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i = 1, #alternating_segments do
local alternating_segment = alternating_segments[i]
if alternating_segment ~= "" then
local this_is_adj
if i % 2 == 1 then
local parsed_run = parse_segment_run(alternating_segment)
for _, parsed_segment in ipairs(parsed_run.segments) do
insert(parsed_segments, parsed_segment)
end
loc = loc or parsed_run.loc
num = num or parsed_run.num
gender = gender or parsed_run.gender
this_is_adj = parsed_run.is_adj
for _, props in ipairs(parsed_run.propses) do
insert(propses, props)
end
else
local parsed_alternating_segment = parse_alternant(alternating_segment)
insert(parsed_segments, parsed_alternating_segment)
loc = loc or parsed_alternating_segment.loc
num = num or parsed_alternating_segment.num
gender = gender or parsed_alternating_segment.gender
this_is_adj = parsed_alternating_segment.is_adj
insert(propses, parsed_alternating_segment.propses)
end
if is_adj == nil then
is_adj = this_is_adj
elseif this_is_adj ~= nil then
is_adj = is_adj and this_is_adj
end
end
end
if #parsed_segments > 1 then
track("multiple-segments")
end
return {
segments = parsed_segments,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Combine each form in FORMS (a list of forms associated with a slot) with each
-- form in NEW_FORMS (either a single string for a single form, or a list of
-- forms) by concatenating EXISTING_FORM .. PREFIX .. NEW_FORM. Also combine
-- NOTES (a table specifying the footnotes associated with each existing form,
-- i.e. a map from form indices to lists of footnotes) with NEW_NOTES (new
-- footnotes associated with the new forms, in the same format as NOTES). Return
-- a pair NEW_FORMS, NEW_NOTES where either or both of FORMS and NOTES (but not
-- the sublists in NOTES) may be destructively modified to generate the return
-- values.
local function append_form(forms, notes, new_forms, new_notes, prefix)
if forms == nil then
return
end
new_forms = new_forms or ""
notes = notes or {}
new_notes = new_notes or {}
prefix = prefix or ""
if type(new_forms) == "table" and #new_forms == 1 then
new_forms = new_forms[1]
end
if type(new_forms) == "string" then
-- If there's only one new form, destructively modify the existing
-- forms and notes for this new form and its footnotes.
for i = 1, #forms do
forms[i] = forms[i] .. prefix .. new_forms
if new_notes[1] then
if not notes[i] then
notes[i] = new_notes[1]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[1]) do
insert(combined_notes, note)
end
notes[i] = combined_notes
end
end
end
return forms, notes
else
-- If there are multiple new forms, we need to loop over all
-- combinations of new and old forms. In that case, use new tables
-- for the combined forms and notes.
local ret_forms = {}
local ret_notes = {}
for i=1, #forms do
for j=1, #new_forms do
insert(ret_forms, forms[i] .. prefix .. new_forms[j])
if new_notes[j] then
if not notes[i] then
-- We are constructing a linearized matrix of size
-- NI x NJ where J is in the inner loop. If I and J
-- are zero-based, the linear index of (I, J) is
-- I * NJ + J. However, we are one-based, so the
-- same formula won't work. Instead, we effectively
-- need to convert to zero-based indices, compute
-- the zero-based linear index, and then convert it
-- back to a one-based index, i.e.
--
-- (I - 1) * NJ + (J - 1) + 1
--
-- i.e. (I - 1) * NJ + J.
ret_notes[(i - 1) * #new_forms + j] = new_notes[j]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[j]) do
insert(combined_notes, note)
end
ret_notes[(i - 1) * #new_forms + j] = combined_notes
end
end
end
end
return ret_forms, ret_notes
end
end
-- Destructively modify any forms in FORMS (a map from a slot to a form or a
-- list of forms) by converting sequences of ae, oe, Ae or Oe to the
-- appropriate ligatures.
local function apply_ligatures(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
forms[slot] = forms[slot]:gsub("[AaOo]e", ligatures)
elseif type(forms[slot]) == "table" then
for i = 1, #forms[slot] do
forms[slot][i] = forms[slot][i]:gsub("[AaOo]e", ligatures)
end
end
end
end
-- Modify any forms in FORMS (a map from a slot to a form or a list of forms) by
-- converting final m to optional n or m.
local function apply_sufn(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
if forms[slot]:sub(-1) == "m" then
forms[slot] = {forms[slot]:gsub("m$", "n"), forms[slot]}
end
elseif type(forms[slot]) == "table" then
-- See if there are any final m's.
local final_m
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
final_m = true
break
end
end
if final_m then
local newval = {}
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
insert(newval, (forms[slot][i]:gsub("m$", "n")))
end
insert(newval, forms[slot][i])
end
forms[slot] = newval
end
end
end
end
-- If NUM == "sg", copy the singular forms to the plural ones; vice-versa if
-- NUM == "pl". This should allow for the equivalent of plural
-- "alpha and omega" formed from two singular nouns, and for the equivalent of
-- plural "St. Vincent and the Grenadines" formed from a singular noun and a
-- plural noun. (These two examples actually occur in Russian, at least.)
local function propagate_number_restrictions(forms, num, is_adj)
if num == "sg" or num == "pl" then
for slot in iter_slots(is_adj) do
if slot:find(num, nil, true) then
local other_num_slot = num == "sg" and slot:gsub("sg", "pl") or slot:gsub("pl", "sg")
forms[other_num_slot] = type(forms[slot]) == "table" and deep_copy(forms[slot]) or forms[slot]
end
end
end
end
local function join_sentences(sentences, joiner)
-- Lowercase the first letter of all but the first sentence, and remove the
-- final period from all but the last sentence. Then join together with the
-- joiner (e.g. " and " or " or ").
-- FIXME: Should we join three or more as e.g. "foo, bar and baz"?
local sentences_to_join = {}
for i, sentence in ipairs(sentences) do
if i < #sentences then
sentence = sentence:gsub("%.$", "")
end
if i > 1 then
sentence = lcfirst(sentence)
end
insert(sentences_to_join, sentence)
end
return concat(sentences_to_join, joiner)
end
-- Construct the declension of a parsed segment run of the form returned by
-- parse_segment_run() or parse_segment_run_allowing_alternants(). Return value
-- is a table
-- {
-- forms = FORMS (keyed by slot, list of forms for that slot),
-- notes = NOTES (keyed by slot, map from form indices to lists of footnotes),
-- title = TITLE (list of titles for each segment in the run),
-- categories = CATEGORIES (combined categories for all segments),
-- }
local function decline_segment_run(parsed_run, pos, is_adj)
local declensions = {
-- For each possible slot (e.g. "abl_sg"), list of possible forms.
forms = {},
-- Keyed by slot (e.g. "abl_sg"). Value is a table indicating the footnotes
-- corresponding to the forms for that slot. Each such table maps indices
-- (the index of the corresponding form) to a list of one or more
-- footnotes.
notes = {},
title = {},
unattested = {},
subtitleses = {},
orig_titles = {},
categories = {},
footnotes = {},
-- May be set true if declining a 1-1 adjective
loc = false,
noneut = false,
nomf = false,
}
for slot in iter_slots(is_adj) do
declensions.forms[slot] = {""}
end
for i, seg in ipairs(parsed_run.segments) do
local decl = seg.decl
if decl then -- not an alternant, not a constant segment
seg.loc = parsed_run.loc
seg.num = seg.num or parsed_run.num
seg.gender = seg.gender or parsed_run.gender
local data, potential_lemma_slots
if seg.is_adj then
if not (m_adj_decl or get_m_adj_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_adj_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
gender = seg.gender,
loc = seg.loc,
noneut = false,
nomf = false,
pos = is_adj and pos or "คำคุณศัพท์",
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_adj_decl or get_m_adj_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
if data.loc then
declensions.loc = true
end
if data.noneut then
declensions.noneut = true
end
if data.nomf then
declensions.nomf = true
end
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg+" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. english .. " ภาษาละติน|การผันรูปนามกลุ่ม" .. english .. "]]"
elseif apparent_decl == "irreg+" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl+" or apparent_decl == "0+" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize adjective declension " .. apparent_decl)
end
data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with ''idem'', ''quīdam''
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
else
if not (m_noun_decl or get_m_noun_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_noun_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
loc = seg.loc,
pos = pos,
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_noun_decl or get_m_noun_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
seg.props.headword_decl = apparent_decl
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. english .. " ภาษาละติน|การผันรูปนามกลุ่ม" .. english .. "]]"
elseif apparent_decl == "irreg" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl" or apparent_decl == "0" or apparent_decl == "sgpl" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize noun declension " .. apparent_decl)
end
data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with 1st-declension ''-ābus'' ending where
-- we want a common prefix to be extracted out if possible
-- in the alternant title-generating code.
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
end
-- Generate linked variants of slots that may be the lemma.
-- If the form is the same as the lemma (with links removed),
-- substitute the original lemma (with links included).
for _, slot in ipairs(potential_lemma_slots) do
local forms = data.forms[slot]
if forms then
local linked_forms = {}
if type(forms) ~= "table" then
forms = {forms}
end
for _, form in ipairs(forms) do
if form == seg.lemma then
insert(linked_forms, seg.orig_lemma)
else
insert(linked_forms, form)
end
end
data.forms["linked_" .. slot] = linked_forms
end
end
if seg.types.lig then
apply_ligatures(data.forms, is_adj)
end
if seg.types.sufn then
apply_sufn(data.forms, is_adj)
end
propagate_number_restrictions(data.forms, seg.num, is_adj)
for slot in iter_slots(is_adj) do
-- 1. Select the forms to append to the existing ones.
local new_forms
if is_adj then
if not seg.is_adj then
error("Can't decline noun '" .. seg.lemma .. "' when overall term is an adjective")
end
new_forms = data.forms[slot]
if not new_forms and slot:find("_[fn]$") then
new_forms = data.forms[slot:gsub("_[fn]$", "_m")]
end
elseif seg.is_adj then
if not seg.gender then
error("Declining modifying adjective " .. seg.lemma .. " but don't know gender of associated noun")
end
-- Select the appropriately gendered equivalent of the case/number
-- combination. Some adjectives won't have feminine or neuter
-- variants, though (e.g. 3-1 and 3-2 adjectives don't have a
-- distinct feminine), so in that case select the masculine.
new_forms = data.forms[slot .. "_" .. mw.ustring.lower(seg.gender)]
or data.forms[slot .. "_m"]
else
new_forms = data.forms[slot]
end
-- 2. Extract the new footnotes in the format we require, which is
-- different from the format passed in by the declension functions.
local new_notes = {}
if type(new_forms) == "string" and data.notes[slot .. "1"] then
new_notes[1] = {data.notes[slot .. "1"]}
elseif new_forms then
for j = 1, #new_forms do
if data.notes[slot .. j] then
new_notes[j] = {data.notes[slot .. j]}
end
end
end
-- 3. Append new forms and footnotes to the existing ones.
new_forms = normalize_form(new_forms)
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot], new_forms,
new_notes, slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
end
for slot, v in pairs(data.unattested) do
if v then
declensions.unattested[slot] = true
end
end
if not seg.types.nocat and (is_adj or not seg.is_adj) then
for _, cat in ipairs(data.categories) do
insert_if_not(declensions.categories, cat)
end
end
if data.footnote then
insert(declensions.footnotes, data.footnote)
end
if seg.prefix ~= "" and seg.prefix ~= "-" and seg.prefix ~= " " then
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
insert(declensions.title, data.title)
elseif seg.alternants then
local seg_declensions = nil
local seg_titles = {}
local seg_subtitleses = {}
local seg_stems_seen = {}
local seg_unattested = {}
local seg_categories = {}
local seg_footnotes = {}
-- If all alternants have exactly one non-constant segment and all are
-- of the same declension, we use special code that displays the
-- differences in the subtitles. Otherwise we use more general code
-- that displays the full title and subtitles of each segment,
-- separating segment combined titles by "and" and the segment-run
-- combined titles by "or".
local title_the_hard_way = false
local alternant_decl = nil
local alternant_decl_title = nil
for _, this_parsed_run in ipairs(seg.alternants) do
local num_non_constant_segments = 0
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
if not alternant_decl then
alternant_decl = segment.decl
elseif alternant_decl ~= segment.decl then
title_the_hard_way = true
num_non_constant_segments = 500
break
end
num_non_constant_segments = num_non_constant_segments + 1
end
end
if num_non_constant_segments ~= 1 then
title_the_hard_way = true
break
end
end
if not title_the_hard_way then
-- If using the special-purpose code, find the subtypes that are
-- not present in a given alternant but are present in at least
-- one other, and record "negative" variants of these subtypes
-- so that the declension-construction code can record subtitles
-- for these negative variants (so we can construct text like
-- "i-stem or imparisyllabic non-i-stem").
local subtypeses = {}
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
insert(subtypeses, segment.types)
insert_if_not(seg_stems_seen, segment.stem2)
end
end
end
local union = set_union(subtypeses)
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
local neg_subtypes = set_difference(union, segment.types)
for neg_subtype, _ in pairs(neg_subtypes) do
segment.types["not_" .. neg_subtype] = true
end
end
end
end
end
for _, this_parsed_run in ipairs(seg.alternants) do
this_parsed_run.loc = seg.loc
this_parsed_run.num = this_parsed_run.num or seg.num
this_parsed_run.gender = this_parsed_run.gender or seg.gender
local this_declensions = decline_segment_run(this_parsed_run, pos, is_adj)
if this_declensions.noneut then
declensions.noneut = true
end
if this_declensions.nomf then
declensions.nomf = true
end
-- If there's a number restriction on the segment run, blank
-- out the forms outside the restriction. This allows us to
-- e.g. construct heteroclites that decline one way in the
-- singular and a different way in the plural.
if this_parsed_run.num == "sg" or this_parsed_run.num == "pl" then
for slot in iter_slots(is_adj) do
if this_parsed_run.num == "sg" and slot:find("pl", nil, true) or
this_parsed_run.num == "pl" and slot:find("sg", nil, true) then
this_declensions.forms[slot] = {}
this_declensions.notes[slot] = nil
end
end
end
if not seg_declensions then
seg_declensions = this_declensions
else
for slot in iter_slots(is_adj) do
-- For a given slot, combine the existing and new forms.
-- We do this by checking to see whether a new form is
-- already present and not adding it if so; in the
-- process, we keep a map from indices in the new forms
-- to indices in the combined forms, for use in
-- combining footnotes below.
local curforms = seg_declensions.forms[slot] or {}
local newforms = this_declensions.forms[slot] or {}
local newform_index_to_new_index = {}
for newj, form in ipairs(newforms) do
local did_break = false
for j = 1, #curforms do
if curforms[j] == form then
newform_index_to_new_index[newj] = j
did_break = true
break
end
end
if not did_break then
insert(curforms, form)
newform_index_to_new_index[newj] = #curforms
end
end
seg_declensions.forms[slot] = curforms
-- Now combine the footnotes. Keep in mind that
-- each form may have its own set of footnotes, and
-- in some cases we didn't add a form from the new
-- list of forms because it already occurred in the
-- existing list of forms; in that case, we combine
-- footnotes from the two sources.
local curnotes = seg_declensions.notes[slot]
local newnotes = this_declensions.notes[slot]
if newnotes then
if not curnotes then
curnotes = {}
end
for index, notes in pairs(newnotes) do
local combined_index = newform_index_to_new_index[index]
if not curnotes[combined_index] then
curnotes[combined_index] = notes
else
local combined = mw.clone(curnotes[combined_index])
for _, note in ipairs(newnotes) do
insert_if_not(combined, note)
end
curnotes[combined_index] = combined
end
end
end
end
end
for slot, v in pairs(this_declensions.unattested) do
if v then
seg_unattested[slot] = true
end
end
for _, cat in ipairs(this_declensions.categories) do
insert_if_not(seg_categories, cat)
end
for _, footnote in ipairs(this_declensions.footnotes) do
insert_if_not(seg_footnotes, footnote)
end
insert_if_not(seg_titles, this_declensions.title)
for _, subtitles in ipairs(this_declensions.subtitleses) do
insert(seg_subtitleses, subtitles)
end
if not alternant_decl_title then
alternant_decl_title = this_declensions.orig_titles[1]
end
end
-- If overall run is singular, copy singular to plural, and
-- vice-versa. See propagate_number_restrictions() for rationale;
-- also, this should eliminate cases of empty forms, which will
-- cause the overall set of forms for that slot to be empty.
propagate_number_restrictions(seg_declensions.forms, parsed_run.num, is_adj)
for slot in iter_slots(is_adj) do
local new_forms = normalize_form(seg_declensions.forms[slot])
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
new_forms, seg_declensions.notes[slot], nil)
end
end
for slot, v in pairs(seg_unattested) do
if v then
declensions.unattested[slot] = true
end
end
if is_adj or not seg.is_adj then
for _, cat in ipairs(seg_categories) do
insert_if_not(declensions.categories, cat)
end
end
for _, footnote in ipairs(seg_footnotes) do
insert_if_not(declensions.footnotes, footnote)
end
local title_to_insert
if title_the_hard_way then
title_to_insert = join_sentences(seg_titles, " or ")
else
-- Special-purpose title-generation code, for the common
-- situation where each alternant has single-segment runs and
-- all segments belong to the same declension.
--
-- 1. Find the initial subtitles common to all segments.
local first_subtitles = seg_subtitleses[1]
local num_common_subtitles = #first_subtitles
for j = 2, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
for k = 1, num_common_subtitles do
if not deep_equals(first_subtitles[k], this_subtitles[k]) then
num_common_subtitles = k - 1
break
end
end
end
-- 2. Construct the portion of the text based on the common subtitles.
local common_subtitles = {}
for j = 1, num_common_subtitles do
if type(first_subtitles[j]) == "table" then
insert(common_subtitles, concat(first_subtitles[j]))
else
insert(common_subtitles, first_subtitles[j])
end
end
local common_subtitle_portion = concat(common_subtitles, ", ")
local non_common_subtitle_portion
-- 3. Special-case the situation where there's one non-common
-- subtitle in each segment and a common prefix or suffix to
-- all of them.
local common_prefix, common_suffix
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
if #this_subtitles ~= num_common_subtitles + 1 or
type(this_subtitles[num_common_subtitles + 1]) ~= "table" or
#this_subtitles[num_common_subtitles + 1] ~= 2 then
break
end
if j == 1 then
common_prefix = this_subtitles[num_common_subtitles + 1][1]
common_suffix = this_subtitles[num_common_subtitles + 1][2]
else
local this_prefix = this_subtitles[num_common_subtitles + 1][1]
local this_suffix = this_subtitles[num_common_subtitles + 1][2]
if this_prefix ~= common_prefix then
common_prefix = nil
end
if this_suffix ~= common_suffix then
common_suffix = nil
end
if not common_prefix and not common_suffix then
break
end
end
end
if common_prefix or common_suffix then
if common_prefix and common_suffix then
error("Something is wrong, first non-common subtitle is actually common to all segments")
end
if common_prefix then
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][2])
end
non_common_subtitle_portion = common_prefix .. concat(non_common_parts, " or ")
else
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][1])
end
non_common_subtitle_portion = concat(non_common_parts, " or ") .. common_suffix
end
else
-- 4. Join the subtitles that differ from segment to segment.
-- Record whether there are any such differing subtitles.
-- If some segments have differing subtitles and others don't,
-- we use the text "otherwise" for the segments without
-- differing subtitles.
local saw_non_common_subtitles = false
local non_common_subtitles = {}
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
local this_non_common_subtitles = {}
for k = num_common_subtitles + 1, #this_subtitles do
if type(this_subtitles[k]) == "table" then
insert(this_non_common_subtitles, concat(this_subtitles[k]))
else
insert(this_non_common_subtitles, this_subtitles[k])
end
end
if #this_non_common_subtitles > 0 then
insert(non_common_subtitles, concat(this_non_common_subtitles, ", "))
saw_non_common_subtitles = true
else
insert(non_common_subtitles, "otherwise")
end
end
non_common_subtitle_portion =
saw_non_common_subtitles and concat(non_common_subtitles, " or ") or ""
end
-- 5. Combine the common and non-common subtitle portions.
local subtitle_portions = {}
if common_subtitle_portion ~= "" then
insert(subtitle_portions, common_subtitle_portion)
end
if non_common_subtitle_portion ~= "" then
insert(subtitle_portions, non_common_subtitle_portion)
end
if #seg_stems_seen > 1 then
insert(subtitle_portions,
(number_to_english[#seg_stems_seen] or "" .. #seg_stems_seen) .. " different stems"
)
end
local subtitle_portion = concat(subtitle_portions, "; ")
if subtitle_portion ~= "" then
title_to_insert = alternant_decl_title .. " (" .. subtitle_portion .. ")"
else
title_to_insert = alternant_decl_title
end
end
-- Don't insert blank title (happens e.g. with "((ali))quis<irreg+>").
if title_to_insert ~= "" then
insert(declensions.title, title_to_insert)
end
else
for slot in iter_slots(is_adj) do
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
end
-- First title is uppercase, remainder have an indefinite article, joined
-- using "with".
local titles = {}
for i, title in ipairs(declensions.title) do
if i == 1 then
insert(titles, ucfirst(title))
else
insert(titles, add_indefinite_article(title))
end
end
declensions.title = concat(titles, " with ")
return declensions
end
local function construct_title(args_title, declensions_title, generate_type, parsed_run)
if args_title then
declensions_title = args_title:gsub("<1>", "[[ภาคผนวก:Latin first declension|first declension]]")
declensions_title = declensions_title:gsub("<1&2>", "[[ภาคผนวก:Latin first declension|first]]/[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<2>", "[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<3>", "[[ภาคผนวก:Latin third declension|third declension]]")
declensions_title = declensions_title:gsub("<4>", "[[ภาคผนวก:Latin fourth declension|fourth declension]]")
declensions_title = declensions_title:gsub("<5>", "[[ภาคผนวก:Latin fifth declension|fifth declension]]")
if generate_type == "headword" then
declensions_title = lcfirst((declensions_title:gsub("%.$", "")))
else
declensions_title = ucfirst(declensions_title)
end
else
local post_text_parts = {}
if parsed_run.loc then
insert(post_text_parts, ", with locative")
end
-- FIXME: Hierosolyma has -a/-ōrum (2nd neut. pl. with sg. meaning) and -a/-ae (1st fem. sg.)
-- alternants, but because it has singular and plural forms overall, it's not made clear that
-- each alternant is restricted to one number.
if parsed_run.num == "sg" then
insert(post_text_parts, ", เอกพจน์เท่านั้น")
elseif parsed_run.num == "pl" then
insert(post_text_parts, ", พหูพจน์เท่านั้น")
end
local post_text = concat(post_text_parts)
if generate_type == "headword" then
declensions_title = lcfirst(declensions_title) .. post_text
else
declensions_title = ucfirst(declensions_title) .. post_text .. "."
end
end
return declensions_title
end
function export.do_generate_noun_forms(parent_args, pos, generate_type, def)
local params = {
[1] = {required = true, default = def or "aqua<1>"},
footnote = true,
title = true,
num = true,
json = {type = "boolean"},
}
for slot in iter_noun_slots() do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.id = true
params.cat = list
params.m = sublist
params.f = sublist
params.n = sublist
params.adj = sublist
params.dim = sublist
params.aug = sublist
params.g = list
params.indecl = {type = "boolean"}
end
if pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if args.title then
track("overriding-title")
end
local parsed_run = parse_segment_run_allowing_alternants(args[1])
parsed_run.loc = parsed_run.loc or not not (args.loc_sg or args.loc_pl)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, false)
if not parsed_run.loc then
declensions.forms.loc_sg = nil
declensions.forms.loc_pl = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
gender = parsed_run.gender,
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
overriding_lemma = args.lemma,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
m = args.m,
f = args.f,
overriding_genders = args.g,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_noun_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_noun_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.do_generate_adj_forms(parent_args, pos, generate_type, degree, def)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, default = def or "bonus"},
footnote = true,
title = true,
num = true,
noneut = boolean,
nomf = boolean,
json = boolean,
}
for slot in iter_adj_slots() do
params[slot] = true
end
for slot in iter_noun_slots(true) do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.adv = sublist
params.id = true
params.cat = list
params.indecl = boolean
if degree == "ขั้นกว่า" or degree == "ขั้นสุด" then
params.positive = sublist
end
if degree ~= "ขั้นกว่า" then
params.comp = sublist
end
if degree ~= "ขั้นสุด" then
params.sup = sublist
end
end
local set_pos
if not pos then
params.pos = {set = {"คำคุณศัพท์", "ตัวกำหนด", "รูปกริยาขยาย", "คำสรรพนาม"}, default = "คำคุณศัพท์"}
set_pos = true
elseif pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if set_pos then
pos = pluralize(args.pos)
end
for slot in iter_noun_slots(true) do
local arg = args[slot]
if arg ~= nil then
for i = 1, genders_n do
local slot_g = slot .. "_" .. genders[i]
local arg_g = args[slot_g]
if arg_g == nil then
args[slot_g] = arg
end
end
args[slot] = nil
end
end
if args.title then
track("overriding-title")
end
local segment_run = args[1]
if not segment_run:match("[<(]") then
-- If the segment run doesn't have any explicit declension specs or alternants,
-- add a default declension spec of <+> to it (or <0+> for indeclinable
-- adjectives). This allows the majority of adjectives to just specify
-- the lemma.
segment_run = segment_run .. (args.indecl and "<0+>" or "<+>")
end
local parsed_run = parse_segment_run_allowing_alternants(segment_run)
parsed_run.loc = parsed_run.loc or not not (
args.loc_sg_m or args.loc_sg_f or args.loc_sg_n or args.loc_pl_m or args.loc_pl_f or args.loc_pl_n
)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, true)
if not parsed_run.loc then
declensions.forms.loc_sg_m = nil
declensions.forms.loc_sg_f = nil
declensions.forms.loc_sg_n = nil
declensions.forms.loc_pl_m = nil
declensions.forms.loc_pl_f = nil
declensions.forms.loc_pl_n = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
accel = {},
loc = declensions.loc,
noneut = args.noneut or declensions.noneut,
nomf = args.nomf or declensions.nomf,
overriding_lemma = args.lemma,
positive = args.positive,
comp = args.comp,
sup = args.sup,
adv = args.adv,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_adj_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_adj_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.show_noun(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_noun_forms(parent_args, "คำนาม")
if type(data) == "string" then -- JSON
return data
end
show_forms(data, false)
local num = data.num
if num == "sg" then
return make_noun_table_sg(data)
elseif num == "pl" then
return make_noun_table_pl(data)
end
return make_noun_table(data)
end
function export.show_adj(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_adj_forms(parent_args)
if type(data) == "string" then -- JSON
return data
end
partial_show_forms(data, true)
return make_adj_table(data)
end
return export
3jfysdh4soatl9bmubhernf7jocwwx2
5776191
5776190
2026-11-02T01:37:44Z
Octahedron80
267
5776191
Scribunto
text/plain
local export = {}
--[=[
Authorship: Ben Wing <benwing2>, with many ideas and a little code coming from
the old [[Module:la-decl-multi]] by KC Kenny Lau.
]=]
-- TODO:
-- (DONE) Eliminate specification of noteindex from la-adj/data
-- (DONE?) Finish autodetection of adjectives
-- (DONE) Remove old noun code
-- (DONE) Implement <.sufn>
-- (DONE) Look into adj voc=false
-- (DONE) Handle loc in adjectives
-- Error on bad subtypes
-- Make sure Google Books link still works.
-- (DONE) Make sure .sufn triggers insertion of 'with m optionally -> n in compounds' in title.
-- (DONE) Make sure title returned to la-adj lowercases the first letter even with a custom title.
--[=[
TERMINOLOGY:
-- "slot" = A particular case/number combination (for nouns) or
case/number/gender combination (for adjectives). Example slot names are
"abl_sg" (for noun) or "acc_pl_f" (for adjectives). Each slot is filled
with zero or more forms.
-- "form" = The declined Latin form representing the value of a given slot.
For example, rēge is a form, representing the value of the abl_sg slot of
the lemma rēx.
-- "lemma" = The dictionary form of a given Latin term. For nouns, it's
generally the nominative singular, but will be the nominative plural of
plurale tantum nouns (e.g. [[castra]]), and may occasionally be another
form (e.g. the genitive singular) if the nominative singular is missing.
For adjectives, it's generally the masculine nominative singular, but
will be the masculine nominative plural of plurale tantum adjectives
(e.g. [[dēnī]]).
-- "plurale tantum" (plural "pluralia tantum") = A noun or adjective that
exists only in the plural. Examples are castra "army camp", faucēs "throat",
and dēnī "ten each" (used for counting pluralia tantum nouns).
-- "singulare tantum" (plural "singularia tantum") = A noun or adjective that
exists only in the singular. Examples are geōlogia "geology" (and in
general most non-count nouns) and the adjective ūnus "one".
]=]
local debug_track_module = "Module:debug/track"
local en_utilities_module = "Module:en-utilities"
local headword_data_module = "Module:headword/data"
local json_module = "Module:JSON"
local la_adj_data_module = "Module:la-adj/data"
local la_adj_table_module = "Module:la-adj/table"
local la_noun_data_module = "Module:la-noun/data"
local la_noun_table_module = "Module:la-noun/table"
local la_utilities_module = "Module:la-utilities"
local languages_module = "Module:languages"
local links_module = "Module:links"
local load_module = "Module:load"
local parameters_module = "Module:parameters"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local concat = table.concat
local insert = table.insert
local iter_adj_slots -- defined below
local iter_noun_slots -- defined below
local umatch = mw.ustring.match
local function add_indefinite_article(...)
add_indefinite_article = require(en_utilities_module).add_indefinite_article
return add_indefinite_article(...)
end
local function contains(...)
contains = require(table_module).contains
return contains(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function deep_equals(...)
deep_equals = require(table_module).deepEquals
return deep_equals(...)
end
local function full_link(...)
full_link = require(links_module).full_link
return full_link(...)
end
local function insert_if_not(...)
insert_if_not = require(table_module).insertIfNot
return insert_if_not(...)
end
local function lcfirst(...)
lcfirst = require(string_utilities_module).lcfirst
return lcfirst(...)
end
local function load_data(...)
load_data = require(load_module).load_data
return load_data(...)
end
local function make_adj_table(...)
make_adj_table = require(la_adj_table_module).make_table
return make_adj_table(...)
end
local function make_noun_table(...)
make_noun_table = require(la_noun_table_module).make_table
return make_noun_table(...)
end
local function make_noun_table_sg(...)
make_noun_table_sg = require(la_noun_table_module).make_table_sg
return make_noun_table_sg(...)
end
local function make_noun_table_pl(...)
make_noun_table_pl = require(la_noun_table_module).make_table_pl
return make_noun_table_pl(...)
end
local function make_stem2(...)
make_stem2 = require(la_utilities_module).make_stem2
return make_stem2(...)
end
local function normalize_form(...)
normalize_form = require(la_utilities_module).normalize_form
return normalize_form(...)
end
local function pluralize(...)
pluralize = require(en_utilities_module).pluralize
return pluralize(...)
end
local function process_params(...)
process_params = require(parameters_module).process
return process_params(...)
end
local function remove_links(...)
remove_links = require(links_module).remove_links
return remove_links(...)
end
local function singularize(...)
singularize = require(en_utilities_module).singularize
return singularize(...)
end
local function split(...)
split = require(string_utilities_module).split
return split(...)
end
local function ucfirst(...)
ucfirst = require(string_utilities_module).ucfirst
return ucfirst(...)
end
local m_adj_decl
local function get_m_adj_decl()
m_adj_decl, get_m_adj_decl = require(la_adj_data_module), nil
return m_adj_decl
end
local m_noun_decl
local function get_m_noun_decl()
m_noun_decl, get_m_noun_decl = require(la_noun_data_module), nil
return m_noun_decl
end
local lang
local function get_lang()
lang, get_lang = require(languages_module).getByCode("la")
return lang
end
local namespace
local function get_namespace()
namespace, get_namespace = load_data(headword_data_module).page.namespace, nil
return namespace
end
local pagename
local function get_pagename()
pagename, get_pagename = load_data(headword_data_module).pagename, nil
return pagename
end
local ligatures = {
['Ae'] = 'Æ',
['ae'] = 'æ',
['Oe'] = 'Œ',
['oe'] = 'œ',
}
local cases = {
"nom", "gen", "acc", "dat", "abl", "voc", "loc"
}
local cases_n = #cases
local nums = {
"sg", "pl"
}
local nums_n = #nums
local genders = {
"m", "f", "n"
}
local genders_n = #genders
local declension_to_english = setmetatable({
["1"] = "ที่ 1",
["1&2"] = "ที่ 1 และ 2",
["2"] = "ที่ 2",
["3"] = "ที่ 3",
["4"] = "ที่ 4",
["5"] = "ที่ 5",
}, {
__index = function(t, k)
return rawget(t, k:match("^[^+-]*"))
end
})
local number_to_english = {
"one", "two", "three", "four", "five"
}
local linked_prefixes = {
"", "linked_"
}
function export.iter_potential_noun_lemma_slots()
local num, case = 1, 0
return function()
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
return cases[case] .. "_" .. nums[num]
end
end
local potential_noun_lemma_slots = {}
for slot in export.iter_potential_noun_lemma_slots() do
insert(potential_noun_lemma_slots, slot)
end
local linked_to_non_linked_noun_slots = {}
for _, slot in ipairs(potential_noun_lemma_slots) do
linked_to_non_linked_noun_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with a noun declension, where a slot
-- is a particular case/number combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg, linked_nom_pl), which aren't overridable.
function export.iter_noun_slots(overridable_only)
local case, num, linked_variant = 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num]
end
end
iter_noun_slots = export.iter_noun_slots
function export.iter_potential_adj_lemma_slots()
local num, case, gen = 1, 1, 0
return function()
gen = gen + 1
if gen > genders_n then
gen = 1
case = case + 1
if case > 3 then
case = 1
num = num + 1
if num > nums_n then
return nil
end
end
end
return cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
-- List of adjective slots for which we generate linked variants. Include
-- feminine and neuter variants because they will be needed if the adjective
-- is part of a multiword feminine or neuter noun.
local potential_adj_lemma_slots = {}
for slot in export.iter_potential_adj_lemma_slots() do
insert(potential_adj_lemma_slots, slot)
end
local linked_to_non_linked_adj_slots = {}
for _, slot in ipairs(potential_adj_lemma_slots) do
linked_to_non_linked_adj_slots["linked_" .. slot] = slot
end
-- Iterate over all the "slots" associated with an adjective declension, where a slot
-- is a particular case/number/gender combination. If overridable_only, don't include the
-- "linked_" variants (linked_nom_sg_m, linked_nom_pl_m, etc.), which aren't overridable.
function export.iter_adj_slots(overridable_only)
local case, num, gen, linked_variant = 1, 1, 1, 0
return function()
linked_variant = linked_variant + 1
local max_linked_variant = (overridable_only or case > 3) and 1 or 2
if linked_variant > max_linked_variant then
linked_variant = 1
gen = gen + 1
if gen > genders_n then
gen = 1
num = num + 1
if num > nums_n then
num = 1
case = case + 1
if case > cases_n then
return nil
end
end
end
end
return linked_prefixes[linked_variant] .. cases[case] .. "_" .. nums[num] .. "_" .. genders[gen]
end
end
iter_adj_slots = export.iter_adj_slots
-- Iterate over all the "slots" associated with a noun or adjective declension (depending on
-- the value of IS_ADJ), where a slot is a particular case/number combination (in the case of
-- nouns) or case/number/gender combination (in the case of adjectives). If OVERRIDABLE_ONLY
-- is specified, only include overridable slots (not including linked_ variants).
local function iter_slots(is_adj, overridable_only)
if is_adj then
return iter_adj_slots(overridable_only)
end
return iter_noun_slots(overridable_only)
end
local function concat_forms_in_slot(forms)
if forms and forms ~= "" and forms ~= "—" and #forms > 0 then
local new_vals = {}
for _, v in ipairs(forms) do
insert(new_vals, (v:gsub("|", "<!>")))
end
return concat(new_vals, ",")
end
end
local function glossary_link(anchor, text)
text = text or anchor
return "[[ภาคผนวก:อภิธานศัพท์#" .. anchor .. "|" .. text .. "]]"
end
local function track(page)
debug_track("la-nominal/" .. page)
return true
end
local function set_union(sets)
local union = {}
for _, set in ipairs(sets) do
for key, _ in pairs(set) do
union[key] = true
end
end
return union
end
local function set_difference(set1, set2)
local diff = {}
for key, _ in pairs(set1) do
if not set2[key] then
diff[key] = true
end
end
return diff
end
-- If a form is set as '*', that means it is unattested
-- but should still be generated
-- TODO: handle asterisks in forms stored in the data
local function unattested_forms(data, args, is_adj)
for slot in iter_slots(is_adj) do
local arg = args[slot]
if arg ~= nil then
arg = arg:match("^*(.*)")
if arg then
data.unattested[slot] = true
args[slot] = arg ~= "" and arg or nil
end
end
end
end
-- Make a link only if the form is attested
local function link_if_attested(form, accel, is_unattested)
local data = {lang = lang or get_lang()}
if is_unattested then
data.alt = "*" .. form
else
data.term = form
data.accel = accel
end
return full_link(data)
end
local function process_form(slot, data, args, linked_to_non_linked)
local forms = data.forms
-- If nomf=1 passed, clear out all masculine and feminine forms.
if data.nomf and slot:match("%f[^%z_][mf]%f[%z_]") then
forms[slot] = nil
end
-- If noneut=1 passed, clear out all neuter forms.
if data.noneut and slot:match("%f[^%z_]n%f[%z_]") then
forms[slot] = nil
end
local val
if args[slot] then
val = args[slot]
data.user_specified[slot] = true
else
-- Overridding nom_sg/nom_sg_m etc. should override linked_nom_sg
-- so that the correct value gets displayed in the headword, which
-- uses linked_nom_sg.
local non_linked_equiv_slot = linked_to_non_linked[slot]
if non_linked_equiv_slot and args[non_linked_equiv_slot] then
val = args[non_linked_equiv_slot]
data.user_specified[slot] = true
else
val = forms[slot]
end
end
if val then
if type(val) == "string" then
val = split(val, "/", true, true)
end
local num = data.num
if (
(num == "pl" and slot:find("sg", nil, true)) or
(num == "sg" and slot:find("pl", nil, true))
) then
forms[slot] = nil
elseif val[1] == "" or val[1] == "-" or val[1] == "—" then
forms[slot] = "—"
if val[2] then
error("Cannot specify additional forms for " .. slot .. ' if it has been cancelled with "-"')
end
else
forms[slot] = val
end
end
end
local function process_noun_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args);
-- Process overrides and canonicalize forms.
for slot in iter_noun_slots() do
process_form(slot, data, args, linked_to_non_linked_noun_slots)
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num]
else
accel_lemma = data.forms["nom_sg"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_noun_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]$", "|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
local function process_adj_forms_and_overrides(data, args, generate_type)
local redlink = false
unattested_forms(data, args, true)
-- Process overrides and canonicalize forms.
for slot in iter_adj_slots() do
process_form(slot, data, args, linked_to_non_linked_adj_slots)
end
-- See if the masculine and feminine/neuter are the same across all slots.
-- If so, blank out the feminine/neuter so we use a table that combines
-- masculine and feminine, or masculine/feminine/neuter.
for _, gender in ipairs({"f", "n"}) do
local other_is_masc = true
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
if not deep_equals(data.forms[case .. "_" .. num .. "_" .. gender],
data.forms[case .. "_" .. num .. "_m"]) then
other_is_masc = false
break
end
end
if not other_is_masc then
break
end
end
if other_is_masc then
for _, case in ipairs(cases) do
for _, num in ipairs(nums) do
data.forms[case .. "_" .. num .. "_" .. gender] = nil
end
end
end
end
-- No accel forms or red link checking if generate_type == "bare".
if generate_type == "bare" then
return
end
-- Compute the lemma for accelerators. Do this after processing
-- overrides in case we overrode the lemma form(s).
local accel_lemma, accel_lemma_f
if data.num and data.num ~= "" then
accel_lemma = data.forms["nom_" .. data.num .. "_m"]
accel_lemma_f = data.forms["nom_" .. data.num .. "_f"]
else
accel_lemma = data.forms["nom_sg_m"]
accel_lemma_f = data.forms["nom_sg_f"]
end
if type(accel_lemma) == "table" then
accel_lemma = accel_lemma[1]
end
if type(accel_lemma_f) == "table" then
accel_lemma_f = accel_lemma_f[1]
end
-- Set the accelerators, and determine if there are red links.
for slot in iter_adj_slots() do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" and #val > 0 then
for _, form in ipairs(val) do
local accel_form = slot
accel_form = accel_form:gsub("_([sp])[gl]_", "|%1|")
if data.noneut then
-- If noneut=1, we're being asked to do a noun like
-- Aquītānus or Rōmānus that has masculine and feminine
-- variants, not an adjective. In that case, make the
-- accelerators correspond to nominal case/number forms
-- without the gender, and use the feminine as the
-- lemma for feminine forms.
if slot:find("_f", nil, true) then
data.accel[slot] = {form = accel_form:gsub("|f$", ""), lemma = accel_lemma_f}
else
data.accel[slot] = {form = accel_form:gsub("|m$", ""), lemma = accel_lemma}
end
else
if not data.forms.nom_sg_n and not data.forms.nom_pl_n then
-- use multipart tags if called for
accel_form = accel_form:gsub("|m$", "|m//f//n")
elseif not data.forms.nom_sg_f and not data.forms.nom_pl_f then
accel_form = accel_form:gsub("|m$", "|m//f")
end
-- use the order nom|m|s, which is more standard than nom|s|m
accel_form = accel_form:gsub("|(.-)|(.-)$", "|%2|%1")
data.accel[slot] = {form = accel_form, lemma = accel_lemma}
end
if not redlink and namespace == "" then
local title = ((lang or get_lang()):makeEntryName(form))
local t = mw.title.new(title)
if t and not t.exists then
insert(data.categories, "Latin " .. data.pos .. " with red links in their inflection tables")
redlink = true
end
end
end
end
end
end
-- Convert data.forms[slot] for all slots into displayable text. This is
-- an older function, still currently used for nouns but not for adjectives.
-- For adjectives, the adjective table module has special code to combine
-- adjacent slots, and needs the original forms plus other text that will
-- go into the displayable text for the slot; this is handled below by
-- partial_show_forms() and finish_show_form().
local function show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if val and val ~= "" and val ~= "—" then
for i, form in ipairs(val) do
local link = link_if_attested(form, data.accel[slot], data.unattested[slot])
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
val[i] = link .. '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>'
else
val[i] = link
end
end
-- FIXME, do we want this difference?
data.forms[slot] = concat(val, is_adj and ", " or "<br />")
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Generate the display form for a set of slots with identical content. We
-- verify that the slots are actually identical, and throw an assertion error
-- if not. The display form is as in show_forms() but combines together all the
-- accelerator forms for all the slots.
local function finish_show_form(data, slots, is_adj)
assert(#slots > 0)
local slot1 = slots[1]
local forms = data.forms[slot1]
local notetext = data.notetext[slot1]
for _, slot in ipairs(slots) do
if not deep_equals(data.forms[slot], forms) then
error("data.forms[" .. slot1 .. "] = " .. (concat_forms_in_slot(forms) or "nil") ..
", but data.forms[" .. slot .. "] = " .. (concat_forms_in_slot(data.forms[slot]) or "nil"))
end
assert(deep_equals(data.notetext[slot], notetext))
end
if not forms then
return "—"
else
local accel_forms = {}
local accel_lemma = data.accel[slot1].lemma
for _, slot in ipairs(slots) do
assert(data.accel[slot].lemma == accel_lemma)
insert(accel_forms, data.accel[slot].form)
end
local combined_accel_form = concat(accel_forms, "|;|")
local accel = {form = combined_accel_form, lemma = accel_lemma}
local formtext = {}
for i, form in ipairs(forms) do
insert(formtext, link_if_attested(form, accel, data.unattested[slot1]) .. notetext[i])
end
-- FIXME, do we want this difference?
return concat(formtext, is_adj and ", " or "<br />")
end
end
-- Used by the adjective table module. This does some of the work of
-- show_forms(); in particular, it converts all empty forms of any format
-- (nil, "", "—") to nil and, if the forms aren't empty, generates the footnote
-- text associated with each form.
local function partial_show_forms(data, is_adj)
local noteindex = 1
local notes = {}
local seen_notes = {}
data.notetext = {}
-- Store this function in DATA so that it can be called from the adjective
-- table module without needing to require this module, which will (or
-- could) lead to recursive module requiring.
data.finish_show_form = finish_show_form
for slot in iter_slots(is_adj) do
local val = data.forms[slot]
if not val or val == "" or val == "—" then
data.forms[slot] = nil
else
local notetext = {}
for i in ipairs(val) do
local this_notes = data.notes[slot .. i]
if this_notes and not data.user_specified[slot] then
if type(this_notes) == "string" then
this_notes = {this_notes}
end
local link_indices = {}
for _, this_note in ipairs(this_notes) do
local this_noteindex = seen_notes[this_note]
if not this_noteindex then
-- Generate a footnote index.
this_noteindex = noteindex
noteindex = noteindex + 1
insert(notes, '<sup style="color: var(--wikt-palette-red,red)">' .. this_noteindex .. '</sup>' .. this_note)
seen_notes[this_note] = this_noteindex
end
insert_if_not(link_indices, this_noteindex)
end
insert(notetext, '<sup style="color: var(--wikt-palette-red,red)">' .. concat(link_indices, ",") .. '</sup>')
else
insert(notetext, "")
end
end
data.notetext[slot] = notetext
end
end
for _, footnote in ipairs(data.footnotes) do
insert(notes, footnote)
end
data.footnotes = concat(notes, "<br />")
end
-- Given an ending (or possibly a full regex matching the entire lemma, if
-- a regex group is present), return the base minus the ending, or nil if
-- the ending doesn't match.
local function extract_base(lemma, ending)
if ending:find("(", nil, true) then
return umatch(lemma, ending)
end
return umatch(lemma, "^(.*)" .. ending .. "$")
end
-- Given ENDINGS_AND_SUBTYPES (a list of pairs of endings with associated
-- subtypes, where each pair consists of a single ending spec and a list of
-- subtypes), check each ending in turn against LEMMA. If it matches, return
-- the pair BASE, STEM2, SUBTYPES where BASE is the remainder of LEMMA minus
-- the ending, STEM2 is as passed in, and SUBTYPES is the subtypes associated
-- with the ending. But don't return SUBTYPES if any of the subtypes in the
-- list is specifically canceled in SPECIFIED_SUBTYPES (a set, i.e. a table
-- where the keys are strings and the value is always true); instead, consider
-- the next ending in turn. If no endings match, throw an error if DECLTYPE is
-- non-nil, mentioning the DECLTYPE (the user-specified declension); but if
-- DECLTYPE is nil, just return nil, nil, nil.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
local function get_noun_subtype_by_ending(lemma, stem2, decltype, specified_subtypes,
endings_and_subtypes)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local subtypes = ending_and_subtypes[2]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
-- In addition, M or F as a subtype is canceled by N, and
-- vice-versa, but M doesn't cancel F or vice-versa; instead,
-- we simply ignore the conflicting gender specification when
-- constructing the combination of specified and inferred subtypes.
-- The reason for this is that neuters have distinct declensions
-- from masculines and feminines, but masculines and feminines have
-- the same declension, and various nouns in Latin that are
-- normally masculine are exceptionally feminine and vice-versa
-- (nauta, agricola, fraxinus, malus "apple tree", manus, rēs,
-- etc.).
--
-- In addition, sg as a subtype is canceled by pl and vice-versa.
-- It's also possible to specify both, which will override sg but
-- not cancel it (in the sense that it won't prevent the relevant
-- rule from matching). For example, there's a rule specifying that
-- lemmas beginning with a capital letter and ending in -ius take
-- the ius.voci.sg subtypes. Specifying such a lemma with the
-- subtype both will result in the ius.voci.both subtypes, whereas
-- specifying such a lemma with the subtype pl will cause this rule
-- not to match, and it will fall through to a less specific rule
-- that returns just the ius subtype, which will be combined with
-- the explicitly specified pl subtype to produce ius.pl.
if specified_subtypes["-" .. subtype] or
subtype == "N" and (specified_subtypes.M or specified_subtypes.F) or
(subtype == "M" or subtype == "F") and specified_subtypes.N or
subtype == "sg" and specified_subtypes.pl or
subtype == "pl" and specified_subtypes.sg then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
local base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending == stem2 then
return base, stem2, subtypes
end
else
local base = extract_base(lemma, ending)
if base then
return base, stem2, subtypes
end
end
end
end
if decltype then
error("Unrecognized ending for declension-" .. decltype .. " noun: " .. lemma)
end
return nil, nil, nil
end
-- Autodetect the subtype of a noun given all the information specified by the
-- user: lemma, stem2, declension type and specified subtypes. Three values are
-- returned: the lemma base (i.e. the stem of the lemma, as required by the
-- declension functions), the new stem2 and the autodetected subtypes. Note
-- that this will not detect a given subtype if the explicitly specified
-- subtypes are incompatible (i.e. if -SUBTYPE is specified for any subtype
-- that would be returned; or if M or F is specified when N would be returned,
-- and vice-versa; or if pl is specified when sg would be returned, and
-- vice-versa).
--
-- NOTE: This function has intimate knowledge of the way that the declension
-- functions handle subtypes, particularly for the third declension.
local function detect_noun_subtype(lemma, stem2, typ, subtypes)
local base, _
if typ == "1" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ām", {"F", "am"}},
{"ās", {"M", "Greek", "Ma"}},
{"ēs", {"M", "Greek", "Me"}},
{"ē", {"F", "Greek"}},
{"ae", {"F", "pl"}},
{"a", {"F"}},
})
elseif typ == "2" then
local detected_subtypes
lemma, stem2, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(.*r)$", {"M", "er"}},
{"^(.*v)os$", {"M", "vos"}},
{"^(.*v)om$", {"N", "vom"}},
-- If the lemma ends in -os and the user said N or -M, then the
-- following won't apply, and the second (neuter) -os will applly.
{"os", {"M", "Greek"}},
{"os", {"N", "Greek", "us"}},
{"on", {"N", "Greek"}},
-- -ius beginning with a capital letter is assumed a proper name,
-- and takes the voci subtype (vocative in -ī) along with the ius
-- subtype and sg-only. Other nouns in -ius just take the ius
-- subtype. Explicitly specify "sg" so that if .pl is given,
-- this rule won't apply.
{"^(%u.*)ius$", {"M", "ius", "voci", "sg"}},
{"ius", {"M", "ius"}},
{"ium", {"N", "ium"}},
-- If the lemma ends in -us and the user said N or -M, then the
-- following won't apply, and the second (neuter) -us will applly.
{"us", {"M"}},
{"us", {"N", "us"}},
{"um", {"N"}},
{"iī", {"M", "ius", "pl"}},
{"ia", {"N", "ium", "pl"}},
-- If the lemma ends in -ī and the user said N or -M, then the
-- following won't apply, and the second (neuter) -ī will applly.
{"ī", {"M", "pl"}},
{"ī", {"N", "us", "pl"}},
{"oe", {"M", "Greek", "pl"}},
{"a", {"N", "pl"}},
})
stem2 = stem2 or lemma
return lemma, stem2, detected_subtypes
elseif typ == "3" then
if subtypes.pl then
if subtypes.Greek then
base = lemma:match("^(.*)erēs$")
if base then
return base .. "ēr", base .. "er", {"er"}
end
base = lemma:match("^(.*)ontēs$")
if base then
return base .. "ōn", base .. "ont", {"on"}
end
base = lemma:match("^(.*)es$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural Greek noun: " .. lemma)
end
base = lemma:match("^(.*)ia$")
if base then
return base, stem2 or base, {"N", "I", "pure"}
end
base = lemma:match("^(.*)a$")
if base then
return base, stem2 or base, {"N"}
end
base = lemma:match("^(.*)ēs$")
if base then
return base, stem2 or base, {}
end
error("Unrecognized ending for declension-3 plural noun: " .. lemma)
end
stem2 = stem2 or make_stem2(lemma)
local detected_subtypes
if subtypes.Greek then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"is", ""}, {"I"}},
{"ēr", {"er"}},
{"ōn", {"on"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
end
if not subtypes.N then
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"^(%u.*pol)is$", ""}, {"F", "polis", "sg", "loc"}},
{{"tūdō", "tūdin"}, {"F"}},
{{"tās", "tāt"}, {"F"}},
{{"tūs", "tūt"}, {"F"}},
{{"tiō", "tiōn"}, {"F"}},
{{"siō", "siōn"}, {"F"}},
{{"xiō", "xiōn"}, {"F"}},
{{"gō", "gin"}, {"F"}},
{{"or", "ōr"}, {"M"}},
{{"tr[iī]x", "trīc"}, {"F"}},
{{"is", ""}, {"I"}},
{{"^(%l.*)ēs$", ""}, {"I"}},
})
if base then
return lemma, stem2, detected_subtypes
end
end
base, _, detected_subtypes = get_noun_subtype_by_ending(lemma, stem2, nil, subtypes, {
{{"us", "or"}, {"N"}},
{{"us", "er"}, {"N"}},
{{"ma", "mat"}, {"N"}},
{{"men", "min"}, {"N"}},
{{"^(%u.*)e$", ""}, {"N", "sg"}},
{{"e", ""}, {"N", "I", "pure"}},
{{"al", "āl"}, {"N", "I", "pure"}},
{{"ar", "ār"}, {"N", "I", "pure"}},
})
if base then
return lemma, stem2, detected_subtypes
end
return lemma, stem2, {}
elseif typ == "4" then
if subtypes.echo or subtypes.Callisto then
base = lemma:match("^(.*)ō$")
if not base then
error("Declension-4 noun of subtype .echo or .Callisto should end in -ō: " .. lemma)
end
if subtypes.Callisto then
return base, nil, {"F", "sg"}
else
return base, nil, {"F"}
end
end
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", {"M"}},
{"ū̆", {"N"}},
{"ūs", {"M", "pl"}},
{"ua", {"N", "pl"}},
})
elseif typ == "5" then
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"iēs", {"F", "i"}},
{"iēs", {"F", "i", "pl"}},
{"ēs", {"F"}},
{"ēs", {"F", "pl"}},
})
elseif typ == "sgpl" then
return lemma, stem2, {}
elseif typ == "irreg" and lemma == "domus" then
-- [[domus]] auto-sets data.loc = true, but we need to know this
-- before declining the noun so we can propagate it to other segments.
return lemma, nil, {"loc"}
elseif typ == "indecl" or typ == "irreg" and (
lemma == "Deus" or umatch(lemma, "^[IJ]ēs[uū]s$") or
lemma == "Athōs" or lemma == "vēnum"
) then
-- Indeclinable nouns, and certain irregular nouns, set data.num = "sg",
-- but we need to know this before declining the noun so we can
-- propagate it to other segments.
return get_noun_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", {"both"}},
{"", {"sg"}},
{"", {"pl"}},
})
else
return lemma, nil, {}
end
end
-- Given ENDINGS_AND_SUBTYPES (a list of four-tuples of ENDING, RETTYPE,
-- SUBTYPES, PROCESS_RETVAL), check each ENDING in turn against LEMMA and
-- STEM2. If it matches, return a four-tuple BASE, STEM2, RETTYPE, NEW_SUBTYPES
-- where BASE is normally the remainder of LEMMA minus the ending, STEM2 is
-- as passed in, RETTYPE is as passed in, and NEW_SUBTYPES is the same as
-- SUBTYPES minus any subtypes beginning with a hyphen. If no endings match,
-- throw an error if DECLTYPPE is non-nil, mentioning the DECLTYPE
-- (user-specified declension); but if DECLTYPE is nil, just return the tuple
-- nil, nil, nil, nil.
--
-- In order for a given entry to match, ENDING must match and also the subtypes
-- in SUBTYPES (a list) must not be incompatible with the passed-in
-- user-specified subtypes SPECIFIED_SUBTYPES (a set, i.e. a table where the
-- keys are strings and the value is always true). "Incompatible" means that
-- a given SUBTYPE is specified in either one and -SUBTYPE in the other, or
-- that "pl" is found in SPECIFIED_SUBTYPES and not in SUBTYPES.
--
-- The ending spec in ENDINGS_AND_SUBTYPES is one of the following:
--
-- 1. A simple string, e.g. "tūdō", specifying an ending.
-- 2. A regex that should match the entire lemma (it should be anchored at
-- the beginning with ^ and at the end with $), and contains a single
-- capturing group to match the base.
-- 3. A pair {SIMPLE_STRING_OR_REGEX, STEM2_ENDING} where
-- SIMPLE_STRING_OR_REGEX is one of the previous two possibilities and
-- STEM2_ENDING is a string specifying the corresponding ending that must
-- be present in STEM2. If this form is used, the combination of
-- base + STEM2_ENDING must exactly match STEM2 in order for this entry
-- to be considered a match. An example is {"is", ""}, which will match
-- lemma == "follis", stem2 == "foll", but not lemma == "lapis",
-- stem2 == "lapid".
--
-- If PROCESS_STEM2 is given and the returned STEM2 would be nil, call
-- process_stem2(BASE) to get the STEM2 to return.
local function get_adj_type_and_subtype_by_ending(lemma, stem2, decltype,
specified_subtypes, endings_and_subtypes, process_stem2)
for _, ending_and_subtypes in ipairs(endings_and_subtypes) do
local ending = ending_and_subtypes[1]
local rettype = ending_and_subtypes[2]
local subtypes = ending_and_subtypes[3]
local process_retval = ending_and_subtypes[4]
local not_this_subtype = false
if (
specified_subtypes.pl and not contains(subtypes, "pl") or
contains(subtypes, "both") and not specified_subtypes.both
) then
-- We now require that plurale tantum terms specify a plural-form lemma.
-- The autodetected subtypes will include 'pl' for such lemmas; if not,
-- we fail this entry. Additionally, if the rule contains 'both', it
-- must be explicitly specified to match.
not_this_subtype = true
else
for _, subtype in ipairs(subtypes) do
-- A subtype is directly canceled by specifying -SUBTYPE.
if specified_subtypes["-" .. subtype] then
not_this_subtype = true
break
end
-- A subtype is canceled if the user specified SUBTYPE and
-- -SUBTYPE is given in the to-be-returned subtypes.
local must_not_be_present = subtype:match("^%-(.*)$")
if must_not_be_present and specified_subtypes[must_not_be_present] then
not_this_subtype = true
break
end
end
end
if not not_this_subtype then
local base
if type(ending) == "table" then
local lemma_ending = ending[1]
local stem2_ending = ending[2]
base = extract_base(lemma, lemma_ending)
if base and base .. stem2_ending ~= stem2 then
base = nil
end
else
base = extract_base(lemma, ending)
end
if base then
-- Remove subtypes of the form -SUBTYPE from the subtypes
-- to be returned.
local new_subtypes = {}
for _, subtype in ipairs(subtypes) do
if subtype:sub(1, 1) ~= "-" then
insert(new_subtypes, subtype)
end
end
if process_retval then
base, stem2 = process_retval(base, stem2)
end
if process_stem2 then
stem2 = stem2 or process_stem2(base)
end
return base, stem2, rettype, new_subtypes
end
end
end
if not decltype then
return nil, nil, nil, nil
elseif decltype == "" then
error("Unrecognized ending for adjective: " .. lemma)
else
error("Unrecognized ending for declension-" .. decltype .. " adjective: " .. lemma)
end
end
-- Autodetect the type and subtype of an adjective given all the information
-- specified by the user: lemma, stem2, declension type and specified subtypes.
-- Four values are returned: the lemma base (i.e. the stem of the lemma, as
-- required by the declension functions), the value of stem2 to pass to the
-- declension function, the declension type and the autodetected subtypes.
-- Note that this will not detect a given subtype if -SUBTYPE is specified for
-- any subtype that would be returned, or if SUBTYPE is specified and -SUBTYPE
-- is among the subtypes that would be returned (such subtypes are filtered out
-- of the returned subtypes).
local function detect_adj_type_and_subtype(lemma, stem2, typ, subtypes)
local function base_as_stem2(base, stem2)
return base, base
end
local function constant_base(baseval)
return function(base, stem2)
return baseval, nil
end
end
local function decl12_stem2(base)
return base
end
local function decl3_stem2(base)
return make_stem2(base)
end
local decl12_entries = {
{"us", "1&2+", {}},
{"a", "1&2+", {}},
{"um", "1&2+", {}},
{"ī", "1&2+", {"pl"}},
{"ae", "1&2+", {"pl"}},
{"a", "1&2+", {"pl"}},
-- Nearly all -os adjectives are greekA
{"os", "1&2+", {"greekA", "-greekE"}},
{"os", "1&2+", {"greekE", "-greekA"}},
{"ē", "1&2+", {"greekE", "-greekA"}},
{"on", "1&2+", {"greekA", "-greekE"}},
{"on", "1&2+", {"greekE", "-greekA"}},
{"^(.*er)$", "1&2+", {"er"}},
{"^(.*ur)$", "1&2+", {"er"}},
{"^(h)ic$", "1&2+", {"ic"}},
}
local decl3_entries = {
{"^(.*er)$", "3-3+", {"I"}},
{"^(.*er)$", "3-3+", {}},
{"is", "3-2+", {"I"}},
{"is", "3-2+", {}},
{"e", "3-2+", {"I"}},
{"e", "3-2+", {}},
{"^(.*[ij])or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(.*[ij])or$", "3-C+", {}},
{"^(min)or$", "3-C+", {"abl_e_i", "-abl_i_e"}},
{"^(min)or$", "3-C+", {}},
{"^(.*trīx)$", "3-def+", {"trix", "abl_i_e", "-abl_e_i"}},
{"^(.*trīx)$", "3-def+", {"trix"}},
{"^(.*tor)$", "3-def+", {"tor", "abl_i_e", "-abl_e_i"}},
{"^(.*tor)$", "3-def+", {"tor"}},
-- Detect -ēs as 3-1 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like quadripēs, volucripēs).
-- Essentially, for declension-3 adjectives, we require that
-- .pl is given if the lemma is plural.
--
-- Most 3-1 adjectives are i-stem (e.g. audāx) so we require -I
-- to be given with non-i-stem adjectives. The first entry below
-- will apply when -I isn't given, the second when it is given.
{"^(.*ēs)$", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"I"}},
{"^(.*ēs)$", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"^(.*ēs)$", "3-1+", {"par"}},
{"^(.*[ij])ōrēs$", "3-C+", {"pl"}},
{"^(min)ōrēs$", "3-C+", {"pl"}},
-- If .pl with -ēs, we don't know if the adjective is 3-1, 3-2
-- or 3-3. Since 3-2 is probably the most common, we infer it
-- (as well as the fact that these adjectives *are* in a sense
-- 3-2 since they have a distinct neuter in -(i)a. Note that
-- we have two entries here; the first one will apply unless
-- -I is given, and will infer an i-stem adjective; the second
-- one will apply otherwise (and infer a non-i-stem 3-1 adjective).
{"ēs", "3-2+", {"pl", "I"}, base_as_stem2},
{"ēs", "3-1+", {"pl", "par"}, base_as_stem2},
-- Same for neuters.
{"ia", "3-2+", {"pl", "I"}, base_as_stem2},
{"a", "3-1+", {"pl", "par"}, base_as_stem2},
-- As above for -ēs but for miscellaneous singulars.
{"", "3-1+", {"I", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"I"}},
{"", "3-1+", {"par", "abl_i_e", "-abl_e_i"}},
{"", "3-1+", {"par"}},
}
local function get_subtypes_and_endings(typ, f)
local subtypes_and_endings = {}
for _, v in ipairs(decl3_entries) do
if f(v) then
v = deep_copy(v)
v[2] = typ
insert(subtypes_and_endings, v)
end
end
return subtypes_and_endings
end
local function compare(spec)
return spec[2] == typ
end
if typ == "+" then
local base, new_stem2, rettype, new_subtypes = get_adj_type_and_subtype_by_ending(lemma, stem2, nil, subtypes, decl12_entries, decl12_stem2)
if base then
return base, new_stem2, rettype, new_subtypes
else
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
end
elseif typ == "3+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl3_entries, decl3_stem2)
elseif typ == "1&2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, decl12_entries, decl12_stem2)
elseif typ == "1-1+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"a", typ, {}},
{"ae", typ, {"pl"}},
})
elseif typ == "2-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"us", typ, {}},
{"um", typ, {}},
{"ī", typ, {"pl"}},
{"a", typ, {"pl"}},
{"os", typ, {"greek"}},
{"on", typ, {"greek"}},
{"oe", typ, {"greek", "pl"}},
})
elseif typ == "3-1+" then
-- Use the specs from `decl3_entries`, but add the two i-stem 3-2+ specs when plural-only, as they're the same when 3-1+.
local subtypes_and_endings = get_subtypes_and_endings(typ, function(spec)
local spec1, spec2 = spec[1], spec[2]
return spec2 == typ or (spec2 == "3-2+" and spec1 == "ēs" or spec1 == "ia")
end)
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-def+" then
-- Like 3-1+ but defective in gender
local subtypes_and_endings = get_subtypes_and_endings(typ, compare)
insert(subtypes_and_endings, {"", typ, {"I"}})
insert(subtypes_and_endings, {"", typ, {"par"}})
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, subtypes_and_endings, decl3_stem2)
elseif typ == "3-2+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"is", typ, {}},
{"e", typ, {}},
-- Detect -ēs as 3-2 without auto-inferring .pl if .pl
-- not specified. If we don't do this, the later entry for
-- -ēs will auto-infer .pl whenever -ēs is specified (which
-- won't work for adjectives like isoscelēs). Essentially,
-- for declension-3 adjectives, we require that .pl is given
-- if the lemma is plural.
{"ēs", typ, {}},
{"ēs", typ, {"pl"}},
{"ia", typ, {"pl"}},
}, decl3_stem2)
elseif typ == "3-3+" or typ == "3-P+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"ēs", typ, {"pl"}, base_as_stem2},
{"ia", typ, {"pl"}, base_as_stem2},
{"", typ, {}},
}, decl3_stem2)
elseif typ == "3-C+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, get_subtypes_and_endings(typ, compare), decl3_stem2)
elseif typ == "irreg+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"^(duo)$", typ, {"pl"}},
{"^(ambō)$", typ, {"pl"}},
{"^(mīll?ia)$", typ, {"N", "pl"}, constant_base("mīlle")},
-- match ea
{"^(ea)$", typ, {}, constant_base("is")},
-- match id
{"^(id)$", typ, {}, constant_base("is")},
-- match plural eī, iī
{"^([ei]ī)$", typ, {"pl"}, constant_base("is")},
-- match plural ea, eae
{"^(eae?)$", typ, {"pl"}, constant_base("is")},
-- match eadem
{"^(eadem)$", typ, {}, constant_base("īdem")},
-- match īdem, idem
{"^([īi]dem)$", typ, {}, constant_base("īdem")},
-- match plural īdem
{"^(īdem)$", typ, {"pl"}},
-- match plural eadem, eaedem
{"^(eae?dem)$", typ, {"pl"}, constant_base("īdem")},
-- match illa, ipsa, ista; it doesn't matter if we overmatch because
-- we'll get an error as we use the stem itself in the returned base
{"^(i[lps][lst])a$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match illud, istud; as above, it doesn't matter if we overmatch
{"^(i[ls][lt])ud$", typ, {}, function(base, stem2) return base .. "e", nil end},
-- match ipsum
{"^(ipsum)$", typ, {}, constant_base("ipse")},
-- match plural illī, ipsī, istī; as above, it doesn't matter if we
-- overmatch
{"^(i[lps][lst])ī$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- match plural illa, illae, ipsa, ipsae, ista, istae; as above, it
-- doesn't matter if we overmatch
{"^(i[lps][lst])ae?$", typ, {"pl"}, function(base, stem2) return base .. "e", nil end},
-- Detect quī as non-plural unless .pl specified.
{"^(quī)$", typ, {}},
-- Otherwise detect quī as plural.
{"^(quī)$", typ, {"pl"}},
-- Same for quae.
{"^(quae)$", typ, {}, constant_base("quī")},
{"^(quae)$", typ, {"pl"}, constant_base("quī")},
{"^(quid)$", typ, {}, constant_base("quis")},
{"^(quod)$", typ, {}, constant_base("quī")},
{"^(qui[cd]quid)$", typ, {}, constant_base("quisquis")},
{"^(quīquī)$", typ, {"pl"}, constant_base("quisquis")},
{"^(quaequae)$", typ, {"pl"}, constant_base("quisquis")},
-- match all remaining lemmas in lemma form
{"", typ, {}},
})
elseif typ == "indecl+" then
return get_adj_type_and_subtype_by_ending(lemma, stem2, typ, subtypes, {
{"", typ, {"both"}},
{"", typ, {"sg"}},
{"", typ, {"pl"}},
})
else -- 0+
return lemma, nil, typ, {}
end
end
-- Parse a segment (e.g. "lūna<1>", "aegis/aegid<3.Greek>", "bōs<irreg.F>",
-- bonus<+>", or "[[vetus]]/veter<3+.-I>"), consisting of a lemma (or optionally
-- a lemma/stem) and declension+subtypes, where a + in the declension indicates
-- an adjective. Brackets can be present to indicate links, for use in
-- {{la-noun}} and {{la-adj}}. The return value is a table, e.g.:
-- {
-- decl = "1",
-- is_adj = false,
-- orig_lemma = "lūna",
-- lemma = "lūna",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"lūn"}
-- }
--
-- or
--
-- {
-- decl = "3",
-- is_adj = false,
-- orig_lemma = "aegis",
-- lemma = "aegis",
-- stem2 = "aegid",
-- gender = nil,
-- types = {["Greek"] = true},
-- args = {"aegis", "aegid"}
-- }
--
-- or
--
-- {
-- decl = "irreg",
-- is_adj = false,
-- orig_lemma = "bōs",
-- lemma = "bōs",
-- stem2 = nil,
-- gender = "F",
-- types = {["F"] = true},
-- args = {"bōs"}
-- }
-- or
--
-- {
-- decl = "1&2+",
-- is_adj = true,
-- orig_lemma = "bonus",
-- lemma = "bonus",
-- stem2 = nil,
-- gender = nil,
-- types = {},
-- args = {"bon"}
-- }
--
-- or
--
-- {
-- decl = "3-1+",
-- is_adj = true,
-- orig_lemma = "[[vetus]]",
-- lemma = "vetus",
-- stem2 = "veter",
-- gender = nil,
-- types = {},
-- args = {"vetus", "veter"}
-- }
local function parse_segment(segment)
local stem_part, spec_part = segment:match("^(.*)<(.-)>$")
local stems = split(stem_part, "/", true, true)
local specs = split(spec_part, ".", true, true)
local types = {}
local num = nil
local loc = false
local args = {}
local decl
for j, spec in ipairs(specs) do
if j == 1 then
decl = spec
else
local begins_with_hyphen
begins_with_hyphen, spec = spec:match("^(%-?)(.*)$")
spec = begins_with_hyphen .. spec:gsub("%-", "_")
types[spec] = true
end
end
local orig_lemma = stems[1]
if not orig_lemma or orig_lemma == "" then
orig_lemma = pagename or get_pagename()
end
local lemma = remove_links(orig_lemma)
local stem2 = stems[2]
if stem2 == "" then
stem2 = nil
end
if #stems > 2 then
error("Too many stems, at most 2 should be given: " .. stem_part)
end
local base, detected_subtypes
local is_adj = false
local gender = nil
if decl:find("+", nil, true) then
base, stem2, decl, detected_subtypes = detect_adj_type_and_subtype(lemma, stem2, decl, types)
is_adj = true
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
else
types[subtype] = true
end
end
else
base, stem2, detected_subtypes = detect_noun_subtype(lemma, stem2, decl, types)
for _, subtype in ipairs(detected_subtypes) do
if types["-" .. subtype] then
-- if a "cancel subtype" spec is given, remove the cancel spec
-- and don't apply the subtype
types["-" .. subtype] = nil
elseif (subtype == "M" or subtype == "F" or subtype == "N") and
(types.M or types.F or types.N) then
-- if gender already specified, don't create conflicting gender spec
elseif (subtype == "sg" or subtype == "pl" or subtype == "both") and
(types.sg or types.pl or types.both) then
-- if number restriction already specified, don't create conflicting
-- number restriction spec
else
types[subtype] = true
end
end
if not types.pl and not types.both and umatch(lemma, "^%u") then
types.sg = true
end
end
if types.loc then
loc = true
types.loc = nil
end
if types.M then
gender = "M"
elseif types.F then
gender = "F"
elseif types.N then
gender = "N"
end
if types.pl then
num = "pl"
types.pl = nil
elseif types.sg then
num = "sg"
types.sg = nil
end
args[1] = base
args[2] = stem2
return {
decl = decl,
is_adj = is_adj,
gender = gender,
orig_lemma = orig_lemma,
lemma = lemma,
stem2 = stem2,
types = types,
num = num,
loc = loc,
args = args,
}
end
-- Parse a segment run (i.e. a string with zero or more segments [see
-- parse_segment] and optional surrounding text, e.g. "foenum<2>-graecum<2>"
-- or "[[pars]]/part<3.abl-e-occ-i> [[oratio|ōrātiōnis]]"). The segment run
-- currently cannot contain any alternants (e.g. "((epulum<2.sg>,epulae<1>))").
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments
-- has a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of per-word properties, where each element is an
-- object {
-- decl = DECL (declension),
-- types = TYPES (set describing the subtypes of a given word),
-- }
-- }
-- Each element in PARSED_SEGMENTS is as returned by parse_segment() but will
-- have an additional .orig_prefix field indicating the text before the segment
-- (including bracketed links) and corresponding .prefix field indicating the text
-- with bracketed links resolved. If there is trailing text, the last element will
-- have only .orig_prefix and .prefix fields containing that trailing text.
local function parse_segment_run(segment_run)
local loc = nil
local num = nil
local is_adj = nil
-- If the segment run begins with a hyphen, include the hyphen in the
-- set of allowed characters for a declined segment. This way, e.g. the
-- suffix [[-cen]] can be declared as {{la-ndecl|-cen/-cin<3>}} rather than
-- {{la-ndecl|-cen/cin<3>}}, which is less intuitive.
local is_suffix = segment_run:sub(1, 1) == "-"
local segments = {}
local propses = {}
-- We want to not break up a bracketed link followed by <> even if it has a space or
-- hyphen in it. So we do an outer capturing split to find the bracketed links followed
-- by <>, then do inner capturing splits on all the remaining text to find the other
-- declined terms.
local bracketed_segments = split(segment_run, "(%[%[[^%[%]]-%]%]<.->)")
for i, bracketed_segment in ipairs(bracketed_segments) do
if i % 2 == 0 then
insert(segments, bracketed_segment)
else
for _, subsegment in ipairs(split(bracketed_segment, is_suffix and "([^<> ,]+<.->)" or "([^<> ,%-]+<.->)")) do
insert(segments, subsegment)
end
end
end
local parsed_segments = {}
local gender = nil
for i = 2, (#segments - 1), 2 do
local parsed_segment = parse_segment(segments[i])
-- Overall locative is true if any segments call for locative.
loc = loc or parsed_segment.loc
-- The first specified value for num is used becomes the overall value.
num = num or parsed_segment.num
if is_adj == nil then
is_adj = parsed_segment.is_adj
else
is_adj = is_adj and parsed_segment.is_adj
end
gender = gender or parsed_segment.gender
parsed_segment.orig_prefix = segments[i - 1]
parsed_segment.prefix = remove_links(segments[i - 1])
parsed_segment.props = {
decl = parsed_segment.decl,
types = parsed_segment.types,
gender = parsed_segment.gender,
num = parsed_segment.num,
}
insert(parsed_segments, parsed_segment)
insert(propses, parsed_segment.props)
end
if segments[#segments] ~= "" then
insert(parsed_segments, {
orig_prefix = segments[#segments],
prefix = remove_links(segments[#segments]),
})
end
return {
segments = parsed_segments,
loc = loc,
num = num,
is_adj = is_adj,
gender = gender,
propses = propses,
}
end
-- Parse an alternant, e.g. "((epulum<2.sg>,epulae<1>))",
-- "((Serapis<3>,Serapis/Serapid<3>))" or
-- "((rēs<5>pūblica<1>,rēspūblica<1>))". The return value is a table of the form
-- {
-- alternants = PARSED_ALTERNANTS (a list of segment runs, each of which is a
-- list of parsed segments as returned by parse_segment_run()),
-- loc = LOC (a boolean indicating whether any of the individual segment runs
-- has a locative),
-- num = NUM (the overall number restriction, one of "sg", "pl" or "both"),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all non-constant alternants are adjectives, false
-- if all nouns, nil if only constant alternants; conflicting alternants
-- cause an error),
-- propses = PROPSES (list of lists of per-word property objecs),
-- }
local function parse_alternant(alternant)
local parsed_alternants = {}
local alternant_spec = alternant:match("^%(%((.-)%)%)$")
local alternants = split(alternant_spec, ",", true, true)
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i, alternant in ipairs(alternants) do
local parsed_run = parse_segment_run(alternant)
insert(parsed_alternants, parsed_run)
loc = loc or parsed_run.loc
-- First time through, set the overall num to the num of the first run,
-- even if nil. After that, if we ever see a run with a different value
-- of num, set the overall num to "both". That way, if all alternants
-- don't specify a num, we get an unspecified num, but if some do and
-- some don't, we get both, because an unspecified num defaults to
-- both.
if i == 1 then
num = parsed_run.num
elseif num ~= parsed_run.num then
-- FIXME, this needs to be rethought to allow for
-- adjective alternants.
num = "both"
end
gender = gender or parsed_run.gender
if is_adj == nil then
is_adj = parsed_run.is_adj
elseif parsed_run.is_adj ~= nil and parsed_run.is_adj ~= is_adj then
error("Saw both noun and adjective alternants; not allowed")
end
insert(propses, parsed_run.propses)
end
return {
alternants = parsed_alternants,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Parse a segment run (see parse_segment_run()). Unlike for
-- parse_segment_run(), this can contain alternants such as
-- "((epulum<2.sg>,epulae<1>))" or "((Serapis<3.sg>,Serapis/Serapid<3.sg>))"
-- embedded in it to indicate words composed of multiple declensions.
-- The return value is a table of the following form:
-- {
-- segments = PARSED_SEGMENTS (a list of parsed segments),
-- loc = LOC (a boolean indicating whether any of the individual segments has
-- a locative),
-- num = NUM (the first specified value for a number restriction, or nil if
-- no number restrictions),
-- gender = GENDER (the first specified or inferred gender, or nil if none),
-- is_adj = IS_ADJ (true if all segments are adjective segments, false if
-- there's at least one noun segment, nil if only raw-text segments),
-- propses = PROPSES (list of either per-word property objects or lists of
-- lists of such objects),
-- }.
-- Each element in PARSED_SEGMENTS is one of three types:
--
-- 1. A regular segment, as returned by parse_segment() but with additional
-- .prefix and .orig_prefix fields indicating the text before the segment, as per
-- the return value of parse_segment_run().
-- 2. A raw-text segment, i.e. a table with only .prefix and .orig_prefix fields
-- containing the raw text.
-- 3. An alternating segment, as returned by parse_alternant().
-- Note that each alternant is a segment run rather than a single parsed
-- segment to allow for alternants like "((rēs<5>pūblica<1>,rēspūblica<1>))".
-- The parsed segment runs in PARSED_SEGMENT_RUNS are tables as returned by
-- parse_segment_run() (of the same form as the overall return value of
-- parse_segment_run_allowing_alternants()).
local function parse_segment_run_allowing_alternants(segment_run)
if segment_run:find(" ", nil, true) then
track("has-space")
end
if segment_run:find("((", nil, true) then
track("has-alternant")
end
local alternating_segments = split(segment_run, "(%(%(.-%)%))")
local parsed_segments = {}
local loc = false
local num = nil
local gender = nil
local is_adj = nil
local propses = {}
for i = 1, #alternating_segments do
local alternating_segment = alternating_segments[i]
if alternating_segment ~= "" then
local this_is_adj
if i % 2 == 1 then
local parsed_run = parse_segment_run(alternating_segment)
for _, parsed_segment in ipairs(parsed_run.segments) do
insert(parsed_segments, parsed_segment)
end
loc = loc or parsed_run.loc
num = num or parsed_run.num
gender = gender or parsed_run.gender
this_is_adj = parsed_run.is_adj
for _, props in ipairs(parsed_run.propses) do
insert(propses, props)
end
else
local parsed_alternating_segment = parse_alternant(alternating_segment)
insert(parsed_segments, parsed_alternating_segment)
loc = loc or parsed_alternating_segment.loc
num = num or parsed_alternating_segment.num
gender = gender or parsed_alternating_segment.gender
this_is_adj = parsed_alternating_segment.is_adj
insert(propses, parsed_alternating_segment.propses)
end
if is_adj == nil then
is_adj = this_is_adj
elseif this_is_adj ~= nil then
is_adj = is_adj and this_is_adj
end
end
end
if #parsed_segments > 1 then
track("multiple-segments")
end
return {
segments = parsed_segments,
loc = loc,
num = num,
gender = gender,
is_adj = is_adj,
propses = propses,
}
end
-- Combine each form in FORMS (a list of forms associated with a slot) with each
-- form in NEW_FORMS (either a single string for a single form, or a list of
-- forms) by concatenating EXISTING_FORM .. PREFIX .. NEW_FORM. Also combine
-- NOTES (a table specifying the footnotes associated with each existing form,
-- i.e. a map from form indices to lists of footnotes) with NEW_NOTES (new
-- footnotes associated with the new forms, in the same format as NOTES). Return
-- a pair NEW_FORMS, NEW_NOTES where either or both of FORMS and NOTES (but not
-- the sublists in NOTES) may be destructively modified to generate the return
-- values.
local function append_form(forms, notes, new_forms, new_notes, prefix)
if forms == nil then
return
end
new_forms = new_forms or ""
notes = notes or {}
new_notes = new_notes or {}
prefix = prefix or ""
if type(new_forms) == "table" and #new_forms == 1 then
new_forms = new_forms[1]
end
if type(new_forms) == "string" then
-- If there's only one new form, destructively modify the existing
-- forms and notes for this new form and its footnotes.
for i = 1, #forms do
forms[i] = forms[i] .. prefix .. new_forms
if new_notes[1] then
if not notes[i] then
notes[i] = new_notes[1]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[1]) do
insert(combined_notes, note)
end
notes[i] = combined_notes
end
end
end
return forms, notes
else
-- If there are multiple new forms, we need to loop over all
-- combinations of new and old forms. In that case, use new tables
-- for the combined forms and notes.
local ret_forms = {}
local ret_notes = {}
for i=1, #forms do
for j=1, #new_forms do
insert(ret_forms, forms[i] .. prefix .. new_forms[j])
if new_notes[j] then
if not notes[i] then
-- We are constructing a linearized matrix of size
-- NI x NJ where J is in the inner loop. If I and J
-- are zero-based, the linear index of (I, J) is
-- I * NJ + J. However, we are one-based, so the
-- same formula won't work. Instead, we effectively
-- need to convert to zero-based indices, compute
-- the zero-based linear index, and then convert it
-- back to a one-based index, i.e.
--
-- (I - 1) * NJ + (J - 1) + 1
--
-- i.e. (I - 1) * NJ + J.
ret_notes[(i - 1) * #new_forms + j] = new_notes[j]
else
local combined_notes = deep_copy(notes[i])
for _, note in ipairs(new_notes[j]) do
insert(combined_notes, note)
end
ret_notes[(i - 1) * #new_forms + j] = combined_notes
end
end
end
end
return ret_forms, ret_notes
end
end
-- Destructively modify any forms in FORMS (a map from a slot to a form or a
-- list of forms) by converting sequences of ae, oe, Ae or Oe to the
-- appropriate ligatures.
local function apply_ligatures(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
forms[slot] = forms[slot]:gsub("[AaOo]e", ligatures)
elseif type(forms[slot]) == "table" then
for i = 1, #forms[slot] do
forms[slot][i] = forms[slot][i]:gsub("[AaOo]e", ligatures)
end
end
end
end
-- Modify any forms in FORMS (a map from a slot to a form or a list of forms) by
-- converting final m to optional n or m.
local function apply_sufn(forms, is_adj)
for slot in iter_slots(is_adj) do
if type(forms[slot]) == "string" then
if forms[slot]:sub(-1) == "m" then
forms[slot] = {forms[slot]:gsub("m$", "n"), forms[slot]}
end
elseif type(forms[slot]) == "table" then
-- See if there are any final m's.
local final_m
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
final_m = true
break
end
end
if final_m then
local newval = {}
for i = 1, #forms[slot] do
if forms[slot][i]:sub(-1) == "m" then
insert(newval, (forms[slot][i]:gsub("m$", "n")))
end
insert(newval, forms[slot][i])
end
forms[slot] = newval
end
end
end
end
-- If NUM == "sg", copy the singular forms to the plural ones; vice-versa if
-- NUM == "pl". This should allow for the equivalent of plural
-- "alpha and omega" formed from two singular nouns, and for the equivalent of
-- plural "St. Vincent and the Grenadines" formed from a singular noun and a
-- plural noun. (These two examples actually occur in Russian, at least.)
local function propagate_number_restrictions(forms, num, is_adj)
if num == "sg" or num == "pl" then
for slot in iter_slots(is_adj) do
if slot:find(num, nil, true) then
local other_num_slot = num == "sg" and slot:gsub("sg", "pl") or slot:gsub("pl", "sg")
forms[other_num_slot] = type(forms[slot]) == "table" and deep_copy(forms[slot]) or forms[slot]
end
end
end
end
local function join_sentences(sentences, joiner)
-- Lowercase the first letter of all but the first sentence, and remove the
-- final period from all but the last sentence. Then join together with the
-- joiner (e.g. " and " or " or ").
-- FIXME: Should we join three or more as e.g. "foo, bar and baz"?
local sentences_to_join = {}
for i, sentence in ipairs(sentences) do
if i < #sentences then
sentence = sentence:gsub("%.$", "")
end
if i > 1 then
sentence = lcfirst(sentence)
end
insert(sentences_to_join, sentence)
end
return concat(sentences_to_join, joiner)
end
-- Construct the declension of a parsed segment run of the form returned by
-- parse_segment_run() or parse_segment_run_allowing_alternants(). Return value
-- is a table
-- {
-- forms = FORMS (keyed by slot, list of forms for that slot),
-- notes = NOTES (keyed by slot, map from form indices to lists of footnotes),
-- title = TITLE (list of titles for each segment in the run),
-- categories = CATEGORIES (combined categories for all segments),
-- }
local function decline_segment_run(parsed_run, pos, is_adj)
local declensions = {
-- For each possible slot (e.g. "abl_sg"), list of possible forms.
forms = {},
-- Keyed by slot (e.g. "abl_sg"). Value is a table indicating the footnotes
-- corresponding to the forms for that slot. Each such table maps indices
-- (the index of the corresponding form) to a list of one or more
-- footnotes.
notes = {},
title = {},
unattested = {},
subtitleses = {},
orig_titles = {},
categories = {},
footnotes = {},
-- May be set true if declining a 1-1 adjective
loc = false,
noneut = false,
nomf = false,
}
for slot in iter_slots(is_adj) do
declensions.forms[slot] = {""}
end
for i, seg in ipairs(parsed_run.segments) do
local decl = seg.decl
if decl then -- not an alternant, not a constant segment
seg.loc = parsed_run.loc
seg.num = seg.num or parsed_run.num
seg.gender = seg.gender or parsed_run.gender
local data, potential_lemma_slots
if seg.is_adj then
if not (m_adj_decl or get_m_adj_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_adj_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
gender = seg.gender,
loc = seg.loc,
noneut = false,
nomf = false,
pos = is_adj and pos or "คำคุณศัพท์",
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_adj_decl or get_m_adj_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
if data.loc then
declensions.loc = true
end
if data.noneut then
declensions.noneut = true
end
if data.nomf then
declensions.nomf = true
end
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg+" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. english .. " ภาษาละติน|การผันรูปนามกลุ่ม" .. english .. "]]"
elseif apparent_decl == "irreg+" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl+" or apparent_decl == "0+" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize adjective declension " .. apparent_decl)
end
--data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with ''idem'', ''quīdam''
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
else
if not (m_noun_decl or get_m_noun_decl())[decl] then
error("Unrecognized declension '" .. decl .. "'")
end
potential_lemma_slots = potential_noun_lemma_slots
data = {
subtitles = {},
num = seg.num or "",
loc = seg.loc,
pos = pos,
forms = {},
types = seg.types,
unattested = {},
categories = {},
notes = {},
}
(m_noun_decl or get_m_noun_decl())[decl](data, seg.args)
local apparent_decl = data.decl or decl
seg.props.headword_decl = apparent_decl
-- Construct title out of "original title" and subtitles.
if not data.title then
if decl == "irreg" and apparent_decl ~= decl and #data.subtitles == 0 then
insert(data.subtitles, glossary_link("ไม่ปรกติ"))
end
if declension_to_english[apparent_decl] then
local english = declension_to_english[apparent_decl]
data.title = "[[ภาคผนวก:การผันรูปนามกลุ่ม" .. english .. " ภาษาละติน|การผันรูปนามกลุ่ม" .. english .. "]]"
elseif apparent_decl == "irreg" then
data.title = glossary_link("ไม่ปรกติ")
elseif apparent_decl == "indecl" or apparent_decl == "0" or apparent_decl == "sgpl" then
data.title = glossary_link("ผันรูปไม่ได้")
else
error("Internal error! Don't recognize noun declension " .. apparent_decl)
end
--data.title = data.title .. " " .. singularize(data.pos)
end
if data.types.sufn then
insert(data.subtitles, {"with", " ''m'' optionally → ''n'' in compounds"})
elseif data.types.not_sufn then
insert(data.subtitles, {"without", " ''m'' optionally → ''n'' in compounds"})
end
-- Record original title and subtitles for use in alternant title-constructing code.
insert(declensions.orig_titles, data.title)
if #data.subtitles > 0 then
local subtitles = {}
for _, subtitle in ipairs(data.subtitles) do
if type(subtitle) == "table" then
-- Occurs e.g. with 1st-declension ''-ābus'' ending where
-- we want a common prefix to be extracted out if possible
-- in the alternant title-generating code.
insert(subtitles, concat(subtitle))
else
insert(subtitles, subtitle)
end
end
data.title = data.title .. " (" .. concat(subtitles, ", ") .. ")"
end
insert(declensions.subtitleses, data.subtitles)
end
-- Generate linked variants of slots that may be the lemma.
-- If the form is the same as the lemma (with links removed),
-- substitute the original lemma (with links included).
for _, slot in ipairs(potential_lemma_slots) do
local forms = data.forms[slot]
if forms then
local linked_forms = {}
if type(forms) ~= "table" then
forms = {forms}
end
for _, form in ipairs(forms) do
if form == seg.lemma then
insert(linked_forms, seg.orig_lemma)
else
insert(linked_forms, form)
end
end
data.forms["linked_" .. slot] = linked_forms
end
end
if seg.types.lig then
apply_ligatures(data.forms, is_adj)
end
if seg.types.sufn then
apply_sufn(data.forms, is_adj)
end
propagate_number_restrictions(data.forms, seg.num, is_adj)
for slot in iter_slots(is_adj) do
-- 1. Select the forms to append to the existing ones.
local new_forms
if is_adj then
if not seg.is_adj then
error("Can't decline noun '" .. seg.lemma .. "' when overall term is an adjective")
end
new_forms = data.forms[slot]
if not new_forms and slot:find("_[fn]$") then
new_forms = data.forms[slot:gsub("_[fn]$", "_m")]
end
elseif seg.is_adj then
if not seg.gender then
error("Declining modifying adjective " .. seg.lemma .. " but don't know gender of associated noun")
end
-- Select the appropriately gendered equivalent of the case/number
-- combination. Some adjectives won't have feminine or neuter
-- variants, though (e.g. 3-1 and 3-2 adjectives don't have a
-- distinct feminine), so in that case select the masculine.
new_forms = data.forms[slot .. "_" .. mw.ustring.lower(seg.gender)]
or data.forms[slot .. "_m"]
else
new_forms = data.forms[slot]
end
-- 2. Extract the new footnotes in the format we require, which is
-- different from the format passed in by the declension functions.
local new_notes = {}
if type(new_forms) == "string" and data.notes[slot .. "1"] then
new_notes[1] = {data.notes[slot .. "1"]}
elseif new_forms then
for j = 1, #new_forms do
if data.notes[slot .. j] then
new_notes[j] = {data.notes[slot .. j]}
end
end
end
-- 3. Append new forms and footnotes to the existing ones.
new_forms = normalize_form(new_forms)
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot], new_forms,
new_notes, slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
end
for slot, v in pairs(data.unattested) do
if v then
declensions.unattested[slot] = true
end
end
if not seg.types.nocat and (is_adj or not seg.is_adj) then
for _, cat in ipairs(data.categories) do
insert_if_not(declensions.categories, cat)
end
end
if data.footnote then
insert(declensions.footnotes, data.footnote)
end
if seg.prefix ~= "" and seg.prefix ~= "-" and seg.prefix ~= " " then
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
insert(declensions.title, data.title)
elseif seg.alternants then
local seg_declensions = nil
local seg_titles = {}
local seg_subtitleses = {}
local seg_stems_seen = {}
local seg_unattested = {}
local seg_categories = {}
local seg_footnotes = {}
-- If all alternants have exactly one non-constant segment and all are
-- of the same declension, we use special code that displays the
-- differences in the subtitles. Otherwise we use more general code
-- that displays the full title and subtitles of each segment,
-- separating segment combined titles by "and" and the segment-run
-- combined titles by "or".
local title_the_hard_way = false
local alternant_decl = nil
local alternant_decl_title = nil
for _, this_parsed_run in ipairs(seg.alternants) do
local num_non_constant_segments = 0
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
if not alternant_decl then
alternant_decl = segment.decl
elseif alternant_decl ~= segment.decl then
title_the_hard_way = true
num_non_constant_segments = 500
break
end
num_non_constant_segments = num_non_constant_segments + 1
end
end
if num_non_constant_segments ~= 1 then
title_the_hard_way = true
break
end
end
if not title_the_hard_way then
-- If using the special-purpose code, find the subtypes that are
-- not present in a given alternant but are present in at least
-- one other, and record "negative" variants of these subtypes
-- so that the declension-construction code can record subtitles
-- for these negative variants (so we can construct text like
-- "i-stem or imparisyllabic non-i-stem").
local subtypeses = {}
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
insert(subtypeses, segment.types)
insert_if_not(seg_stems_seen, segment.stem2)
end
end
end
local union = set_union(subtypeses)
for _, this_parsed_run in ipairs(seg.alternants) do
for _, segment in ipairs(this_parsed_run.segments) do
if segment.decl then
local neg_subtypes = set_difference(union, segment.types)
for neg_subtype, _ in pairs(neg_subtypes) do
segment.types["not_" .. neg_subtype] = true
end
end
end
end
end
for _, this_parsed_run in ipairs(seg.alternants) do
this_parsed_run.loc = seg.loc
this_parsed_run.num = this_parsed_run.num or seg.num
this_parsed_run.gender = this_parsed_run.gender or seg.gender
local this_declensions = decline_segment_run(this_parsed_run, pos, is_adj)
if this_declensions.noneut then
declensions.noneut = true
end
if this_declensions.nomf then
declensions.nomf = true
end
-- If there's a number restriction on the segment run, blank
-- out the forms outside the restriction. This allows us to
-- e.g. construct heteroclites that decline one way in the
-- singular and a different way in the plural.
if this_parsed_run.num == "sg" or this_parsed_run.num == "pl" then
for slot in iter_slots(is_adj) do
if this_parsed_run.num == "sg" and slot:find("pl", nil, true) or
this_parsed_run.num == "pl" and slot:find("sg", nil, true) then
this_declensions.forms[slot] = {}
this_declensions.notes[slot] = nil
end
end
end
if not seg_declensions then
seg_declensions = this_declensions
else
for slot in iter_slots(is_adj) do
-- For a given slot, combine the existing and new forms.
-- We do this by checking to see whether a new form is
-- already present and not adding it if so; in the
-- process, we keep a map from indices in the new forms
-- to indices in the combined forms, for use in
-- combining footnotes below.
local curforms = seg_declensions.forms[slot] or {}
local newforms = this_declensions.forms[slot] or {}
local newform_index_to_new_index = {}
for newj, form in ipairs(newforms) do
local did_break = false
for j = 1, #curforms do
if curforms[j] == form then
newform_index_to_new_index[newj] = j
did_break = true
break
end
end
if not did_break then
insert(curforms, form)
newform_index_to_new_index[newj] = #curforms
end
end
seg_declensions.forms[slot] = curforms
-- Now combine the footnotes. Keep in mind that
-- each form may have its own set of footnotes, and
-- in some cases we didn't add a form from the new
-- list of forms because it already occurred in the
-- existing list of forms; in that case, we combine
-- footnotes from the two sources.
local curnotes = seg_declensions.notes[slot]
local newnotes = this_declensions.notes[slot]
if newnotes then
if not curnotes then
curnotes = {}
end
for index, notes in pairs(newnotes) do
local combined_index = newform_index_to_new_index[index]
if not curnotes[combined_index] then
curnotes[combined_index] = notes
else
local combined = mw.clone(curnotes[combined_index])
for _, note in ipairs(newnotes) do
insert_if_not(combined, note)
end
curnotes[combined_index] = combined
end
end
end
end
end
for slot, v in pairs(this_declensions.unattested) do
if v then
seg_unattested[slot] = true
end
end
for _, cat in ipairs(this_declensions.categories) do
insert_if_not(seg_categories, cat)
end
for _, footnote in ipairs(this_declensions.footnotes) do
insert_if_not(seg_footnotes, footnote)
end
insert_if_not(seg_titles, this_declensions.title)
for _, subtitles in ipairs(this_declensions.subtitleses) do
insert(seg_subtitleses, subtitles)
end
if not alternant_decl_title then
alternant_decl_title = this_declensions.orig_titles[1]
end
end
-- If overall run is singular, copy singular to plural, and
-- vice-versa. See propagate_number_restrictions() for rationale;
-- also, this should eliminate cases of empty forms, which will
-- cause the overall set of forms for that slot to be empty.
propagate_number_restrictions(seg_declensions.forms, parsed_run.num, is_adj)
for slot in iter_slots(is_adj) do
local new_forms = normalize_form(seg_declensions.forms[slot])
if new_forms == nil then
declensions.forms[slot] = nil
declensions.notes[slot] = nil
else
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
new_forms, seg_declensions.notes[slot], nil)
end
end
for slot, v in pairs(seg_unattested) do
if v then
declensions.unattested[slot] = true
end
end
if is_adj or not seg.is_adj then
for _, cat in ipairs(seg_categories) do
insert_if_not(declensions.categories, cat)
end
end
for _, footnote in ipairs(seg_footnotes) do
insert_if_not(declensions.footnotes, footnote)
end
local title_to_insert
if title_the_hard_way then
title_to_insert = join_sentences(seg_titles, " or ")
else
-- Special-purpose title-generation code, for the common
-- situation where each alternant has single-segment runs and
-- all segments belong to the same declension.
--
-- 1. Find the initial subtitles common to all segments.
local first_subtitles = seg_subtitleses[1]
local num_common_subtitles = #first_subtitles
for j = 2, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
for k = 1, num_common_subtitles do
if not deep_equals(first_subtitles[k], this_subtitles[k]) then
num_common_subtitles = k - 1
break
end
end
end
-- 2. Construct the portion of the text based on the common subtitles.
local common_subtitles = {}
for j = 1, num_common_subtitles do
if type(first_subtitles[j]) == "table" then
insert(common_subtitles, concat(first_subtitles[j]))
else
insert(common_subtitles, first_subtitles[j])
end
end
local common_subtitle_portion = concat(common_subtitles, ", ")
local non_common_subtitle_portion
-- 3. Special-case the situation where there's one non-common
-- subtitle in each segment and a common prefix or suffix to
-- all of them.
local common_prefix, common_suffix
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
if #this_subtitles ~= num_common_subtitles + 1 or
type(this_subtitles[num_common_subtitles + 1]) ~= "table" or
#this_subtitles[num_common_subtitles + 1] ~= 2 then
break
end
if j == 1 then
common_prefix = this_subtitles[num_common_subtitles + 1][1]
common_suffix = this_subtitles[num_common_subtitles + 1][2]
else
local this_prefix = this_subtitles[num_common_subtitles + 1][1]
local this_suffix = this_subtitles[num_common_subtitles + 1][2]
if this_prefix ~= common_prefix then
common_prefix = nil
end
if this_suffix ~= common_suffix then
common_suffix = nil
end
if not common_prefix and not common_suffix then
break
end
end
end
if common_prefix or common_suffix then
if common_prefix and common_suffix then
error("Something is wrong, first non-common subtitle is actually common to all segments")
end
if common_prefix then
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][2])
end
non_common_subtitle_portion = common_prefix .. concat(non_common_parts, " or ")
else
local non_common_parts = {}
for j = 1, #seg_subtitleses do
insert(non_common_parts, seg_subtitleses[j][num_common_subtitles + 1][1])
end
non_common_subtitle_portion = concat(non_common_parts, " or ") .. common_suffix
end
else
-- 4. Join the subtitles that differ from segment to segment.
-- Record whether there are any such differing subtitles.
-- If some segments have differing subtitles and others don't,
-- we use the text "otherwise" for the segments without
-- differing subtitles.
local saw_non_common_subtitles = false
local non_common_subtitles = {}
for j = 1, #seg_subtitleses do
local this_subtitles = seg_subtitleses[j]
local this_non_common_subtitles = {}
for k = num_common_subtitles + 1, #this_subtitles do
if type(this_subtitles[k]) == "table" then
insert(this_non_common_subtitles, concat(this_subtitles[k]))
else
insert(this_non_common_subtitles, this_subtitles[k])
end
end
if #this_non_common_subtitles > 0 then
insert(non_common_subtitles, concat(this_non_common_subtitles, ", "))
saw_non_common_subtitles = true
else
insert(non_common_subtitles, "otherwise")
end
end
non_common_subtitle_portion =
saw_non_common_subtitles and concat(non_common_subtitles, " or ") or ""
end
-- 5. Combine the common and non-common subtitle portions.
local subtitle_portions = {}
if common_subtitle_portion ~= "" then
insert(subtitle_portions, common_subtitle_portion)
end
if non_common_subtitle_portion ~= "" then
insert(subtitle_portions, non_common_subtitle_portion)
end
if #seg_stems_seen > 1 then
insert(subtitle_portions,
(number_to_english[#seg_stems_seen] or "" .. #seg_stems_seen) .. " different stems"
)
end
local subtitle_portion = concat(subtitle_portions, "; ")
if subtitle_portion ~= "" then
title_to_insert = alternant_decl_title .. " (" .. subtitle_portion .. ")"
else
title_to_insert = alternant_decl_title
end
end
-- Don't insert blank title (happens e.g. with "((ali))quis<irreg+>").
if title_to_insert ~= "" then
insert(declensions.title, title_to_insert)
end
else
for slot in iter_slots(is_adj) do
declensions.forms[slot], declensions.notes[slot] = append_form(
declensions.forms[slot], declensions.notes[slot],
slot:find("linked", nil, true) and seg.orig_prefix or seg.prefix)
end
insert(declensions.title, "ส่วนที่" .. glossary_link("ผันรูปไม่ได้"))
end
end
-- First title is uppercase, remainder have an indefinite article, joined
-- using "with".
local titles = {}
for i, title in ipairs(declensions.title) do
if i == 1 then
insert(titles, ucfirst(title))
else
insert(titles, add_indefinite_article(title))
end
end
declensions.title = concat(titles, " with ")
return declensions
end
local function construct_title(args_title, declensions_title, generate_type, parsed_run)
if args_title then
declensions_title = args_title:gsub("<1>", "[[ภาคผนวก:Latin first declension|first declension]]")
declensions_title = declensions_title:gsub("<1&2>", "[[ภาคผนวก:Latin first declension|first]]/[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<2>", "[[ภาคผนวก:Latin second declension|second declension]]")
declensions_title = declensions_title:gsub("<3>", "[[ภาคผนวก:Latin third declension|third declension]]")
declensions_title = declensions_title:gsub("<4>", "[[ภาคผนวก:Latin fourth declension|fourth declension]]")
declensions_title = declensions_title:gsub("<5>", "[[ภาคผนวก:Latin fifth declension|fifth declension]]")
if generate_type == "headword" then
declensions_title = lcfirst((declensions_title:gsub("%.$", "")))
else
declensions_title = ucfirst(declensions_title)
end
else
local post_text_parts = {}
if parsed_run.loc then
insert(post_text_parts, ", with locative")
end
-- FIXME: Hierosolyma has -a/-ōrum (2nd neut. pl. with sg. meaning) and -a/-ae (1st fem. sg.)
-- alternants, but because it has singular and plural forms overall, it's not made clear that
-- each alternant is restricted to one number.
if parsed_run.num == "sg" then
insert(post_text_parts, ", เอกพจน์เท่านั้น")
elseif parsed_run.num == "pl" then
insert(post_text_parts, ", พหูพจน์เท่านั้น")
end
local post_text = concat(post_text_parts)
if generate_type == "headword" then
declensions_title = lcfirst(declensions_title) .. post_text
else
declensions_title = ucfirst(declensions_title) .. post_text .. "."
end
end
return declensions_title
end
function export.do_generate_noun_forms(parent_args, pos, generate_type, def)
local params = {
[1] = {required = true, default = def or "aqua<1>"},
footnote = true,
title = true,
num = true,
json = {type = "boolean"},
}
for slot in iter_noun_slots() do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.id = true
params.cat = list
params.m = sublist
params.f = sublist
params.n = sublist
params.adj = sublist
params.dim = sublist
params.aug = sublist
params.g = list
params.indecl = {type = "boolean"}
end
if pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if args.title then
track("overriding-title")
end
local parsed_run = parse_segment_run_allowing_alternants(args[1])
parsed_run.loc = parsed_run.loc or not not (args.loc_sg or args.loc_pl)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, false)
if not parsed_run.loc then
declensions.forms.loc_sg = nil
declensions.forms.loc_pl = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
gender = parsed_run.gender,
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
overriding_lemma = args.lemma,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
m = args.m,
f = args.f,
overriding_genders = args.g,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_noun_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_noun_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.do_generate_adj_forms(parent_args, pos, generate_type, degree, def)
local boolean = {type = "boolean"}
local params = {
[1] = {required = true, default = def or "bonus"},
footnote = true,
title = true,
num = true,
noneut = boolean,
nomf = boolean,
json = boolean,
}
for slot in iter_adj_slots() do
params[slot] = true
end
for slot in iter_noun_slots(true) do
params[slot] = true
end
if generate_type == "headword" then
local list = {list = true}
local sublist = {sublist = "/"}
params.lemma = list
params.adv = sublist
params.id = true
params.cat = list
params.indecl = boolean
if degree == "ขั้นกว่า" or degree == "ขั้นสุด" then
params.positive = sublist
end
if degree ~= "ขั้นกว่า" then
params.comp = sublist
end
if degree ~= "ขั้นสุด" then
params.sup = sublist
end
end
local set_pos
if not pos then
params.pos = {set = {"คำคุณศัพท์", "ตัวกำหนด", "รูปกริยาขยาย", "คำสรรพนาม"}, default = "คำคุณศัพท์"}
set_pos = true
elseif pos == "เลข" then
params.type = true
end
local args = process_params(parent_args, params)
if set_pos then
pos = pluralize(args.pos)
end
for slot in iter_noun_slots(true) do
local arg = args[slot]
if arg ~= nil then
for i = 1, genders_n do
local slot_g = slot .. "_" .. genders[i]
local arg_g = args[slot_g]
if arg_g == nil then
args[slot_g] = arg
end
end
args[slot] = nil
end
end
if args.title then
track("overriding-title")
end
local segment_run = args[1]
if not segment_run:match("[<(]") then
-- If the segment run doesn't have any explicit declension specs or alternants,
-- add a default declension spec of <+> to it (or <0+> for indeclinable
-- adjectives). This allows the majority of adjectives to just specify
-- the lemma.
segment_run = segment_run .. (args.indecl and "<0+>" or "<+>")
end
local parsed_run = parse_segment_run_allowing_alternants(segment_run)
parsed_run.loc = parsed_run.loc or not not (
args.loc_sg_m or args.loc_sg_f or args.loc_sg_n or args.loc_pl_m or args.loc_pl_f or args.loc_pl_n
)
parsed_run.num = args.num or parsed_run.num
local declensions = decline_segment_run(parsed_run, pos, true)
if not parsed_run.loc then
declensions.forms.loc_sg_m = nil
declensions.forms.loc_sg_f = nil
declensions.forms.loc_sg_n = nil
declensions.forms.loc_pl_m = nil
declensions.forms.loc_pl_f = nil
declensions.forms.loc_pl_n = nil
end
declensions.title = construct_title(args.title, declensions.title, generate_type, parsed_run)
local all_data = {
title = declensions.title,
footnotes = {},
num = parsed_run.num or "",
propses = parsed_run.propses,
forms = declensions.forms,
unattested = declensions.unattested,
categories = declensions.categories,
notes = {},
user_specified = {},
accel = {},
loc = declensions.loc,
noneut = args.noneut or declensions.noneut,
nomf = args.nomf or declensions.nomf,
overriding_lemma = args.lemma,
positive = args.positive,
comp = args.comp,
sup = args.sup,
adv = args.adv,
id = args.id,
pos = pos,
cat = args.cat,
indecl = args.indecl,
num_type = args["type"],
}
if generate_type ~= "bare" then
all_data.accel = {}
end
if args.footnote then
insert_if_not(all_data.footnotes, args.footnote)
end
for _, footnote in ipairs(declensions.footnotes) do
insert_if_not(all_data.footnotes, footnote)
end
for slot in iter_adj_slots() do
if declensions.notes[slot] then
for index, notes in pairs(declensions.notes[slot]) do
all_data.notes[slot .. index] = notes
end
end
end
process_adj_forms_and_overrides(all_data, args, generate_type)
if args.json then
return require(json_module).toJSON(all_data)
end
return all_data
end
function export.show_noun(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_noun_forms(parent_args, "คำนาม")
if type(data) == "string" then -- JSON
return data
end
show_forms(data, false)
local num = data.num
if num == "sg" then
return make_noun_table_sg(data)
elseif num == "pl" then
return make_noun_table_pl(data)
end
return make_noun_table(data)
end
function export.show_adj(frame)
local parent_args = frame:getParent().args
local data = export.do_generate_adj_forms(parent_args)
if type(data) == "string" then -- JSON
return data
end
partial_show_forms(data, true)
return make_adj_table(data)
end
return export
2ifvcb7ml48si2fr8f0e1bnyl3jq8z9
酸素
0
2296523
5776212
5776171
2026-11-02T07:03:30Z
InternetArchiveBot
12208
Rescuing 1 sources and tagging 0 as dead. #IABot (v2.0.9.6)
5776212
wikitext
text/x-wiki
== ภาษาจีน ==
{{zh-forms}}
=== การออกเสียง ===
{{zh-pron
|m=suānsù
|mn=tw:sng-sò͘/twv:sàng-sò͘/twv:sàng-soh
|mn_note=sàng-sò͘ & sàng-soh - derived from Japanese reading, referring to oxygen masks
|cat=n
}}
=== คำนาม ===
{{head|zh|คำนาม}}
# {{lb|zh|dated|หรือ|Taiwanese Hokkien}} [[ออกซิเจน]]
#* {{zh-x|酸素 和 水素 造成 的 純一---無-雜 的 水,這樣 的 東西{xi},如果 是 有 生命 的 自然界 中,是 不存在 的。||MSC|ref='''1924''', {{lang|zh|出了象牙之塔}}, original title {{lang|ja|象牙の塔を出て}} (''Depart from the Ivory Tower''), by {{w|Kuriyagawa Hakuson|{{smallcaps|Kuriyagawa}} Hakuson}}, transl. {{w|Lu Xun}}}}
#* {{zh-x|卵白{pe̍h}質,伊 ê 成分 是 炭{Thàn}素,^水素,'''^酸素''',窒{Chek}素,伊 ê 路用 是 會{ōe} hō͘ 人 ê 身軀 得{tit}著[着] 勢{sè}力{le̍k},也 是 hō͘ 細胞 加{ke}添 新 %ê。|Protein: its composition is carbon, hydrogen, '''oxygen''' and nitrogen; it is used to give energy to the human body and to regenerate cells.|TW|ref='''1926''', {{lang|zh|張基全}} (Tiuⁿ Ki-chôan), [http://210.240.194.97/nmtl/dadwt/thak.asp?id=513&kw=%B2%BF%AF%C0 {{lang|zh|酒佮健康}} (Chiú kap Kiān-khong)]}}
# {{lb|zh|Taiwanese Hokkien}} [[หน้ากากออกซิเจน]]
#* {{zh-x|方{Png} 耀{Iāu}~乾[乾]{khiân} 看~著 病房 內底 鬥 '''sàng-sòo''' 的 阿母{bú},想~著 伊 細漢 時 的 願望,一{it}定 有{ū} 真 濟{chē} 感觸;……|While looking at his mother wearing an '''oxygen mask''' in the hospital room, [[:w:zh:方耀乾|Png Iau-khian]] (Fang Yaoqian) thought of her childhood dreams, and he must have had so many feelings; ...|ref='''2008''', 陳佩如, [http://paperupload.nttu.edu.tw/download.php?sn=1031 方耀乾台語詩研究:1997-2008]|MN}}
==== คำพ้องความ ====
* {{s|ออกซิเจน}} {{zh-l|氧}}
=== อ้างอิง ===
* {{R:nan:tdj|54913|v=1|p=773}}
{{C|zh|ธาตุเคมี}}
== ภาษาญี่ปุ่น ==
{{ja-kanjitab|さん|そ|yomi=on}}
{{elements|ja|O|{{ja-r|窒%素|ちっ%そ}}|N|{{ja-r|フッ素|フッそ}}|F|sort=すいそ}}
{{swp|lang=ja}}
=== รากศัพท์ ===
จาก{{affix|ja|sort=さんそ|酸|素|tr1=san|t1=[[กรด]]|tr2=so|t2=[[ธาตุ]]}}, {{cal|ja|nl|nocap=1|zuurstof|lit=[[sourstuff]]}}; เทียบ{{cog|de|Sauerstoff}}, {{cog|lb|Sauerstoff}}, {{cog|id|zat asam}}; {{coinage|ja|alt=อูดางาวะ โยอัน|อูดางาวะ โยอัน|in=1834|nat=ญี่ปุ่น|occ=นักวิทยาศาสตร์}} ในหนังสือของเขา [https://web.archive.org/web/20250213030807/https://www.wul.waseda.ac.jp/kotenseki/html/ya09/ya09_00857_0037/index.html 遠西医方名物考補遺] ([https://web.archive.org/web/20210917200333/https://archive.wul.waseda.ac.jp/kosho/ya09/ya09_00857/ya09_00857_0043/ya09_00857_0043_p0023.jpg “酸素”])
=== การออกเสียง ===
{{ja-pron|acc=1|さんそ}}
=== คำนาม ===
{{ja-noun|さんそ}}
# [[ออกซิเจน]] {{gl|ธาตุ}}
# ออกซิเจน {{gl|โมเลกุล}}
==== ลูกคำ ====
{{col2|ja
|{{ja-r|酸%素%気|さん%そ%き|oxygen [[gas]]}}
|{{ja-r|酸%素 を 送り込む|さん%そ を おくりこむ|[[oxygenate]]}}
|{{ja-r|酸%素%酸|さん%そ%さん|oxoacid}}
|{{ja-r|酸%素 吸%入|さん%そ きゅう%にゅう|oxygen inhalation}}
|{{ja-r|酸%素 供%給|さん%そ きょう%きゅう|oxygen supply}}
|{{ja-r|酸%素 欠%乏|さん%そ けつ%ぼう|oxygen deficiency}}
|{{ja-r|酸%素 テント|さん%そ テント|oxygen tent}}
|{{ja-r|酸%素 マスク|さん%そ マスク|oxygen mask}}
|{{ja-r|酸%素 添%加 酵%素|さん%そ てん%か こう%そ|oxygenase}}
|{{ja-r|酸%素アセチレン炎|さん%そ-アセチレン-えん|oxyacetylene flame}}
|{{ja-r|重%酸%素|じゅう%さん%そ|heavy oxygen}}
|{{ja-r|低%酸%素%脳%症|てい%さん%そ%-のう%しょう|hypoxic encephalopathy}}
|{{ja-r|圧%縮 酸%素|あっ%しゅく さん%そ|compressed oxygen}}
|{{ja-r|液%体 酸%素|えき%たい さん%そ|liquid oxygen}}
|{{ja-r|活%性 酸%素|かっ%せい さん%そ|reactive oxygen}}
|{{ja-r|溶%存 酸%素|よう%ぞん さん%そ|dissolved oxygen}}
|{{ja-r|高%圧 酸%素 室|こう%あつ さん%そ しつ|hyperbaric oxygen chamber}}
|{{ja-r|高%圧 酸%素 療%法|こう%あつ さん%そ りょう%ほう|hyperbaric oxygen therapy}}
|{{ja-r|化%学%的 酸%素 要%求%量|か%がく%てき さん%そ よう%きゅう%りょう|chemical oxygen demand}}
|{{ja-r|生%物%化%学%的 酸%素 要%求%量|せい%ぶつ%-か%がく%てき さん%そ よう%きゅう%りょう|biochemical oxygen demand}}
}}
==== คำสืบทอด ====
* {{desc|bor=y|ko|산소}}
* {{desc|bor=y|nan|酸素|tr=sng-sò͘}}
=== ดูเพิ่ม ===
* {{ja-r|酸%化|さん%か|gloss=[[ออกซิเดชัน]]}}
=== อ้างอิง ===
* {{R:Kanjipedia Kotoba|0002668000}}
7xdt7al6jpmma9hr3kkjfp6tqf9qdgq
legislations
0
2359455
5776176
2026-11-02T01:03:26Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776176
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== คำนาม ===
{{head|en|รูปนาม}}
# {{plural of|en|legislation}} (การออกกฎหมายหลายครั้ง)
=== คำสลับอักษร ===
* {{anagrams|en|a=aegiillnosst|Agostinellis}}
3gqu9abkdukqa5xeo2fhx5muzksbq2e
5776177
5776176
2026-11-02T01:03:53Z
Octahedron80
267
/* คำนาม */
5776177
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== คำนาม ===
{{head|en|รูปนาม}}
# {{plural of|en|legislation}} (การออกกฎหมายหลายครั้ง/หลายฉบับ)
=== คำสลับอักษร ===
* {{anagrams|en|a=aegiillnosst|Agostinellis}}
0r71sum5qtsl6yb7hz8a4rrdtl25mnt
legislation
0
2359456
5776178
2026-11-02T01:05:28Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776178
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== รากศัพท์ ===
จาก{{bor|en|la-lat|lēgislātiō||การบัญญัติกฎหมาย}}; ในทางสัณฐานวิทยา {{suffix|en|legislate|ion}}
=== การออกเสียง ===
* {{IPA|en|/ˌlɛd͡ʒɪˈsleɪʃən/|/ˌlɛd͡ʒəˈsleɪʃən/|[ˌlɛd͡ʒˈsleɪʃən]|a=GA,CA}}
** {{audio|en|LL-Q1860 (eng)-Vealhurl-legislation.wav|a=Southern England}}
** {{audio|en|en-us-legislation.ogg|a=Southern California}}
* {{IPA|en|/ˌled͡ʒɪˈslæɪʃən/|a=AU}}
* {{IPA|en|/ˌled͡ʒəˈslæɪʃən/|a=NZ}}
* {{hyph|en|le|gis|la|tion}}
* {{rhymes|en|eɪʃən|s=4}}
=== คำนาม ===
{{en-noun|-|s}}
# [[การออก]][[กฎหมาย]], [[การบัญญัติ]]กฎหมาย; [[การร่าง]]และ[[ตรา]]กฎหมาย
#: {{syn|en|lawmaking}}
#: {{ux|en|pass '''legislation'''|ผ่าน'''การออกกฎหมาย'''/ผ่าน'''ร่างกฎหมาย'''}}
#: {{ux|en|There is a lack of '''legislation''' about human cloning.|ยังขาด'''การบัญญัติกฎหมาย'''เกี่ยวกับการโคลนมนุษย์}}
# [[ตัวบทกฎหมาย]], กฎหมายที่ตราขึ้นโดย[[สภานิติบัญญัติ|องค์กรนิติบัญญัติ]]หรือองค์กรปกครองอื่น
#* {{quote-book|en|year=2003|author=Ruben E. Agpalo|title=Statutory Construction|page=467
|passage=The general rule is that constitutional provisions are self-executing, except when the provisions themselves expressly require '''legislations''' to implement them {{...}}
|translation=หลักทั่วไปคือบทบัญญัติแห่งรัฐธรรมนูญมีผลบังคับใช้ได้ในตัวเอง เว้นแต่เมื่อบทบัญญัตินั้น ๆ ระบุไว้อย่างชัดแจ้งว่าต้องมี'''กฎหมาย'''เพื่อนำไปปฏิบัติ {{...}}}}
#* {{RQ:en:New York Times|author=Soumya Karlamangla|title=California Bans All Plastic Bags After Its First Effort Backfired|url=https://www.nytimes.com/2024/09/23/us/california-plastic-bags-ban.html|date=23-09-2024|passage=Gov. Gavin Newsom signed '''legislation''' on Sunday banning the sale at grocery checkouts of all plastic bags, regardless of thickness.|translation=ผู้ว่าการรัฐ แกวิน นิวซัม ได้ลงนามใน'''กฎหมาย'''เมื่อวันอาทิตย์ ซึ่งสั่งห้ามจำหน่ายถุงพลาสติกทุกชนิด ณ จุดชำระเงินของร้านขายของชำ โดยไม่คำนึงถึงความหนา|archiveurl=https://web.archive.org/web/20251102103852/https://www.nytimes.com/2024/09/23/us/california-plastic-bags-ban.html|archivedate=2 November 2025}}
==== คำลูกกลุ่ม ====
* {{sense|กฎหมายที่ตราขึ้น}} {{l|en|rooster}} {{qualifier|สแลงพ้นสมัย; กฎหมายที่ตราขึ้นเพื่อประโยชน์ของฝ่ายนิติบัญญัติเองเท่านั้น}}
==== ลูกคำ ====
{{col|en|antilegislation|counterlegislation|delegislation|overlegislation|postlegislation|prelegislation|relegislation
|delegated legislation|primary legislation|secondary legislation|subordinate legislation|subsidiary legislation|Magnitsky legislation}}
=== คำสลับอักษร ===
* {{anagrams|en|a=aegiillnost|Agostinelli}}
{{cln|en|ศัพท์ที่มี 4 พยางค์}}
{{C|en|รัฐบาลและการปกครอง|กฎหมาย}}
nbwqkdu8wk8zgi3jlp6qxievdv5baj6
lawmaking
0
2359457
5776179
2026-11-02T01:06:39Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776179
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== รากศัพท์ ===
จาก{{compound|en|law|make|alt2=making}}
=== คำนาม ===
{{en-noun|~}}
# กระบวนการ[[ผ่าน]]หรือ[[ตรา]][[กฎหมาย]]; [[การออกกฎหมาย]], [[นิติบัญญัติ]]
==== คำเกี่ยวข้อง ====
* {{l|en|lawmaker}}
rrbtsx00syj02gvg9te43oqw99fv6oo
5776180
5776179
2026-11-02T01:07:00Z
Octahedron80
267
/* คำนาม */
5776180
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== รากศัพท์ ===
จาก{{compound|en|law|make|alt2=making}}
=== คำนาม ===
{{en-noun|~}}
# กระบวนการ[[ผ่าน]]หรือ[[ตรา]][[กฎหมาย]]; [[การออก]]กฎหมาย, [[นิติบัญญัติ]]
==== คำเกี่ยวข้อง ====
* {{l|en|lawmaker}}
7expatk4q6schrjdgniaviwktia7cqo
แม่แบบ:RQ:en:CNN
10
2359458
5776181
2026-11-02T01:09:54Z
Octahedron80
267
นำเข้าจาก enwikt
5776181
wikitext
text/x-wiki
{{#invoke:quote|call_quote_template
|template = quote-web
|en
|author = {{{author|{{{1|}}}}}}
|authorlink = {{{authorlink|}}}
|title = {{#if:{{{title|}}}{{{2|}}}
| {{{title|{{{2|}}}}}}
| {{maintenance line|please specify the article title}}
}}
|series = {{#if:{{{section|}}}
| {{#if:{{num|{{{section}}}}}
| section {{{section}}}
| {{ucfirst:{{{section}}}}} section
}}
}}
|work = w:CNN
|location = Atlanta, Ga.
|publisher = w:Warner Bros. Discovery
|date = {{{date|{{{3|}}}}}}
|textparam = 4
|footer = {{#if:{{{footer|}}}|{{small|{{{footer}}}}}}}{{#if:{{{url|}}}{{{titleurl|}}}|{{#if:{{{archiveurl|}}}||{{#if:{{{footer|}}}|. }}{{maintenance line|Can we [[Wiktionary:Quotations/Resources#How to archive sites|archive]] this [[URL]]?}}}}}}
|translation={{{t|{{{translation|}}}}}}
|propagateparams = origdate,author2,author3,author4,author5,author6,author7,authorlink,authorlink2,authorlink3,authorlink4,authorlink5,quotee,genre,url,url2,archiveurl,year,date_published,year_published,archivedate,volume,issue,page,pages,pageurl,column,columns,titleurl,accessdate,termlang,nocolon,source,speaker,t,issn,oclc,month,trans-title
|allowparams = author,1,title,2,section,date,3,authorlink,t,translation
}}<noinclude>{{documentation}}</noinclude>
9p10vffmc7njj15cn1v3x1hek9hj4n9
lawmaker
0
2359459
5776182
2026-11-02T01:10:22Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776182
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:Legislator}}
=== รากศัพท์ ===
จาก{{inh|en|enm|lawemaker}}, {{m|enm|lawe maker}}, เทียบเท่า {{affix|en|law|maker}}
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Vininn126-lawmaker.wav|a=US}}
=== คำนาม ===
{{en-noun}}
# ผู้ที่[[สร้าง]]หรือ[[ตรา]][[กฎหมาย]]; ผู้[[ออก]]กฎหมาย, ผู้[[บัญญัติ]]กฎหมาย, [[สมาชิก]][[สภานิติบัญญัติ]]
#: {{hypo|en|legislator|regulator}}
#* {{quote-journal|en|date=2021-04-21|title=French to favour rail over short-haul flights|journal=RAIL|issue=929|page=11|text='''Lawmakers''' in France voted over the weekend of April 10/11 to introduce measures to restrict flights on routes where the same journey can be made by rail in under two and a half hours.|translation='''สมาชิกรัฐสภา'''ในฝรั่งเศสได้ลงมติในช่วงสุดสัปดาห์ของวันที่ 10–11 เมษายน เพื่อนำมาตรการจำกัดเที่ยวบินในเส้นทางที่สามารถเดินทางด้วยรถไฟได้ในเวลาไม่ถึงสองชั่วโมงครึ่งมาใช้}}
#* {{RQ:en:CNN|author=Lauren Fox; Morgan Rimmer; Clare Foran|title=Senate votes to confirm Pam Bondi as attorney general|url=https://www.cnn.com/2022/01/20/politics/electoral-count-act-reform-senate/index.html|date=2022-01-20|passage=Republican Sens. Susan Collins of Maine and Mitt Romney of Utah both confirmed that a bipartisan group of '''lawmakers''' will have a series of video meetings starting in the next several days to see if they could agree on a deal soon.|translation=สว. พรรครีพับลิกัน ทั้ง ซูซาน คอลลินส์ จากรัฐเมน และ มิตต์ รอมนีย์ จากรัฐยูทาห์ ต่างยืนยันว่ากลุ่ม'''สมาชิกรัฐสภา'''จากทั้งสองพรรคจะมีการประชุมทางวิดีโออย่างต่อเนื่องเริ่มในอีกไม่กี่วันข้างหน้า เพื่อดูว่าจะสามารถบรรลุข้อตกลงร่วมกันได้ในเร็ว ๆ นี้หรือไม่|archiveurl=https://web.archive.org/web/20220421025404/https://www.cnn.com/2022/01/20/politics/electoral-count-act-reform-senate/index.html}}
#* {{RQ:en:CNN|author=Annie Grayer|title=Trump border czar privately tempers Republican lawmaker expectations on administration’s initial deportation operation|url=https://www.cnn.com/2025/01/11/politics/trump-border-czar-tom-homan-deportations/index.html|date=2025-01-11|passage=“Many members are only now beginning to understand that,” one GOP '''lawmaker''' told CNN.|translation=“สมาชิกหลายคนเพิ่งจะเริ่มเข้าใจเรื่องนั้นในตอนนี้เอง” '''สมาชิกรัฐสภา'''พรรครีพับลิกันคนหนึ่งกล่าวกับซีเอ็นเอ็น|archiveurl=https://web.archive.org/web/20250321165119/https://www.cnn.com/2025/01/11/politics/trump-border-czar-tom-homan-deportations/index.html}}
==== คำเกี่ยวข้อง ====
* {{l|en|lawgiver}}
* {{l|en|lawmaking}}
==== คำแปลภาษาอื่น ====
{{trans-see|legislator}}
{{C|en|กฎหมาย|บุคคล}}
bflqltn8fgoje1edno0tfdz6yfpks5q
5776193
5776182
2026-11-02T01:46:43Z
Octahedron80
267
/* คำแปลภาษาอื่น */
5776193
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:Legislator}}
=== รากศัพท์ ===
จาก{{inh|en|enm|lawemaker}}, {{m|enm|lawe maker}}, เทียบเท่า {{affix|en|law|maker}}
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Vininn126-lawmaker.wav|a=US}}
=== คำนาม ===
{{en-noun}}
# ผู้ที่[[สร้าง]]หรือ[[ตรา]][[กฎหมาย]]; ผู้[[ออก]]กฎหมาย, ผู้[[บัญญัติ]]กฎหมาย, [[สมาชิก]][[สภานิติบัญญัติ]]
#: {{hypo|en|legislator|regulator}}
#* {{quote-journal|en|date=2021-04-21|title=French to favour rail over short-haul flights|journal=RAIL|issue=929|page=11|text='''Lawmakers''' in France voted over the weekend of April 10/11 to introduce measures to restrict flights on routes where the same journey can be made by rail in under two and a half hours.|translation='''สมาชิกรัฐสภา'''ในฝรั่งเศสได้ลงมติในช่วงสุดสัปดาห์ของวันที่ 10–11 เมษายน เพื่อนำมาตรการจำกัดเที่ยวบินในเส้นทางที่สามารถเดินทางด้วยรถไฟได้ในเวลาไม่ถึงสองชั่วโมงครึ่งมาใช้}}
#* {{RQ:en:CNN|author=Lauren Fox; Morgan Rimmer; Clare Foran|title=Senate votes to confirm Pam Bondi as attorney general|url=https://www.cnn.com/2022/01/20/politics/electoral-count-act-reform-senate/index.html|date=2022-01-20|passage=Republican Sens. Susan Collins of Maine and Mitt Romney of Utah both confirmed that a bipartisan group of '''lawmakers''' will have a series of video meetings starting in the next several days to see if they could agree on a deal soon.|translation=สว. พรรครีพับลิกัน ทั้ง ซูซาน คอลลินส์ จากรัฐเมน และ มิตต์ รอมนีย์ จากรัฐยูทาห์ ต่างยืนยันว่ากลุ่ม'''สมาชิกรัฐสภา'''จากทั้งสองพรรคจะมีการประชุมทางวิดีโออย่างต่อเนื่องเริ่มในอีกไม่กี่วันข้างหน้า เพื่อดูว่าจะสามารถบรรลุข้อตกลงร่วมกันได้ในเร็ว ๆ นี้หรือไม่|archiveurl=https://web.archive.org/web/20220421025404/https://www.cnn.com/2022/01/20/politics/electoral-count-act-reform-senate/index.html}}
#* {{RQ:en:CNN|author=Annie Grayer|title=Trump border czar privately tempers Republican lawmaker expectations on administration’s initial deportation operation|url=https://www.cnn.com/2025/01/11/politics/trump-border-czar-tom-homan-deportations/index.html|date=2025-01-11|passage=“Many members are only now beginning to understand that,” one GOP '''lawmaker''' told CNN.|translation=“สมาชิกหลายคนเพิ่งจะเริ่มเข้าใจเรื่องนั้นในตอนนี้เอง” '''สมาชิกรัฐสภา'''พรรครีพับลิกันคนหนึ่งกล่าวกับซีเอ็นเอ็น|archiveurl=https://web.archive.org/web/20250321165119/https://www.cnn.com/2025/01/11/politics/trump-border-czar-tom-homan-deportations/index.html}}
==== คำเกี่ยวข้อง ====
* {{l|en|lawgiver}}
* {{l|en|lawmaking}}
{{C|en|กฎหมาย|บุคคล}}
flc7bb81uxnt9m8k1rn72cntmj1r75a
legislator
0
2359460
5776183
2026-11-02T01:16:32Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด เรียงลำดับหัวเรื่องภาษา
5776183
wikitext
text/x-wiki
== ภาษาโปแลนด์ ==
{{wp|pl:}}
=== รากศัพท์ ===
{{ety|pl|:lbor|la:lēgislātor|text=+|tree=1}}
=== การออกเสียง ===
{{pl-pr|a=LL-Q809 (pol)-Poemat-legislator.wav}}
=== คำนาม ===
{{pl-noun|m-pr}}
# {{lb|pl|กฎหมาย}} [[ผู้เชี่ยวชาญ]][[ด้าน]][[กฎหมาย]], [[นักกฎหมาย]]
# {{lb|pl|การปกครอง|กฎหมาย}} ผู้ที่[[ออก]]หรือ[[ตรา]][[กฎหมาย]]
#: {{syn|pl|prawodawca|ustawodawca}}
==== การผันรูป ====
{{pl-ndecl|<m.pr>}}
==== คำเกี่ยวข้อง ====
{{col|pl|title=adjectives|legislacyjny|legislatywny}}
{{col|pl|title=nouns|legislacja|legislatura|legislatywa}}
=== อ่านเพิ่ม ===
* {{R:pl:WSJP}}
* {{R:pl:PWN}}
{{C|pl|บุคคลชาย|อาชีพ}}
== ภาษาโรมาเนีย ==
=== รากศัพท์ ===
{{ety|ro|:bor|fr:législateur|:af<surf>|legisla|-tor|text=+|tree=1}}
=== คำนาม ===
{{ro-noun|m|legislatori}}
# ผู้ที่[[ออก]]หรือ[[ตรา]][[กฎหมาย]]
==== การผันรูป ====
{{ro-noun-m}}
== ภาษาละติน ==
=== รากศัพท์ ===
ในภาษาละตินคลาสสิก มักเขียนคำนี้แยกกัน: {{m|la|lēgis}} {{m|la|lātor}}
=== การออกเสียง ===
* {{la-IPA|lēgislātor}}
=== คำนาม ===
{{la-noun|lēgislātor<3>}}
# ผู้ที่[[ออก]]หรือ[[ตรา]][[กฎหมาย]]
#* '''ประมาณ ค.ศ. 130–180''', [[w:ไกอุส (นักนิติศาสตร์)|ไกอุส]], ''[[w:สถาบันแห่งไกอุส|Institutiones]]'', 3.76
#*: {{quote|la|nec me praeterit non satis in ea re '''legislatorem''' voluntatem suam verbis expressisse
|translation=''ทว่า ข้าพเจ้ามิได้ละเลยข้อสังเกตที่ว่า '''ผู้ออกกฎหมาย'''มิได้แสดงเจตจำนงของตนในเรื่องนี้ด้วยถ้อยคำที่ชัดเจนเพียงพอ''}}
# ผู้[[บัญญัติ]]กฎหมาย
==== การผันรูป ====
{{la-ndecl|lēgislātor<3>}}
==== คำสืบทอด ====
{{top2}}
* {{desc|ca|legislador}}
* {{desc|en|legislator}}
* {{desc|fr|législateur}}
* {{desc|it|legislatore}}
* {{desc|nrf|législateur}}
* {{desc|pl|legislator|bor=1}}
* {{desc|pt|legislador}}
* {{desc|ro|legislator}}
* {{desc|es|legislador}}
{{bottom}}
=== อ้างอิง ===
* {{R:la:L&S}}
* {{R:la:Gaffiot}}
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alt|en|legislatour||obsolete|rare}}
=== รากศัพท์ ===
จาก{{uder|en|la|lēgislātor}}
=== การออกเสียง ===
* {{IPA|en|/ˈlɛd͡ʒɪˌsleɪtə/|a=RP}}
* {{IPA|en|/ˈlɛd͡ʒɪˌsleɪtɚ/|[ˈlɛd͡ʒɪˌsleɪɾɚ]|a=GA,CA}}
** {{audio|en|LL-Q1860 (eng)-Flame, not lame-legislator.wav|a=Milwaukee,Wisconsin}}
* {{IPA|en|/ˈled͡ʒɪˌslæɪtə/|[ˈled͡ʒɪˌslæɪɾə]|a=AU}}
** {{audio|en|LL-Q1860 (eng)-Jjamesryan-legislator.wav|a=Brisbane}}
* {{IPA|en|/ˈled͡ʒəˌslæɪtə/|[ˈled͡ʒəˌslæɪɾə]|a=AU}}
=== คำนาม ===
{{en-noun}}
# ผู้ที่[[ออก]]หรือ[[ตรา]][[กฎหมาย]]
## โดยเฉพาะอย่างยิ่ง สมาชิกของ[[องค์กรนิติบัญญัติ]]; [[สมาชิก]][[รัฐสภา]], สมาชิก[[สภานิติบัญญัติ]]
##* {{RQ:Landon Ethel Churchill|page=249|volume=II|passage=Give the children of the poor that portion of education which will enable them to know their own resources; which will cultivate in them an onward-looking hope, and give them rational amusement in their leisure hours: this, and this only, will work out that moral revolution, which is the '''legislator''''s noblest purpose.|translation=จงมอบการศึกษาในส่วนที่จะช่วยให้บุตรหลานของผู้ยากไร้ได้ตระหนักรู้ถึงทรัพยากรของตนเอง ซึ่งจะช่วยปลูกฝังความหวังที่มองไปข้างหน้า และมอบความเพลิดเพลินอันมีเหตุผลในยามว่างแก่พวกเขา สิ่งนี้ และสิ่งนี้เท่านั้น ที่จะก่อให้เกิดการปฏิวัติทางศีลธรรม อันเป็นเป้าหมายอันสูงส่งที่สุดของ'''ผู้ออกกฎหมาย'''}}
##: {{syn|en|lawmaker}}
## โดยเฉพาะอย่างยิ่ง ไม่ได้หมายถึงสมาชิกราย[[บุคคล]]ขององค์กรนิติบัญญัติ แต่หมายถึง[[ผู้ร่าง]]กฎหมายในเชิงนามธรรมของ[[บทบัญญัติ]]หรือตัวบทกฎหมายที่เกี่ยวข้องสำหรับ{{w|การตีความกฎหมายลายลักษณ์อักษร|หลักการตีความกฎหมาย}}; [[ฝ่ายนิติบัญญัติ]], [[ผู้ร่างกฎหมาย]]
##: {{syn|en|lawmaker}}
==== ลูกคำ ====
{{col|en|colegislator|legislatorship|legislatress|nonlegislator
}}
=== คำสลับอักษร ===
* {{anagrams|en|a=aegillorst|allegorist}}
{{C|en|กฎหมาย|บุคคล}}
== ภาษาอินโดนีเซีย ==
{{wp|id:}}
=== รากศัพท์ ===
{{lbor|id|la|lēgislātor}}
=== การออกเสียง ===
* {{IPA|id|[lɛɡɪsˈlatɔr]}}
* {{hyphenation|id|lè|gis|la|tor}}
=== คำนาม ===
{{id-noun|head=lègislator}}
# ผู้ที่[[ออก]]หรือ[[ตรา]][[กฎหมาย]]
==== คำเกี่ยวข้อง ====
{{col|id
|legislatif
|legislasi
}}
=== อ่านเพิ่ม ===
* {{R:KBBI Daring}}
47g3cgzzk373ovcdneiy7s1g9kgcv2h
legislatori
0
2359461
5776184
2026-11-02T01:17:01Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด เรียงลำดับหัวเรื่องภาษา
5776184
wikitext
text/x-wiki
== ภาษาละติน ==
=== คำนาม ===
{{head|la|รูปนาม|head=lēgislātōrī}}
# {{inflection of|la|lēgislātor||dat|s}}
== ภาษาอิตาลี ==
=== คำนาม ===
{{head|it|รูปนาม|g=m}}
# {{plural of|it|legislatore}}
=== คำสลับอักษร ===
* {{anagrams|it|a=aegiillorst|allegoristi}}
j4zcq00j7ilnnv0mb1wug7py6ofiuct
first person
0
2359462
5776198
2026-11-02T02:22:11Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776198
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alter|en|first-person|1st person|Ist person}}
=== รากศัพท์ ===
{{calque|en|la|prīma persōna}}, แปลตรงตัวจาก{{der|en|grc|πρῶτον πρόσωπον}} อีกทอดหนึ่ง; แทนที่คำพื้นถิ่น {{noncog|ang|forma had}} ซึ่งแปลตามตัวอักษรว่า "[[รูป]][[สภาวะ]]"
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{senseid|en|grammar}}{{lb|en|ไวยากรณ์}} [[บุรุษที่หนึ่ง]] (รูปของคำสรรพนามหรือคำ[[กริยา]]ที่ใช้แทนผู้พูดหรือผู้เขียนของประโยคที่คำเหล่านั้นปรากฏ)
#: {{ux|en|"Am" is the '''first person''' singular of "to be".|"Am" คือรูปเอกพจน์'''บุรุษที่หนึ่ง'''ของคำว่า "to be"}}
#* {{quote-video game|en|developer=w:BioWare|title={{w|Mass Effect (video game)|Mass Effect}}|location=Redwood City|publisher=Electronic Arts|year=2008|system=PC|scene=Hanar Codex entry|isbn=9780784546642|oclc=246633669|passage=All hanar have two names. The Face Name is known to the world; the Soul Name is kept for use among close friends and relations. Hanar never refer to themselves in the '''first person''' in conversation with someone they know on a Face Name basis. To do so is considered egotistical, so instead they refer to themselves as "this one", or the impersonal "it".|translation=ชาวฮานาร์ทุกคนมีสองชื่อ ชื่อหน้า (Face Name) เป็นชื่อที่โลกรู้จัก ส่วนชื่อวิญญาณ (Soul Name) จะสงวนไว้ใช้เฉพาะในหมู่เพื่อนสนิทและญาติมิตร ชาวฮานาร์จะไม่เรียกแทนตัวเองด้วยสรรพนาม'''บุรุษที่หนึ่ง'''เลยในการสนทนากับคนที่รู้จักกันในระดับชื่อหน้า การทำเช่นนั้นถือเป็นการหลงตัวเอง พวกเขาจึงมักเรียกแทนตัวเองว่า "ผู้นี้" หรือใช้คำสรรพนามไม่ชี้เฉพาะว่า "มัน" แทน}}
# [[การเล่า]][[เรื่อง]]แบบบุรุษที่หนึ่ง (รูปแบบการเขียนคำบรรยายที่ใช้คำกริยาในบุรุษที่หนึ่ง เพื่อสร้างความรู้สึกว่าเหตุการณ์นั้นกำลังเกิดขึ้นกับตัวผู้เล่าเอง)
==== คำจ่ากลุ่ม ====
* {{l|en|grammatical person}}
==== คำเกี่ยวข้อง ====
* {{l|en|second person}}
* {{l|en|third person}}
==== ลูกคำ ====
{{col2|en|first-person dual|first-person plural|first-person shooter|first-person singular}}
lk0fo273ifr707ic2m41l0ivxhr4lie
5776199
5776198
2026-11-02T02:23:16Z
Octahedron80
267
/* คำนาม */
5776199
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alter|en|first-person|1st person|Ist person}}
=== รากศัพท์ ===
{{calque|en|la|prīma persōna}}, แปลตรงตัวจาก{{der|en|grc|πρῶτον πρόσωπον}} อีกทอดหนึ่ง; แทนที่คำพื้นถิ่น {{noncog|ang|forma had}} ซึ่งแปลตามตัวอักษรว่า "[[รูป]][[สภาวะ]]"
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{senseid|en|grammar}}{{lb|en|ไวยากรณ์}} [[บุรุษที่หนึ่ง]] (รูปของ[[คำสรรพนาม]]หรือ[[คำกริยา]]ที่ใช้แทนผู้[[พูด]]หรือผู้[[เขียน]]ของ[[ประโยค]]ที่คำเหล่านั้นปรากฏ)
#: {{ux|en|"Am" is the '''first person''' singular of "to be".|"Am" คือรูปเอกพจน์'''บุรุษที่หนึ่ง'''ของคำว่า "to be"}}
#* {{quote-video game|en|developer=w:BioWare|title={{w|Mass Effect (video game)|Mass Effect}}|location=Redwood City|publisher=Electronic Arts|year=2008|system=PC|scene=Hanar Codex entry|isbn=9780784546642|oclc=246633669|passage=All hanar have two names. The Face Name is known to the world; the Soul Name is kept for use among close friends and relations. Hanar never refer to themselves in the '''first person''' in conversation with someone they know on a Face Name basis. To do so is considered egotistical, so instead they refer to themselves as "this one", or the impersonal "it".|translation=ชาวฮานาร์ทุกคนมีสองชื่อ ชื่อหน้า (Face Name) เป็นชื่อที่โลกรู้จัก ส่วนชื่อวิญญาณ (Soul Name) จะสงวนไว้ใช้เฉพาะในหมู่เพื่อนสนิทและญาติมิตร ชาวฮานาร์จะไม่เรียกแทนตัวเองด้วยสรรพนาม'''บุรุษที่หนึ่ง'''เลยในการสนทนากับคนที่รู้จักกันในระดับชื่อหน้า การทำเช่นนั้นถือเป็นการหลงตัวเอง พวกเขาจึงมักเรียกแทนตัวเองว่า "ผู้นี้" หรือใช้คำสรรพนามไม่ชี้เฉพาะว่า "มัน" แทน}}
# [[การเล่า]][[เรื่อง]]แบบบุรุษที่หนึ่ง (รูปแบบการเขียน[[คำบรรยาย]]ที่ใช้คำกริยาในบุรุษที่หนึ่ง เพื่อสร้าง[[ความรู้สึก]]ว่า[[เหตุการณ์]]นั้นกำลังเกิดขึ้นกับตัวผู้เล่าเอง)
==== คำจ่ากลุ่ม ====
* {{l|en|grammatical person}}
==== คำเกี่ยวข้อง ====
* {{l|en|second person}}
* {{l|en|third person}}
==== ลูกคำ ====
{{col2|en|first-person dual|first-person plural|first-person shooter|first-person singular}}
gecbvc3p5yz9d3h5jwl8ytgce3njsin
5776200
5776199
2026-11-02T02:24:29Z
Octahedron80
267
/* คำนาม */
5776200
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alter|en|first-person|1st person|Ist person}}
=== รากศัพท์ ===
{{calque|en|la|prīma persōna}}, แปลตรงตัวจาก{{der|en|grc|πρῶτον πρόσωπον}} อีกทอดหนึ่ง; แทนที่คำพื้นถิ่น {{noncog|ang|forma had}} ซึ่งแปลตามตัวอักษรว่า "[[รูป]][[สภาวะ]]"
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{senseid|en|grammar}}{{lb|en|ไวยากรณ์}} [[บุรุษที่หนึ่ง]] (รูปของ[[คำสรรพนาม]]หรือ[[คำกริยา]]ที่ใช้แทนผู้[[พูด]]หรือผู้[[เขียน]]ของ[[ประโยค]]ที่คำเหล่านั้นปรากฏ)
#: {{ux|en|"Am" is the '''first person''' singular of "to be".|"Am" คือรูปเอกพจน์'''บุรุษที่หนึ่ง'''ของคำว่า "to be"}}
#* {{quote-video game|en|developer=w:BioWare|title={{w|Mass Effect (video game)|Mass Effect}}|location=Redwood City|publisher=Electronic Arts|year=2008|system=PC|scene=Hanar Codex entry|isbn=9780784546642|oclc=246633669|passage=All hanar have two names. The Face Name is known to the world; the Soul Name is kept for use among close friends and relations. Hanar never refer to themselves in the '''first person''' in conversation with someone they know on a Face Name basis. To do so is considered egotistical, so instead they refer to themselves as "this one", or the impersonal "it".|translation=ชาวฮานาร์ทุกคนมีสองชื่อ ชื่อหน้า (Face Name) เป็นชื่อที่โลกรู้จัก ส่วนชื่อวิญญาณ (Soul Name) จะสงวนไว้ใช้เฉพาะในหมู่เพื่อนสนิทและญาติมิตร ชาวฮานาร์จะไม่เรียกแทนตัวเองด้วยสรรพนาม'''บุรุษที่หนึ่ง'''เลยในการสนทนากับคนที่รู้จักกันในระดับชื่อหน้า การทำเช่นนั้นถือเป็นการหลงตัวเอง พวกเขาจึงมักเรียกแทนตัวเองว่า "ผู้นี้" หรือใช้คำสรรพนามไม่ชี้เฉพาะว่า "มัน" แทน}}
# [[การเล่า]][[เรื่อง]]แบบ[[บุคคล]]ที่หนึ่ง (รูปแบบการเขียน[[คำบรรยาย]]ที่ใช้คำกริยาในบุรุษที่หนึ่ง เพื่อสร้าง[[ความรู้สึก]]ว่า[[เหตุการณ์]]นั้นกำลังเกิดขึ้นกับตัวผู้เล่าเอง)
==== คำจ่ากลุ่ม ====
* {{l|en|grammatical person}}
==== คำเกี่ยวข้อง ====
* {{l|en|second person}}
* {{l|en|third person}}
==== ลูกคำ ====
{{col2|en|first-person dual|first-person plural|first-person shooter|first-person singular}}
27a6p8ypdfv3od6iy3b4pqhdtl51y31
5776208
5776200
2026-11-02T02:33:16Z
Octahedron80
267
/* รากศัพท์ */
5776208
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alter|en|first-person|1st person|Ist person}}
=== รากศัพท์ ===
{{calque|en|la|prīma persōna}}, แปลตรงตัวจาก{{der|en|grc|πρῶτον πρόσωπον}} อีกทอดหนึ่ง; แทนที่คำพื้นถิ่น {{noncog|ang|forma had|lit=[[รูป]][[สภาวะ]]}}
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{senseid|en|grammar}}{{lb|en|ไวยากรณ์}} [[บุรุษที่หนึ่ง]] (รูปของ[[คำสรรพนาม]]หรือ[[คำกริยา]]ที่ใช้แทนผู้[[พูด]]หรือผู้[[เขียน]]ของ[[ประโยค]]ที่คำเหล่านั้นปรากฏ)
#: {{ux|en|"Am" is the '''first person''' singular of "to be".|"Am" คือรูปเอกพจน์'''บุรุษที่หนึ่ง'''ของคำว่า "to be"}}
#* {{quote-video game|en|developer=w:BioWare|title={{w|Mass Effect (video game)|Mass Effect}}|location=Redwood City|publisher=Electronic Arts|year=2008|system=PC|scene=Hanar Codex entry|isbn=9780784546642|oclc=246633669|passage=All hanar have two names. The Face Name is known to the world; the Soul Name is kept for use among close friends and relations. Hanar never refer to themselves in the '''first person''' in conversation with someone they know on a Face Name basis. To do so is considered egotistical, so instead they refer to themselves as "this one", or the impersonal "it".|translation=ชาวฮานาร์ทุกคนมีสองชื่อ ชื่อหน้า (Face Name) เป็นชื่อที่โลกรู้จัก ส่วนชื่อวิญญาณ (Soul Name) จะสงวนไว้ใช้เฉพาะในหมู่เพื่อนสนิทและญาติมิตร ชาวฮานาร์จะไม่เรียกแทนตัวเองด้วยสรรพนาม'''บุรุษที่หนึ่ง'''เลยในการสนทนากับคนที่รู้จักกันในระดับชื่อหน้า การทำเช่นนั้นถือเป็นการหลงตัวเอง พวกเขาจึงมักเรียกแทนตัวเองว่า "ผู้นี้" หรือใช้คำสรรพนามไม่ชี้เฉพาะว่า "มัน" แทน}}
# [[การเล่า]][[เรื่อง]]แบบ[[บุคคล]]ที่หนึ่ง (รูปแบบการเขียน[[คำบรรยาย]]ที่ใช้คำกริยาในบุรุษที่หนึ่ง เพื่อสร้าง[[ความรู้สึก]]ว่า[[เหตุการณ์]]นั้นกำลังเกิดขึ้นกับตัวผู้เล่าเอง)
==== คำจ่ากลุ่ม ====
* {{l|en|grammatical person}}
==== คำเกี่ยวข้อง ====
* {{l|en|second person}}
* {{l|en|third person}}
==== ลูกคำ ====
{{col2|en|first-person dual|first-person plural|first-person shooter|first-person singular}}
g9necz7fwsh8l4zer3e3hhicpko6c60
5776209
5776208
2026-11-02T02:42:00Z
Octahedron80
267
/* รากศัพท์ */
5776209
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
{{wp|en:}}
=== รูปแบบอื่น ===
* {{alter|en|first-person|1st person|Ist person}}
=== รากศัพท์ ===
{{calque|en|la|prīma persōna}}, แปลตรงตัวจาก{{der|en|grc|πρῶτον πρόσωπον}} อีกทอดหนึ่ง; แทนที่คำพื้นถิ่น {{noncog|ang|forma had|lit=สภาวะที่ 1}}
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{senseid|en|grammar}}{{lb|en|ไวยากรณ์}} [[บุรุษที่หนึ่ง]] (รูปของ[[คำสรรพนาม]]หรือ[[คำกริยา]]ที่ใช้แทนผู้[[พูด]]หรือผู้[[เขียน]]ของ[[ประโยค]]ที่คำเหล่านั้นปรากฏ)
#: {{ux|en|"Am" is the '''first person''' singular of "to be".|"Am" คือรูปเอกพจน์'''บุรุษที่หนึ่ง'''ของคำว่า "to be"}}
#* {{quote-video game|en|developer=w:BioWare|title={{w|Mass Effect (video game)|Mass Effect}}|location=Redwood City|publisher=Electronic Arts|year=2008|system=PC|scene=Hanar Codex entry|isbn=9780784546642|oclc=246633669|passage=All hanar have two names. The Face Name is known to the world; the Soul Name is kept for use among close friends and relations. Hanar never refer to themselves in the '''first person''' in conversation with someone they know on a Face Name basis. To do so is considered egotistical, so instead they refer to themselves as "this one", or the impersonal "it".|translation=ชาวฮานาร์ทุกคนมีสองชื่อ ชื่อหน้า (Face Name) เป็นชื่อที่โลกรู้จัก ส่วนชื่อวิญญาณ (Soul Name) จะสงวนไว้ใช้เฉพาะในหมู่เพื่อนสนิทและญาติมิตร ชาวฮานาร์จะไม่เรียกแทนตัวเองด้วยสรรพนาม'''บุรุษที่หนึ่ง'''เลยในการสนทนากับคนที่รู้จักกันในระดับชื่อหน้า การทำเช่นนั้นถือเป็นการหลงตัวเอง พวกเขาจึงมักเรียกแทนตัวเองว่า "ผู้นี้" หรือใช้คำสรรพนามไม่ชี้เฉพาะว่า "มัน" แทน}}
# [[การเล่า]][[เรื่อง]]แบบ[[บุคคล]]ที่หนึ่ง (รูปแบบการเขียน[[คำบรรยาย]]ที่ใช้คำกริยาในบุรุษที่หนึ่ง เพื่อสร้าง[[ความรู้สึก]]ว่า[[เหตุการณ์]]นั้นกำลังเกิดขึ้นกับตัวผู้เล่าเอง)
==== คำจ่ากลุ่ม ====
* {{l|en|grammatical person}}
==== คำเกี่ยวข้อง ====
* {{l|en|second person}}
* {{l|en|third person}}
==== ลูกคำ ====
{{col2|en|first-person dual|first-person plural|first-person shooter|first-person singular}}
jw13c6pi8lbkfzcko1lpf9y8kke3m3w
first-person
0
2359463
5776201
2026-11-02T02:25:12Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776201
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first-person.wav|a=US}}
=== คำนาม ===
{{en-noun}}
# {{altform|en|first person}}
=== คำคุณศัพท์ ===
{{en-adj|-}}
# {{lb|en|ภาพยนตร์|โทรทัศน์|วิดีโอเกม}} [[มุมมอง]][[บุคคล]]ที่หนึ่ง (การนำเสนอภาพผ่านสายตาของ[[ตัวเอก]]หรือตัวละครหลักเอง)
==== ลูกคำ ====
* {{l|en|first-person shooter}}, [[first-person view]] ([[FPV]])
=== ดูเพิ่ม ===
* {{l|en|firsthand}}
* {{l|en|third person}}
{{C|en|Video game genres}}
jyzab111msvfsu6v4eazr1sjjh84onx
5776202
5776201
2026-11-02T02:25:46Z
Octahedron80
267
/* คำนาม */
5776202
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== การออกเสียง ===
* {{audio|en|LL-Q1860 (eng)-Flame, not lame-first-person.wav|a=US}}
=== คำนาม ===
{{en-noun|~}}
# {{altform|en|first person}}
=== คำคุณศัพท์ ===
{{en-adj|-}}
# {{lb|en|ภาพยนตร์|โทรทัศน์|วิดีโอเกม}} [[มุมมอง]][[บุคคล]]ที่หนึ่ง (การนำเสนอภาพผ่านสายตาของ[[ตัวเอก]]หรือตัวละครหลักเอง)
==== ลูกคำ ====
* {{l|en|first-person shooter}}, [[first-person view]] ([[FPV]])
=== ดูเพิ่ม ===
* {{l|en|firsthand}}
* {{l|en|third person}}
{{C|en|Video game genres}}
esa0l8ivcpm61l6db16i1pz8t4fdurc
1st person
0
2359464
5776203
2026-11-02T02:27:44Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776203
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== คำนาม ===
{{en-noun|?}}
# {{abbr of|en|first person}}
#: {{cot|en|2nd person|3rd person|4th person}}
b5ib229hq3o9ka1m3w6r1189xeej6qv
Ist person
0
2359465
5776204
2026-11-02T02:28:39Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776204
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== คำนาม ===
{{en-noun|?}}
# {{lb|en|พ้นสมัย}} {{alternative form of|en|first person}}
#* {{quote-journal |en |journal=Journal of Tamil Studies |volume=2 |issue=2 |year=1970 |text=Most writers on this subject conclude that the notions '''Ist person''', IInd person and IIIrd person are semantic primes, ie.., cannot be further broken down into other semantic components.|translation=ผู้เขียนส่วนใหญ่ในหัวข้อนี้สรุปว่ามโนทัศน์เรื่อง'''บุรุษที่ 1''', บุรุษที่ 2 และบุรุษที่ 3 เป็นหน่วยมูลฐานทางอรรถศาสตร์ กล่าวคือ ไม่สามารถจำแนกออกเป็นองค์ประกอบทางอรรถศาสตร์อื่นได้อีก}}
#* {{quote-journal |en |journal=Bulletin of the Deccan College Research Institute |volume=34 |year=1974 |page=35 |text=In H. singular '''Ist person''' form is different from the rest and in plural the IInd person form is different from the rest of the forms.|translation=ในภาษาฮินดี รูปเอกพจน์'''บุรุษที่ 1''' จะแตกต่างจากรูปอื่น ๆ และในรูปพหูพจน์ รูปบุรุษที่ 2 จะแตกต่างจากรูปที่เหลือ}}
#* {{quote-book |en |title=Ladakhi grammar |year=1979 |isbn=0896840522 |page=104 |text=Plural suffixes may be optionally added after the '''Ist person''' exclusive ''ƞ''ə—z̆e, '''Ist person''' inclusive ''ƞ''ə—tə''ƞ'', and IInd person non-honorific khyo—z̆e and honorific ñe—z̆e.|translation=อาจเลือกเติมปัจจัยพหูพจน์ต่อท้ายรูป'''บุรุษที่ 1''' แบบไม่รวมผู้ฟัง ''ƞ''ə—z̆e, '''บุรุษที่ 1''' แบบรวมผู้ฟัง ''ƞ''ə—tə''ƞ'', และรูปบุรุษที่ 2 แบบไม่ยกย่อง khyo—z̆e ตลอดจนแบบยกย่อง ñe—z̆e ได้}}
==== คำเกี่ยวข้อง ====
* {{l|en|IInd person}}
* {{l|en|IIIrd person}}
=== คำสลับอักษร ===
* {{anagrams|en|a=einoprsst|Peronists|Tres Pinos|nose strip|personist|porn sites|porn-sites|pornsites|pterosins}}
{{cln|en|terms containing Roman numerals}}
qg65fkf28e9lqndsuamcz0sf6792z5x
5776207
5776204
2026-11-02T02:31:46Z
Octahedron80
267
/* คำสลับอักษร */
5776207
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== คำนาม ===
{{en-noun|?}}
# {{lb|en|พ้นสมัย}} {{alternative form of|en|first person}}
#* {{quote-journal |en |journal=Journal of Tamil Studies |volume=2 |issue=2 |year=1970 |text=Most writers on this subject conclude that the notions '''Ist person''', IInd person and IIIrd person are semantic primes, ie.., cannot be further broken down into other semantic components.|translation=ผู้เขียนส่วนใหญ่ในหัวข้อนี้สรุปว่ามโนทัศน์เรื่อง'''บุรุษที่ 1''', บุรุษที่ 2 และบุรุษที่ 3 เป็นหน่วยมูลฐานทางอรรถศาสตร์ กล่าวคือ ไม่สามารถจำแนกออกเป็นองค์ประกอบทางอรรถศาสตร์อื่นได้อีก}}
#* {{quote-journal |en |journal=Bulletin of the Deccan College Research Institute |volume=34 |year=1974 |page=35 |text=In H. singular '''Ist person''' form is different from the rest and in plural the IInd person form is different from the rest of the forms.|translation=ในภาษาฮินดี รูปเอกพจน์'''บุรุษที่ 1''' จะแตกต่างจากรูปอื่น ๆ และในรูปพหูพจน์ รูปบุรุษที่ 2 จะแตกต่างจากรูปที่เหลือ}}
#* {{quote-book |en |title=Ladakhi grammar |year=1979 |isbn=0896840522 |page=104 |text=Plural suffixes may be optionally added after the '''Ist person''' exclusive ''ƞ''ə—z̆e, '''Ist person''' inclusive ''ƞ''ə—tə''ƞ'', and IInd person non-honorific khyo—z̆e and honorific ñe—z̆e.|translation=อาจเลือกเติมปัจจัยพหูพจน์ต่อท้ายรูป'''บุรุษที่ 1''' แบบไม่รวมผู้ฟัง ''ƞ''ə—z̆e, '''บุรุษที่ 1''' แบบรวมผู้ฟัง ''ƞ''ə—tə''ƞ'', และรูปบุรุษที่ 2 แบบไม่ยกย่อง khyo—z̆e ตลอดจนแบบยกย่อง ñe—z̆e ได้}}
==== คำเกี่ยวข้อง ====
* {{l|en|IInd person}}
* {{l|en|IIIrd person}}
=== คำสลับอักษร ===
* {{anagrams|en|a=einoprsst|Peronists|Tres Pinos|nose strip|personist|porn sites|porn-sites|pornsites|pterosins}}
{{cln|en|ศัพท์ที่มีเลขโรมัน}}
sm0wdkoe64yg6swtrs5n7sbidylmiy5
Ist
0
2359466
5776205
2026-11-02T02:30:37Z
Octahedron80
267
นำเข้าจาก enwikt เก็บกวาด
5776205
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== รากศัพท์ ===
จาก{{suffix|en|{{l|mul|I}}|pos1=เลขโรมัน 1|st}}
=== คำคุณศัพท์ ===
{{en-adj|-}}
# {{abbreviation of|en|first}}
#* {{quote-journal |en |journal=Philosophical Transactions of the Royal Society of London: Biological sciences |volume=184 |year=1894 |text=The reply from one large branch of the musculo-cutaneous in the forearm is abolished, but not the reply from the other. 3rd section, replies from musculo-cutaneous in forearm abolished; from '''Ist''' and IInd palmar and dorsal digitales collaterales reduced. 4th section, replies from '''Ist''', IInd, and IIIrd collateral digitals, both dorsal and plantar, abolished; from the IVth, Vth, and VIth dorsal collaterals obviously diminished; from the IVth palmar collateral obviously diminished.|translation=การตอบสนองจากแขนงใหญ่แขนงหนึ่งของเส้นประสาทมัสคูโลคิวเทเนียสในปลายแขนนั้นหมดไป แต่การตอบสนองจากอีกแขนงหนึ่งยังคงอยู่ ส่วนที่ 3 การตอบสนองจากเส้นประสาทมัสคูโลคิวเทเนียสในปลายแขนหมดไป การตอบสนองจากแขนงข้างด้านฝ่ามือและหลังมือที่ '''1''' และที่ 2 ลดลง ส่วนที่ 4 การตอบสนองจากแขนงข้างของนิ้วที่ '''1''', ที่ 2 และที่ 3 ทั้งด้านหลังมือและฝ่าเท้าหมดไป จากแขนงข้างด้านหลังเท้าที่ 4, ที่ 5 และที่ 6 ลดลงอย่างเห็นได้ชัด จากแขนงข้างด้านฝ่ามือที่ 4 ลดลงอย่างเห็นได้ชัด}}
#* {{quote-journal |en |journal=Scientific Papers |volume=1 |year=1907 |author=[[w:George Darwin|George Howard Darwin]] |page=178 |text=If each angle were multiplied by three, the eighteen entries of the 1<sup>st</sup> quadrant would be converted into three groups of six, lying in three quadrants, viz., '''I<sup>st</sup>''', II<sup>nd</sup>, III<sup>rd</sup>;|translation=หากแต่ละมุมถูกคูณด้วยสาม ค่าทั้งสิบแปดรายการของจตุภาคที่ 1 จะถูกแปลงเป็นสามกลุ่ม กลุ่มละหกรายการ ซึ่งอยู่ในสามจตุภาค ได้แก่ จตุภาคที่ '''1''', ที่ 2, ที่ 3}}
#* {{quote-book |en |title=The 1st and 2nd Battalions The Sherwood Foresters (Nottinghamshire and Derbyshire Regiment) in the Great War |year=1925 |publisher2=Andrews UK |location2=United Kingdom |year2=2011 |isbn2=1781513872 |author=w:H. C. Wylly |text=To the right or south of these was the First Army, under General Sir Douglas Haig—the Indian Corps about Festubert, and then in order the '''Ist''' and IVth Corps—a total of nine divisions, or approximately 180,000 men, the two armies thus extending from north of Ypres to north of Arras, and mustering together some 440,000 men.|translation=ทางขวาหรือทางใต้ของกองกำลังเหล่านี้คือกองทัพที่หนึ่ง ภายใต้การบังคับบัญชาของพลเอก เซอร์ ดักลาส เฮก — กองพลน้อยอินเดียอยู่บริเวณแฟ็สตูว์แบร์ และถัดมาตามลำดับคือกองทัพน้อยที่ '''1''' และที่ 4 — รวมเป็นเก้ากองพล หรือประมาณ 180,000 นาย สองกองทัพจึงแผ่ขยายตั้งแต่ทางเหนือของอีพร์ไปจนถึงทางเหนือของอารัส และรวมกำลังพลเข้าด้วยกันได้ราว 440,000 นาย}}
#* {{quote-book |en |title=Comprehensive Objective Book for AIEEE (All India Engineering Entrance Examination) and Other Engineering Entrance Examinations |year=2006 |isbn=8179680231 |author=Narinder Kumar; Dr. J. K. Juneja; Dr. N. K. Verma; Dr. Kulbhushan Prakash |text=(d) No. of ways of attempting '''Ist''', IInd, IIIrd questions are 4 each.|translation=(ง) จำนวนวิธีในการเลือกทำข้อสอบข้อที่ '''1''', ที่ 2, ที่ 3 คือข้อละ 4 วิธี}}
#* {{quote-book |en |title=The Pearson Guide to the NDA Examination |author=Edgar Thorpe; Showick Thorpe |isbn=8131702014 |month=September |year=2007 |publisher=w:Pearson Education |text=13. Total number of ways of selecting four shoes (in order '''Ist''', IInd, IIIrd, IVth) is 20 × 19 × 18 × 17|translation=13. จำนวนวิธีทั้งหมดในการเลือกรองเท้าสี่ข้าง (ตามลำดับข้างที่ '''1''', ที่ 2, ที่ 3, ที่ 4) คือ 20 × 19 × 18 × 17}}
==== ดูเพิ่ม ====
* ถัดไป: [[IInd]]
=== คำสลับอักษร ===
* {{anagrams|en|a=ist|SIT|'tis|Sit|its|its'|TIs|t'is|tis|STI|TIS|sit|'its|it's|TSI|ITS|S.T.I.}}
{{cln|en|ordinal numbers|terms containing Roman numerals}}
0ymt5navnxmey3rm4is4yv83dvqpyh8
5776206
5776205
2026-11-02T02:31:23Z
Octahedron80
267
/* คำสลับอักษร */
5776206
wikitext
text/x-wiki
== ภาษาอังกฤษ ==
=== รากศัพท์ ===
จาก{{suffix|en|{{l|mul|I}}|pos1=เลขโรมัน 1|st}}
=== คำคุณศัพท์ ===
{{en-adj|-}}
# {{abbreviation of|en|first}}
#* {{quote-journal |en |journal=Philosophical Transactions of the Royal Society of London: Biological sciences |volume=184 |year=1894 |text=The reply from one large branch of the musculo-cutaneous in the forearm is abolished, but not the reply from the other. 3rd section, replies from musculo-cutaneous in forearm abolished; from '''Ist''' and IInd palmar and dorsal digitales collaterales reduced. 4th section, replies from '''Ist''', IInd, and IIIrd collateral digitals, both dorsal and plantar, abolished; from the IVth, Vth, and VIth dorsal collaterals obviously diminished; from the IVth palmar collateral obviously diminished.|translation=การตอบสนองจากแขนงใหญ่แขนงหนึ่งของเส้นประสาทมัสคูโลคิวเทเนียสในปลายแขนนั้นหมดไป แต่การตอบสนองจากอีกแขนงหนึ่งยังคงอยู่ ส่วนที่ 3 การตอบสนองจากเส้นประสาทมัสคูโลคิวเทเนียสในปลายแขนหมดไป การตอบสนองจากแขนงข้างด้านฝ่ามือและหลังมือที่ '''1''' และที่ 2 ลดลง ส่วนที่ 4 การตอบสนองจากแขนงข้างของนิ้วที่ '''1''', ที่ 2 และที่ 3 ทั้งด้านหลังมือและฝ่าเท้าหมดไป จากแขนงข้างด้านหลังเท้าที่ 4, ที่ 5 และที่ 6 ลดลงอย่างเห็นได้ชัด จากแขนงข้างด้านฝ่ามือที่ 4 ลดลงอย่างเห็นได้ชัด}}
#* {{quote-journal |en |journal=Scientific Papers |volume=1 |year=1907 |author=[[w:George Darwin|George Howard Darwin]] |page=178 |text=If each angle were multiplied by three, the eighteen entries of the 1<sup>st</sup> quadrant would be converted into three groups of six, lying in three quadrants, viz., '''I<sup>st</sup>''', II<sup>nd</sup>, III<sup>rd</sup>;|translation=หากแต่ละมุมถูกคูณด้วยสาม ค่าทั้งสิบแปดรายการของจตุภาคที่ 1 จะถูกแปลงเป็นสามกลุ่ม กลุ่มละหกรายการ ซึ่งอยู่ในสามจตุภาค ได้แก่ จตุภาคที่ '''1''', ที่ 2, ที่ 3}}
#* {{quote-book |en |title=The 1st and 2nd Battalions The Sherwood Foresters (Nottinghamshire and Derbyshire Regiment) in the Great War |year=1925 |publisher2=Andrews UK |location2=United Kingdom |year2=2011 |isbn2=1781513872 |author=w:H. C. Wylly |text=To the right or south of these was the First Army, under General Sir Douglas Haig—the Indian Corps about Festubert, and then in order the '''Ist''' and IVth Corps—a total of nine divisions, or approximately 180,000 men, the two armies thus extending from north of Ypres to north of Arras, and mustering together some 440,000 men.|translation=ทางขวาหรือทางใต้ของกองกำลังเหล่านี้คือกองทัพที่หนึ่ง ภายใต้การบังคับบัญชาของพลเอก เซอร์ ดักลาส เฮก — กองพลน้อยอินเดียอยู่บริเวณแฟ็สตูว์แบร์ และถัดมาตามลำดับคือกองทัพน้อยที่ '''1''' และที่ 4 — รวมเป็นเก้ากองพล หรือประมาณ 180,000 นาย สองกองทัพจึงแผ่ขยายตั้งแต่ทางเหนือของอีพร์ไปจนถึงทางเหนือของอารัส และรวมกำลังพลเข้าด้วยกันได้ราว 440,000 นาย}}
#* {{quote-book |en |title=Comprehensive Objective Book for AIEEE (All India Engineering Entrance Examination) and Other Engineering Entrance Examinations |year=2006 |isbn=8179680231 |author=Narinder Kumar; Dr. J. K. Juneja; Dr. N. K. Verma; Dr. Kulbhushan Prakash |text=(d) No. of ways of attempting '''Ist''', IInd, IIIrd questions are 4 each.|translation=(ง) จำนวนวิธีในการเลือกทำข้อสอบข้อที่ '''1''', ที่ 2, ที่ 3 คือข้อละ 4 วิธี}}
#* {{quote-book |en |title=The Pearson Guide to the NDA Examination |author=Edgar Thorpe; Showick Thorpe |isbn=8131702014 |month=September |year=2007 |publisher=w:Pearson Education |text=13. Total number of ways of selecting four shoes (in order '''Ist''', IInd, IIIrd, IVth) is 20 × 19 × 18 × 17|translation=13. จำนวนวิธีทั้งหมดในการเลือกรองเท้าสี่ข้าง (ตามลำดับข้างที่ '''1''', ที่ 2, ที่ 3, ที่ 4) คือ 20 × 19 × 18 × 17}}
==== ดูเพิ่ม ====
* ถัดไป: [[IInd]]
=== คำสลับอักษร ===
* {{anagrams|en|a=ist|SIT|'tis|Sit|its|its'|TIs|t'is|tis|STI|TIS|sit|'its|it's|TSI|ITS|S.T.I.}}
{{cln|en|ordinal numbers|ศัพท์ที่มีเลขโรมัน}}
f8zhl8x7womoouhw1r997iwyd9ao5cg
คุยกับผู้ใช้:Thitiporn Pachsungneon
3
2359467
5776210
2026-11-02T04:14:34Z
New user message
2698
เพิ่ม[[Template:Welcome|สารต้อนรับ]]ในหน้าคุยของผู้ใช้ใหม่
5776210
wikitext
text/x-wiki
{{Template:Welcome|name=Thitiporn Pachsungneon}}
-- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 11:14, 2 พฤศจิกายน 2569 (+07)
rmqhuxtjqfox0yl9z4c0q18nzr34w0u
คุยกับผู้ใช้:ซุปเปอร์คัพ ดรีมตัวลาย
3
2359468
5776211
2026-11-02T05:05:55Z
New user message
2698
เพิ่ม[[Template:Welcome|สารต้อนรับ]]ในหน้าคุยของผู้ใช้ใหม่
5776211
wikitext
text/x-wiki
{{Template:Welcome|name=ซุปเปอร์คัพ ดรีมตัวลาย}}
-- [[ผู้ใช้:New user message|New user message]] ([[คุยกับผู้ใช้:New user message|คุย]]) 12:05, 2 พฤศจิกายน 2569 (+07)
fh7eluambtfsz3vzqhtfnq4dz8nma1y
ฝนราชการ
0
2359469
5776213
2026-11-02T10:08:47Z
Octahedron80
267
สร้างหน้าด้วย "== ภาษาไทย == === รากศัพท์ === {{com+|th|ฝน|ราชการ}} === การออกเสียง === {th-pron|ฝน-ราด-ชะ-กาน}} === คำนาม === {{th-noun}} # {{lb|th|ปาก|ขำขัน}} [[ปรากฏการณ์]][[ฝน]][[ตก]][[หนัก]][[ใน]][[ช่วง]][[เย็น]] [[ซึ่ง]][[ตรง]][[กับ]][[เวลา]][[เลิก]]..."
5776213
wikitext
text/x-wiki
== ภาษาไทย ==
=== รากศัพท์ ===
{{com+|th|ฝน|ราชการ}}
=== การออกเสียง ===
{th-pron|ฝน-ราด-ชะ-กาน}}
=== คำนาม ===
{{th-noun}}
# {{lb|th|ปาก|ขำขัน}} [[ปรากฏการณ์]][[ฝน]][[ตก]][[หนัก]][[ใน]][[ช่วง]][[เย็น]] [[ซึ่ง]][[ตรง]][[กับ]][[เวลา]][[เลิก]][[งาน]][[ของ]][[ข้าราชการ]] [[พนักงาน]][[ออฟฟิศ]] [[และ]][[นักเรียน]]
shhmi39an7x2pqqhkmhiifa52zvedry
5776214
5776213
2026-11-02T10:08:57Z
Octahedron80
267
/* การออกเสียง */
5776214
wikitext
text/x-wiki
== ภาษาไทย ==
=== รากศัพท์ ===
{{com+|th|ฝน|ราชการ}}
=== การออกเสียง ===
{{th-pron|ฝน-ราด-ชะ-กาน}}
=== คำนาม ===
{{th-noun}}
# {{lb|th|ปาก|ขำขัน}} [[ปรากฏการณ์]][[ฝน]][[ตก]][[หนัก]][[ใน]][[ช่วง]][[เย็น]] [[ซึ่ง]][[ตรง]][[กับ]][[เวลา]][[เลิก]][[งาน]][[ของ]][[ข้าราชการ]] [[พนักงาน]][[ออฟฟิศ]] [[และ]][[นักเรียน]]
5g2jqrtaucgyhpypokl1tfqfzx4itvc
5776215
5776214
2026-11-02T10:10:24Z
Octahedron80
267
/* คำนาม */
5776215
wikitext
text/x-wiki
== ภาษาไทย ==
=== รากศัพท์ ===
{{com+|th|ฝน|ราชการ}}
=== การออกเสียง ===
{{th-pron|ฝน-ราด-ชะ-กาน}}
=== คำนาม ===
{{th-noun}}
# {{lb|th|ปาก|ขำขัน}} [[ปรากฏการณ์]][[ฝน]][[ตก]][[หนัก]][[ใน]][[ช่วง]][[เย็น]] [[ซึ่ง]][[ตรง]][[กับ]][[เวลา]][[เลิก]][[งาน]][[ของ]][[ข้าราชการ]] [[พนักงาน]][[ออฟฟิศ]] [[และ]][[นักเรียน]]
#: {{syn|th|ฝนเลิกงาน}}
tmwbh0h0i2hqw1llfn7lib019btteyu
ฝนเลิกงาน
0
2359470
5776216
2026-11-02T10:10:51Z
Octahedron80
267
สร้างหน้าด้วย "== ภาษาไทย == === รากศัพท์ === {{com+|th|ฝน|เลิก|งาน}} === การออกเสียง === {{th-pron|ฝน-เลิก-งาน}} === คำนาม === {{th-noun}} # {{lb|th|ปาก|ขำขัน}} {{synonym of|th|ฝนเลิกงาน}}"
5776216
wikitext
text/x-wiki
== ภาษาไทย ==
=== รากศัพท์ ===
{{com+|th|ฝน|เลิก|งาน}}
=== การออกเสียง ===
{{th-pron|ฝน-เลิก-งาน}}
=== คำนาม ===
{{th-noun}}
# {{lb|th|ปาก|ขำขัน}} {{synonym of|th|ฝนเลิกงาน}}
joi8b4fl5hjmxob5grhjlsgzh3e1owu
5776217
5776216
2026-11-02T10:11:03Z
Octahedron80
267
/* คำนาม */
5776217
wikitext
text/x-wiki
== ภาษาไทย ==
=== รากศัพท์ ===
{{com+|th|ฝน|เลิก|งาน}}
=== การออกเสียง ===
{{th-pron|ฝน-เลิก-งาน}}
=== คำนาม ===
{{th-noun}}
# {{lb|th|ปาก|ขำขัน}} {{synonym of|th|ฝนราชการ}}
836lrc28uxfudsponclyegu3t49difc