local module_path = 'Module:grc-decl/decl'
local m_decl_static_data = mw.loadData(module_path .. '/classes')
local m_paradigms = mw.loadData(module_path .. '/staticdata/paradigms')
local m_dialect_groups = mw.loadData(module_path .. '/staticdata/dialects')
local m_accent = require('Module:grc-accent')
local m_links = require('Module:links')
local m_str_utils = require("Module:string utilities")
local check_type = require('libraryUtil').checkType
local grc = require('Module:languages').getByCode('grc')
local grk_pro = require('Module:languages').getByCode('grk-pro')
local codepoint = m_str_utils.codepoint
local deep_copy = require('Module:table').deepCopy
local to_NFC = mw.ustring.toNFC
local to_NFD = mw.ustring.toNFD
local ufind = m_str_utils.find
local umatch = m_str_utils.match
local usub = m_str_utils.sub
local export = {
inflections = {},
adjinflections = m_decl_static_data.adjinflections,
adjinflections_con = m_decl_static_data.adjinflections_con,
}
local function ine(var)
if var == '' then
return nil
else
return var
end
end
local function quote(text)
return '“' .. text .. '”'
end
local function mention(alt)
return m_links.full_link({ alt = alt, lang = grc }, 'term')
end
local function proto_mention(alt)
return m_links.full_link({ alt = alt, lang = grk_pro }, 'term')
end
local dental_lookup = {
= '*-ts',
= '*-ds',
= '*-tʰs',
}
-- Return one of the three options, or an error message.
-- suffix is true if args does not contain an accent mark and begins in a hyphen.
local function accent_switch(accent_term, if_oxytone, if_perispomenon, otherwise, error_message, unaccented_suffix)
local value
-- accent_term can be nil if this is a paradigm for an unaccented_suffix.
check_type('accent_term', 1, accent_term, 'string', unaccented_suffix)
if accent_term == 'oxytone' then
value = if_oxytone
elseif accent_term == 'perispomenon' then
value = if_perispomenon
else
value = otherwise
end
return value or error_message and error(error_message)
end
local function uncontracted_error(contracted, dialect, nom_ending)
if contracted == false and
(dialect == 'att' or dialect == 'koi' or dialect == 'byz') then
--]
require('Module:debug').track('grc-decl/uncontracted error')
mw.log('In the Attic, Koine, or Byzantine dialects, nouns in ' ..
quote(nom_ending) .. ' are contracted. Remove ' ..
quote('con') .. ' from the ' ..
quote('form') .. ' parameter or change the dialect.')
end
end
local function nonrecessive_error(accent_term, error_message)
if accent_term == 'oxytone' or accent_term == 'perispomenon' then
error(error_message)
end
end
local function some(t, func)
for _, item in ipairs(t) do
if func(item) then
return true
end
end
return false
end
local function dial_condition(actual_dialect, dialect1, dialect2)
if not actual_dialect then
return false
end
if type(dialect1) == "string" then
return actual_dialect == dialect1 or
dialect2 and actual_dialect == dialect2 or
m_dialect_groups and m_dialect_groups
elseif type(dialect1) == "table" then
return some(dialect1, function(dialect)
return dial_condition(actual_dialect, dialect)
end)
end
end
local function dial_form_multi(args, forms, dialects)
local actual_dialect = args.dial
if not actual_dialect then
return
end
local ctable = args.ctable
-- Forms is an array of tables containing individual forms in
-- the same dialect or dialects:
-- { <case–number>, <form> }
-- Dialects is a sequence of strings or tables:
-- "<dialect code>"
-- or
-- { "<dialect code 1>", "<dialect code 2>", ... }
if dialects then
if dial_condition(actual_dialect, dialects) then
for _, form in ipairs(forms) do
if type(form) == 'string' then
ctable] = form
end
end
end
-- Forms is a sequence of tables:
-- { <case–number>, <form>, <dialect code> }
-- or
-- { <case–number>, <form>, { <dialect code 1>, <dialect code 2>, ... } }
elseif type(forms) == 'table' then
for _, form_data in ipairs(forms) do
if dial_condition(actual_dialect, form_data) and type(form_data) == 'string' then
ctable] = form_data
end
end
end
end
local function dial_form(args, f, suffix, dialect1, dialect2)
if dial_condition(args.dial, dialect1, dialect2) then
args.ctable = suffix
end
end
-- Should Aeolic be moved here?
local function dial_forms_first(args)
dial_form_multi(args, {
{ 'GD', '$αι(ῐ)ν/$ῃῐν' },
{ 'GP', '$ᾱ́ων/$έ͜ων/$ῶν' },
{ 'DP', '$ῃσῐ(ν)/$ῃς/$αις' },
},
'epi')
dial_form_multi(args, {
{ 'GP', '$έων/$ῶν', 'ion' },
{ 'DS', '$η', 'boi' }, -- 104.3
{ 'DS', '$αι', { 'ara', 'ele' } },
{ 'DS', '$ᾱ', { 'the', 'les' } },
{ 'GD', '$αίαιρ', 'ele' },
{ 'GP', '$έων/$ῶν', 'ion' }, -- 104.6
{ 'GP', '$ᾶν', 'nonIA' },
{ 'GP', '$ᾱ́ων', 'boi' },
{ 'DP', '$ῃσῐ(ν)', 'ion' }, -- 104.7, ato must be dealt with separately
{ 'DP', '$αισῐ(ν)', 'les' },
{ 'DP', '$ᾱσῐ(ν)', 'ato' },
{ 'DP', '$ῃσῐ(ν)', 'ion' },
{ 'AP', '$ᾰς', 'buck78' }, -- 104.8
{ 'AP', '$ᾰνς', { 'kre', 'arg' } },
{ 'AP', '$αις', 'les' },
{ 'AP', '$αιρ', 'ele' }, })
end
local function dial_forms_first_oxy(args)
dial_form_multi(args, {
{ 'DP', '$ῇσῐ(ν)/$ῇς/$αῖς' },
{ 'GD', '$αῖ(ῐ)ν/$ῇῐν' },
{ 'GP', '$ᾱ́ων/$έ͜ων/$ῶν' },
},
'epi')
dial_form_multi(args, {
{ 'DS', '$η', 'boi' }, -- 104.3
{ 'DS', '$αι', { 'ara', 'ele' } },
{ 'DS', '$ᾱ', { 'the', 'les' } },
{ 'GP', '$έων/$ῶν', 'ion' }, -- 104.6
{ 'GP', '$ᾶν', 'nonIA' },
{ 'GP', '$ᾱ́ων', 'boi' },
{ 'DP', '$ῃσῐ(ν)', 'ion' }, -- 104.7, ato must be dealt with separately
{ 'DP', '$αισῐ(ν)', 'les' },
{ 'DP', '$ᾶσῐ(ν)', 'ato' },
{ 'AP', '$ᾰς', 'buck78' }, -- 104.8
{ 'AP', '$ᾰνς', { 'kre', 'arg' } },
{ 'AP', '$αις', 'les' },
{ 'AP', '$αιρ', 'ele' },
{ 'GD', '$αίαιρ', 'ele' }, })
end
export.inflections = function(args)
local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
if args.accent.term == 'oxytone' then
dial_forms_first_oxy(args)
else
if args.adjective then
args.ctable = '$ων'
end
if args.accent.term ~= 'perispomenon' then
dial_forms_first(args)
end
end
end
--[=[
Only difference from "1st-alp-pax" and "1st-eta-pax" is that because of the "-prx",
] doesn't shift the accent to the end of the stem
in all forms. Used only in 1st-and-2nd-declension adjectives.
]=]
-- export.inflections = export.inflections
-- export.inflections = export.inflections
export.inflections = function(args)
local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
if args.accent.term == 'oxytone' then
dial_form(args, 'DP', '$ῆσῐ(ν)', 'ato')
-- Assuming this applies for perispomenon as well as oxytone.
dial_forms_first_oxy(args)
elseif args.accent.term ~= 'perispomenon' then
if args.adjective then
args.ctable = '$ων'
end
dial_form(args, 'DP', '$ησῐ(ν)', 'ato')
dial_forms_first(args)
end
end
-- Always recessive, except in adjectives or derivatives of them.
local function short_alpha(code)
return function(args)
if code == 'als' then
-- Ionic and Epic have eta, not alpha, in all forms except the
-- nominative and accusative singular.
if args.dial == 'ion' or args.dial == 'epi' then
mw.logObject(args, 'args')
short_alpha('ets')(args)
return
end
elseif code ~= 'ets' then
error('Invalid code ' .. tostring(code))
end
local accent_on_ultima = args.adjective and '_prx' or nil
local suffix = accent_switch(args.accent.term,
accent_on_ultima, accent_on_ultima, '_prx',
'First-declension feminine nouns with nominative singular in ' ..
'short alpha must have recessive accent.', args.suffix)
args.ctable = deep_copy(m_paradigms)
dial_forms_first(args)
if code == 'als' then
dial_form(args, 'DP', '$ᾱσῐ(ν)', 'ato')
else
dial_form(args, 'DP', '$ησῐ(ν)', 'ato')
end
end
end
export.inflections = short_alpha('als')
export.inflections = short_alpha('ets')
export.inflections = function(args)
local suffix = accent_switch(args.accent.term, nil, '_con', '_pax',
'Oxytone masculine first declension not supported.', args.suffix)
args.ctable = deep_copy(m_paradigms)
if args.accent.term ~= 'perispomenon' then
dial_forms_first(args)
dial_form_multi(args, {
{ 'GS', '$ᾱο/$ε͜ω/$ω', 'epi' },
{ 'GS', '$εω/$ω', 'ion' },
{ 'GS', '$ᾱ', 'nonIA' },
{ 'GS', '$ᾱο', 'boi' },
{ 'GS', '$ᾱυ', 'ark' }, })
end
end
export.inflections = function(args)
local suffix = accent_switch(args.accent.term, '', '_con', '_pax', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
-- ], genitive singular ἰδιώτεω, not *ἰδιωτέω,
-- at least in Herodotus 1.123.
if args.dial == 'ion' or args.dial == 'epi' then
args.synaeresis = true
end
if args.accent.term == 'oxytone' then
dial_forms_first_oxy(args)
dial_form_multi(args, {
{ 'GS', '$ᾶο/$έ͜ω/$ῶ', 'epi' },
{ 'GS', '$έω/$ῶ', 'ion' },
{ 'GS', '$ᾶ', 'nonIA' },
{ 'GS', '$ᾶο', 'boi' },
{ 'GS', '$ᾶυ', 'ark' }, })
elseif args.accent.term ~= 'perispomenon' then
dial_forms_first(args)
dial_form_multi(args, {
{ 'GS', '$ᾱο/$ε͜ω/$ω', 'epi' },
{ 'GS', '$εω/$ω', 'ion' },
{ 'GS', '$ᾱ', 'nonIA' },
{ 'GS', '$ᾱο', 'boi' },
{ 'GS', '$ᾱυ', 'ark' }, })
end
if args == 'α' or usub(args.stem, -1) == 'τ' then
args.ctable = '$ᾰ'
end
end
local function dial_forms_second(args)
dial_form_multi(args, {
{ 'GS', '$ου/$οῖο/$οιο/$όο/$οο' },
{ 'GD', '$οιῐν' },
{ 'DP', '$οισῐ(ν)/$οις' },
},
'epi')
dial_form_multi(args, {
{ 'GS', '$οιο', 'dor' },
{ 'GS', '$οι', 'the' }, -- 106.1
{ 'GS', '$ω', 'severe', 'boi' },
{ 'GS', '$ων', 'kyp' },
{ 'DS', '$ω', 'les' }, -- 106.2
{ 'DS', '$ου', 'the' },
{ 'DS', '$οι', { 'ele', 'boi' } },
{ 'DS', '$οι', { 'ara', 'eub' } },
{ 'DP', '$οισῐ(ν)/$οις', { 'ato', 'ion' } }, -- 106.4
{ 'DP', '$οισῐ(ν)', 'les' },
{ 'AP', '$ως', 'severe' }, -- 106.5
{ 'AP', '$ος', 'buck78' },
{ 'AP', '$ονς', { 'kre', 'arg' } },
{ 'AP', '$οις', 'les' },
{ 'AP', '$οιρ', 'ele' },
{ 'GD', '$οίοιρ', 'ele' }, -- 106.6
{ 'ND', '$ου', 'the' }, -- 23
{ 'GP', '$ουν', 'the' }, })
end
local function dial_forms_second_oxy(args)
dial_form_multi(args, {
{ 'GS', '$οῦ/$οῖο/$όο' },
{ 'GD', '$οῖῐν' },
{ 'DP', '$οῖσῐ(ν)/$οῖς' },
},
'epi')
dial_form_multi(args, {
{ 'GS', '$οῖο', 'dor' },
{ 'GS', '$οῖ', 'the' }, -- 106.1
{ 'GS', '$ῶ', 'severe', 'boi' },
{ 'GS', '$ῶν', 'kyp' },
{ 'DS', '$ω', 'les' }, -- 106.2
{ 'DS', '$οῦ', 'the' },
{ 'DS', '$οῖ', { 'ele', 'boi' } },
{ 'DS', '$οῖ', { 'ara', 'eub' } },
{ 'DP', '$οῖσῐ(ν)/$οῖς', { 'ato', 'ion' } }, -- 106.4
{ 'DP', '$οισῐ(ν)', 'les' },
{ 'AP', '$ώς', 'severe' }, -- 106.5
{ 'AP', '$ός', 'buck78' },
{ 'AP', '$όνς', { 'kre', 'arg' } },
{ 'AP', '$οίς', 'les' },
{ 'AP', '$οίρ', 'ele' },
{ 'GD', '$οίοιρ', 'ele' }, -- 106.6
{ 'ND', '$ού', 'the' }, -- 23
{ 'GP', '$οῦν', 'the' }, })
end
-- For nouns like λόγος and ἔργον.
local function second_basic(gender)
return function(args)
local suffix = accent_switch(args.accent.term, '', nil, '_prx',
'Perispomenon basic second-declension nouns not supported.', args.suffix)
args.ctable = deep_copy(m_paradigms)
if args.accent.term == 'oxytone' then
dial_forms_second_oxy(args)
else
dial_forms_second(args)
end
end
end
export.inflections = second_basic()
export.inflections = second_basic('_N')
local function second_contracted(gender)
return function(args)
local suffix = accent_switch(args.accent.term, nil, '', '_pax',
'Oxytone contracted second-declension nouns are not supported.', args.suffix)
if gender == '_N' and suffix == '_pax' then
error('Paroxytone contracted neuter second-declension nouns are not supported')
end
args.ctable = deep_copy(m_paradigms)
end
end
export.inflections = second_contracted()
export.inflections = second_contracted('_N', true)
local function second_Attic(gender)
return function(args)
local suffix = accent_switch(args.accent.term, '', '_con', '_prx', nil, args.suffix)
if gender == '_N' and suffix == '_con' then
error('Perispomenon Attic declension neuter nouns are not supported.')
end
args.force_antepenult = true
args.ctable = deep_copy(m_paradigms)
end
end
export.inflections = second_Attic()
export.inflections = second_Attic('_N')
--[[
In order:
- vowel before ντ at end of stem, or declension category
- masculine nominative singular ending
- neuter nominative singular ending
- segment preceding ῐ in dative plural
]]
local nt = {
= { 'ων', 'ον', 'ουσ' }, -- ἔχων (ἔχω)
= { 'ους', 'ον', 'ουσ' }, -- δούς (δίδωμι)
= { 'ων', 'ουν', 'ουσ' }, -- ποιῶν (ποιέω), δηλῶν (δηλόω)
= { 'εις', 'εν', 'εισ' }, -- θείς (τίθημι), ἀχθείς (ἄγω)
= { 'ων', 'ων', 'ωσ' }, -- ζῶν (ζάω)
}
-- Get nominative singular and dative plural (minus ῐ(ν)) for ντ-stems.
local function handle_nt(base, decl_type, gender, is_neuter, is_neuter_noun, is_adjective, arg1, arg2)
local NS, DP
local m_data = mw.loadData('Module:grc-utilities/data')
base = to_NFD(base)
-- Here we must match α, ε, ο, υ, ω, or ου (with any number of
-- diacritics) followed by ντ.
local whole_match, vowel, diacritic = umatch(base,
'((ου)(' .. m_data.combining_diacritic .. '*)ντ)$')
if not whole_match then
whole_match, vowel, diacritic = umatch(base,
'(()(' .. m_data.combining_diacritic .. '*)ντ)$')
end
if not whole_match then
error('Something is wrong with the stem ' .. base .. '.')
end
local nom_base = base:gsub(whole_match, '')
-- Declension type has priority over vowel because δούς (δοντ-) and
-- λαβών (λαβόντ-) have the same stem vowel, but different masculine
-- nominative singulars.
local data = nt or nt
if data then
if is_adjective then
NS = nom_base .. (is_neuter and data or data)
end
DP = nom_base .. data
else
local macron, breve = m_data.diacritics.macron, m_data.diacritics.breve
if diacritic == macron or diacritic == breve or diacritic == '' then
if vowel then
local intermediate_base = nom_base .. vowel
DP = intermediate_base .. macron .. 'σ'
if is_adjective then
if is_neuter then
NS = intermediate_base .. breve .. 'ν'
else
NS = intermediate_base .. macron .. 'ς'
end
end
else
error('Failed to generate nominative singular and dative plural for ' ..
quote(arg1) .. ', ' .. quote(arg2) .. '.')
end
else
error('Invalid diacritic ' ..
(type(diacritic) == 'string' and string.format("U+%X", codepoint(diacritic)) or tostring(diacritic)) ..
') in the stem ' .. base .. '.')
end
end
if not adjective and not gender then
gender = 'M'
gender.M = true
end
return NS, DP
end
local finals = {
= 'ψ',
= 'ξ',
= 'ξ',
= 'ξ',
= 'ψ',
= 'ψ',
= 'ξ',
--[[
= ,
= ,
= ,
--]]
}
local function new_gender(gender)
return { gender, = true }
end
-- is_neuter: boolean
-- accent_alternating: boolean
local function third_nom(args, is_neuter, accent_alternating)
-- strings
local arg1, arg2, base = args, args, to_NFC(args.stem)
local ctable = args.ctable
-- output
local NS, DS, AS, DP = ctable.NS, ctable.DS, ctable.AS, ctable.DP
-- boolean
local adjective = args.adjective
-- tables
local gender, notes = args.gender, args.notes
--[[
local notes = {
labial = 'Nominative singular ' ..
mention('-ψ') ..
(('φβ'):find(last1) and ' is a neutralization of ' or ' is a respelling of ') ..
mention(last1) ..
' + the nominative singular suffix ' ..
mention('-ς') .. '.',
velar_dental = 'Nominative singular ' ..
mention('-ξ') ..
' is a neutralization of ' ..
mention(last2) ..
' + the nominative singular suffix ' ..
mention('-ς') .. '.',
'Nominative singular ' ..
mention('-ξ') ..
(umatch('χγ', last1) and ' is a neutralization of ' or ' is a respelling of ') ..
mention(last1) ..
' + the nominative singular suffix ' ..
mention('-ς') .. '.'
}
--]]
local identifying_ending
local chars, char2 = umatch(base, '(.(.))$')
-- '$', 'τ$', ''
if chars == 'ντ' or chars == 'κτ' then
identifying_ending = chars
else
identifying_ending = char2
end
local NS_ending = finals
if NS_ending then
NS = base:gsub(identifying_ending .. "$", NS_ending)
DP = NS
end
if not (NS or DP) then
local decl_type = args.decl_type
local last1, last2 = usub(base, -1), usub(base, -2)
local nom_base = usub(base, 1, -2)
local is_neuter_noun = is_neuter and not adjective
if identifying_ending == 'ντ' then
-- May also add gender.
NS, DP = handle_nt(base, decl_type, gender, is_neuter,
is_neuter_noun, adjective, arg1, arg2)
elseif ('τδθ'):find(identifying_ending) then
DP = nom_base .. 'σ'
if not adjective then
if usub(base, -3) == 'τητ' or ('δθ'):find(last1) then
if not gender then
gender = new_gender('F')
end
elseif last2 == 'ητ' or last2 == 'ωτ' then
if not gender then
gender = new_gender('M')
end
elseif last2 == 'ᾰτ' or last2 == 'ᾱτ' then
if not gender then
gender = new_gender('N')
end
end
end
if is_neuter then
if last2 == 'οτ' then
NS = nom_base .. 'ς'
else
NS = nom_base
end
else
if last2 == 'οτ' then
NS = base:gsub('οτ', 'ως')
else
NS = nom_base .. 'ς'
end
end
if arg1:find('ς$') then
table.insert(notes,
'Nominative singular ' ..
mention('-ς') ..
' arose by reduction of the original cluster ' ..
proto_mention(dental_lookup) .. '.')
end
elseif ('ρν'):find(identifying_ending) then
local vowel = usub(base, -2, -2)
if last1 == 'ρ' then
DP = base .. 'σ'
else
DP = nom_base .. 'σ'
end
if is_neuter then
NS = base
elseif vowel == 'ε' then
NS = usub(base, 1, -3) .. 'η' .. last1
elseif vowel == 'ο' then
NS = usub(base, 1, -3) .. 'ω' .. last1
elseif vowel == 'ῑ' then --ῥίς etc.
NS = nom_base .. 'ς'
elseif (vowel == 'ᾰ' and gender ~= 'N') then
NS = usub(base, 1, -3) .. 'ᾱς'
else
NS = base
end
local last3 = usub(base, -3)
if last3 == 'γον' or last3 == 'δον' then
gender = gender or 'F'
elseif ufind(last2, 'ρ') then
gender = gender or 'N'
else
gender = gender or 'M'
end
elseif identifying_ending == 'σ' then
DP = base
if is_neuter_noun then
NS = nom_base .. 'ς'
else
NS = usub(base, 1, -3) .. 'ης'
end
DS = '$ῐ̈'
elseif identifying_ending == 'ω' then
-- m_paradigms.lp_prx.NS is nil. Is the condition correct?
if NS == m_paradigms.lp_prx.NS then
DS = nom_base .. 'ῳ/$ῐ̈'
AS = '$/$ᾰ'
else
DS = '$ῐ̈́'
AS = '$/$ᾰ́'
end
DP = nom_base .. 'ωσ'
NS = nom_base .. 'ως'
gender = gender or 'M'
else
error('Stem does not end in a consonant: ' .. base)
end
end
-- This overrides any assignment done above. Better to improve the code
-- above or prevent it from assigning NS.
if not adjective then
NS = arg1
end
if DP then
if accent_alternating then
DP = DP .. 'ῐ́(ν)'
else
DP = DP .. 'ῐ(ν)'
end
end
ctable.DS, ctable.AS = DS, AS
ctable.NS, ctable.DP = NS, DP
args.gender = gender
end
local function dial_forms_third(args)
if not args.ctable.DP then
require('Module:debug').track('grc-decl/no dative plural')
mw.log("no DP: " .. args .. ", " .. args)
end
dial_form_multi(args, {
{ 'DP', args.ctable and args.ctable .. '/$εσσῐ(ν)/$εσῐ(ν)' or '$εσσῐ(ν)/$εσῐ(ν)' },
{ 'GD', '$οιῐν' },
},
'epi')
dial_form_multi(args, {
{ 'DP', '$εσσῐ(ν)', 'dor' },
{ 'AS', '$ᾰν', 'kyp' },
{ 'DP', '$εσσῐ(ν)', { 'les', 'the' } },
{ 'DP', '$εσσῐ(ν)', 'boi' },
{ 'DP', '$οις', { 'lok', 'ele' } },
{ 'AP', '$ες', { 'ark', 'ele' } },
{ 'AP', '$ᾰς/$ᾰνς', 'kre' }, })
end
local function dial_forms_third_oxy(args)
dial_form_multi(args, {
{ 'GD', '$οῖῐν' },
{ 'DP', args.ctable and args.ctable .. '/$εσσῐ(ν)/$εσῐ(ν)' or '$εσσῐ(ν)/$εσῐ(ν)' },
},
'epi')
dial_form_multi(args, {
{ 'DP', '$εσσῐ(ν)', 'dor' },
{ 'AS', '$ᾰν', 'kyp' },
{ 'DP', '$εσσῐ(ν)', { 'les', 'the' } },
{ 'DP', '$εσσῐ(ν)', 'boi' },
{ 'DP', '$οῖς', { 'lok', 'ele' } },
{ 'AP', '$ες', { 'ark', 'ele' } },
{ 'AP', '$ᾰς/$ᾰνς', 'kre' }, })
end
-- For instance, ].
local function third_comparative(args, neuter)
local short_stem, count = args.stem:gsub('ον$', '')
if count ~= 1 then
error('Stem failure.')
end
if neuter then
dial_form(args, 'NP', '$ᾰ/' .. short_stem .. 'ω', 'att')
else
dial_form_multi(args, {
{ 'AS', '$ᾰ/' .. short_stem .. 'ω'},
{ 'NP', '$ες/' .. short_stem .. 'ους' },
{ 'AP', '$ᾰς/' .. short_stem .. 'ους' },
}, 'att')
end
end
-- These nouns are monosyllabic, but unlike most they do not accent the ending
-- of the genitive–dative dual and genitive plural: παίδων, not *παιδῶν.
local monosyllabic_exceptions = {
= true,
= true,
= true,
= true,
= true,
= true,
= true,
}
-- Other exceptions: contracted forms like ἔαρος > ἦρος (not *ἠρός).
-- The source for this is Smyth § 252.
local function third(neuter)
return function (args)
if args.decl_type:find('ντ$') and m_accent.syllables(args, 'eq', 1) then
-- ]
require('Module:debug').track('grc-decl/monosyllabic nt')
end
-- This tracks monosyllabic nouns or adjectives that have alternating accent
-- position. Masculine and neuter participles (with stems in -ντ) and
-- suffixes do not.
local accent_alternating = not (args.suffix or args.stem:find('ντ$'))
and m_accent.syllables(args, 'eq', 1)
args.accent_alternating = accent_alternating
local accent_on_ultima = accent_alternating and '' or '_prx'
local suffix = accent_switch(args.accent.term, accent_on_ultima, accent_on_ultima, '_prx', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
if not (args.NS and args.DP) then
third_nom(args, neuter, accent_alternating)
end
if args.comparative then
third_comparative(args, neuter)
end
if not neuter then
local last_two = usub(args.stem, -2)
if last_two == 'ῐδ' or last_two == 'ῐτ' or last_two == 'ῑθ' then
if args.accent.term ~= 'oxytone' then
dial_form(args, 'AS', args.ctable, 'epi', 'ion') -- i.e. -χάριν; also poetic
args.ctable = args or usub(args.stem, 1, -2) .. 'ν'
end
args.ctable = args or usub(args.stem, 1, -2)
elseif last_two == 'ντ' and (args.decl_type == '1&3-εσσ' or not args.adjective) then
args.ctable = args or usub(args.stem, 1, -2)
elseif args.accent.term ~= 'oxytone' and args.accent.term ~= 'perispomenon' and
ufind(args.stem, '$') then
args.ctable = args or args.stem
-- What's an example of this?
elseif usub(args.stem, -1) == 'ε' then
args.ctable = args or args.stem .. 'ς'
end
if args.accent.term == 'perispomenon' then
args.ctable = args
end
end
if accent_alternating then
dial_forms_third_oxy(args)
if monosyllabic_exceptions)] then
-- Remove accent marks: that is, make the form be accented on the
-- stem.
args.ctable.GD = m_accent.strip_tone(args.ctable.GD)
args.ctable.GP = m_accent.strip_tone(args.ctable.GP)
end
else
dial_forms_third(args)
end
end
end
export.inflections = third(false)
export.inflections = third(true)
local function dial_forms_es(args, neuter)
dial_form_multi(args, {
{ 'GS', '$εος', { 'nonIA' } },
{ 'GS', '$ιος', 'buck9' },
{ 'GS', '$εος/$ευς', { 'ion', 'epi' } },
{ 'DS', '$ει/$εῐ̈', { 'epi', 'ion' } },
{ 'ND', '$ει/$εε', { 'ion', 'epi' } },
{ 'GD', '$έοιν', { 'nonIA', 'ion' } },
{ 'GD', '$ίοιν', 'buck9' },
{ 'NP', neuter and '$εᾰ' or '$εις/$εες', { 'ion', 'epi' } },
{ 'GP', '$έων', { 'nonIA', 'ion', 'epi' } },
{ 'GP', '$ίων', 'buck9' }, })
if not neuter then
dial_form_multi(args, {
{ 'AS', '$εᾰ', { 'nonIA', 'ion', 'epi' } },
{ 'AP', '$εᾰς', { 'nonIA', 'ion', 'epi' } },
{ 'AP', '$ιᾰς', 'buck9' }, })
end
end
local function dial_forms_es_oxy(args, neuter)
dial_form_multi(args, {
{ 'GS', '$έος', 'nonIA' },
{ 'GS', '$ίος', 'buck9' },
{ 'GS', '$έος/$εῦς', { 'ion', 'epi' } },
{ 'DS', '$εῖ/$έῐ̈', { 'epi', 'ion' } },
{ 'ND', '$εῖ/$έε', { 'ion', 'epi' } },
{ 'GD', '$έοιν', { 'nonIA', 'ion' } },
{ 'GD', '$ίοιν', 'buck9' },
{ 'NP', neuter and '$έᾰ' or '$εῖς/$έες', { 'ion', 'epi' } },
{ 'GP', '$έων', { 'nonIA', 'ion', 'epi' } },
{ 'GP', '$ίων', 'buck9' }, })
if not neuter then
dial_form_multi(args, {
{ 'AS', '$έᾰ', { 'nonIA', 'ion', 'epi' } },
{ 'AS', '$ίᾰ', 'buck9' },
{ 'AP', '$έᾰς', { 'nonIA', 'ion', 'epi' } },
{ 'AP', '$ίᾰς', 'buck9' }, })
end
end
-- Adjectives with these endings have persistent accent (Smyth 292c):
-- εὐώδης, εὐῶδες.
local persistent_es = {
= true,
= true,
= true,
= true,
}
-- How to deal with τριήρων, not *τριηρῶν (Smyth 264)?
local function es_adj(gender_code, open)
return function(args)
uncontracted_error(args.contracted or not open, args.dial, '-ης')
local suffix = accent_switch(args.accent.term, '', nil, '_prx',
'Perispomenon third-declension forms in -ης not supported.', args.suffix)
args.ctable = deep_copy(m_paradigms[(gender_code or '') .. 'es_adj' ..
suffix .. (open and '_open' or '')])
-- Most adjectives of this type have recessive accent. Set accent to
-- antepenult.
if not args.suffix and not persistent_es, -4))] then
args.accent.position = -3
end
if args.accent.term == 'oxytone' then
dial_forms_es_oxy(args, gender_code == 'N_')
else
dial_forms_es(args, gender_code == 'N_')
end
if gender_code ~= 'N_' then
dial_form(args, 'NS', '$εῖς', 'boi', 'the')
end
end
end
export.inflections = es_adj()
export.inflections = es_adj('N_')
export.inflections = es_adj(nil, true)
export.inflections = es_adj('N_', true)
local function neuter_os(open)
return function(args)
nonrecessive_error(args.accent.term,
'Third-declension neuter nouns in -ος must be accented recessively')
uncontracted_error(args.contracted or not open, args.dial, '-ος')
args.ctable = deep_copy(m_paradigms)
dial_forms_es(args, true)
end
end
export.inflections = neuter_os()
export.inflections = neuter_os(true)
local function neuter_as(open)
return function(args)
nonrecessive_error(args.accent.term,
'Third-declension neuter nouns in -ας must be accented recessively')
uncontracted_error(args.contracted or not open, args.dial, '-ᾰς')
args.ctable = deep_copy(m_paradigms)
end
end
export.inflections = neuter_as()
export.inflections = neuter_as(true)
local function kles(open)
return function(args)
local dialect = args.dial
uncontracted_error(args.contracted or not open, dialect, '-κλης')
if dialect == 'ion' or dialect == 'epi' then
open = true
end
args.ctable = deep_copy(m_paradigms)
args.number = { 'S', S = true }
args.recessive_VS = true
if open then
dial_form_multi(args, {
{ 'GS', '$κλῆος/$κλέος' },
{ 'DS', '$κλῆῐ̈/$κλέῐ̈' },
{ 'AS', '$κλῆᾰ/$κλέᾱ' },
},
{ 'epi', 'ion' })
else
dial_form_multi(args, {
{ 'GS', '$κλέος', 'nonIA' },
{ 'GS', '$κλεῖος', 'boi' }, })
end
end
end
export.inflections = kles()
export.inflections = kles(true)
-- Needs dialectal forms.
local function weak_i(gender_code)
return function(args)
nonrecessive_error(args.accent.term,
'Third-declension nouns with stem in short ῐ must be accented recessively.')
args.ctable = deep_copy(m_paradigms)
args.synaeresis = true
-- Or Ionic or both?
dial_form_multi(args, {
{ 'DS', '$ει/$εῐ̈' },
{ 'ND', '$ει/$εε' },
{ 'NP', '$εις/$εες' },
},
'epi')
end
end
export.inflections = weak_i()
export.inflections = weak_i('N_')
local function dial_forms_weak_u(args, neuter)
dial_form_multi(args, {
{ 'GS', '$έος', { 'epi', 'ion' } },
{ 'GD', '$έοιῐν', 'epi' },
{ 'DP', '$έεσσῐ(ν)/$έεσῐ(ν)/$εσῐ(ν)', 'epi' },
{ 'DP', '$έεσσῐ(ν)', 'dor' }})
if args.adjective then
args.ctable = '$έος'
args.ctable = '$έε'
end
if not neuter then
dial_form_multi(args, {
{ 'NP', '$έες', 'epi' }, -- What about nonIA and ion?
{ 'AP', '$έᾰς', { 'nonIA', 'ion', 'epi' } }, })
end
end
local function dial_forms_weak_u_prx(args, neuter)
dial_form_multi(args, {
{ 'GS', '$εος', { 'epi', 'ion' } },
{ 'GD', '$έοιῐν', 'epi' },
{ 'DP', '$έεσσῐ(ν)/$έεσῐ(ν)/$εσῐ(ν)', 'epi' },
{ 'DP', '$έεσσῐ(ν)', 'dor' }, })
if args.adjective then
args.ctable = '$εος'
args.ctable = '$εε'
args.ctable = '$έων'
end
if not neuter then
dial_form_multi(args, {
{ 'NP', '$εες' },
{ 'AP', '$εᾰς' },
}, 'epi')
end
end
local function weak_u (gender)
return function(args)
local suffix = accent_switch(args.accent.term, '', nil, '_prx', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
if not args.adjective then
args.synaeresis = true
end
local neuter = gender == 'N_'
if args.accent.term == 'oxytone' then
dial_forms_weak_u(args, neuter)
else
dial_forms_weak_u_prx(args, neuter)
end
end
end
export.inflections = weak_u()
export.inflections = weak_u('N_')
export.inflections = function(args)
nonrecessive_error("Nouns in -ῐς cannot be accented on the ultima")
args.ctable = deep_copy(m_paradigms.pure_i_prx)
dial_form_multi(args, {
{ 'GS', '$ῐος/$ηος' },
{ 'DS', '$ῐῐ/$ῑ/$ηῐ̈/$ει' },
{ 'GD', '$ῐ́οιῐν' },
{ 'NP', '$ῐες/$ηες' },
{ 'DP', '$ῐ́εσσῐ(ν)/$εσῐ(ν)/$ῐσῐ(ν)' },
{ 'AP', '$ῐᾰς/$ηᾰς/$ῑς' },
},
'epi')
dial_form(args, 'DP', '$ῐ́εσσῐ(ν)', 'dor')
end
export.inflections = function(args)
args.ctable = deep_copy(m_paradigms.N_pure_i_prx)
end
export.inflections = export.inflections
local function pure_u_short(gender_code)
return function(args)
-- No error message needed.
local suffix = accent_switch(args.accent.term, '', nil, '_prx', nil, args.suffix)
if gender_code == 'N_' and args.accent.term == 'oxytone' then
error('Neuter nouns in short -ῠ must be accented recessively.')
end
args.ctable = deep_copy(m_paradigms)
dial_form_multi(args, {
{ 'GD', '$ῠ́οιῐν' },
{ 'DP', '$ῠ́εσσῐ(ν)/$ῠσῐ(ν)/$ῠσσῐ(ν)' },
},
'epi')
dial_form(args, 'DP', '$ῠ́εσσῐ(ν)', 'dor')
end
end
export.inflections = pure_u_short()
export.inflections = pure_u_short('N_')
export.inflections = function(args)
local suffix = accent_switch(args.accent.term, '', '_con', '_prx', nil, args.suffix)
args.ctable = deep_copy(m_paradigms)
dial_form(args, 'DP', '$ῠ́εσσῐ(ν)', 'dor')
dial_form_multi(args, {
{ 'DP', accent_switch(args.accent.term,
'$ῠ́εσσῐ(ν)/$ῠσῐ(ν)/$ῠσσῐ(ν)',
'$ῠ́εσσῐ(ν)',
'$ῠ́εσσῐ(ν)/$ῡσῐ(ν)/$ῡσσῐ(ν)', nil, args.suffix) },
{ 'GD', '$ῠ́οιῐν' },
},
'epi')
end
local function third_eus(contracted)
return function(args)
-- Maybe only Attic actually?
local dialect = args.dial
if contracted and not (dialect == 'att' or dialect == 'koi' or dialect == 'byz') then
error('Only Attic, Koine, or Byzantine Greek have contracted declined forms for nouns in -ευς.')
end
if dialect == 'kyp' or dialect == 'boi' then
args.ctable = deep_copy(m_paradigms.eus_hwos)
elseif dialect == 'les' or dialect == 'epi' then
args.ctable = deep_copy(m_paradigms.eus_hos)
dial_form_multi(args, {
{ 'GS', '$ῆος/$έος' },
{ 'DS', '$ῆῐ̈/$έῐ̈' },
{ 'AS', '$ῆᾰ/$έᾰ' },
{ 'GD', '$ήοιῐν' },
{ 'DP', '$ήεσσῐ(ν)/$εῦσῐ(ν)' },
},
'epi')
elseif dialect == 'the' or dialect == 'ele' then
args.ctable = deep_copy(m_paradigms.eus_eios)
else
if contracted then
args.ctable = deep_copy(m_paradigms.eus_con)
table.insert(args.titleapp, ']')
else
args.ctable = deep_copy(m_paradigms.eus)
end
dial_form_multi(args, {
{ 'DP', '$έεσσῐ(ν)', 'dor' }, -- this is somewhat conjectured
{ 'GS', '$έος', { 'nonIA', 'ion' } },
{ 'DS', '$εῖ', 'att' },
{ 'AS', '$έᾰ', { 'nonIA', 'ion' } },
{ 'AS', '$ῆ', { 'doric', 'del' } },
{ 'AS', '$έᾰ', { 'lok', 'kre' } },
{ 'NP', '$έες/$εῖς', 'ion' },
{ 'NP', '$εῖς', 'nonIA' },
{ 'NP', '$ῆς', { 'koa', 'lak' } },
{ 'NP', '$ῆς', 'ara' },
{ 'NP', '$έες', 'kre' },
{ 'NP', '$εῖς', 'late' },
{ 'AP', '$έᾰς', { 'nonIA', 'ion' } },
{ 'NS', '$ής', 'ara' },
{ 'AS', '$ήν', 'ara' }, })
--[[
Contraction only for nouns with vowel before -ευς.
]]
local diacritics = mw.loadData('Module:grc-utilities/data').diacritics
local macron, breve = diacritics.macron, diacritics.breve
if ufind(args.stem, '?$') then
dial_form_multi(args, {
{ 'GS', '$έως/$ῶς' },
{ 'AS', '$έᾱ/$ᾶ' },
{ 'GP', '$έων/$ῶν' },
{ 'AP', '$έᾱς/$ᾶς' },
},
'att')
table.insert(args.notes, 'The first form is uncontracted, the second ].')
elseif contracted then
error('Forms with a stem ending in ' ..
require('Module:grc-utilities').tag(usub(args.stem, -1)) ..
' do not have contracted forms.')
end
end
end
end
export.inflections = third_eus(false)
export.inflections = third_eus(true)
export.inflections = function(args)
args.ctable = deep_copy(m_paradigms.oi)
args.number = { 'S', S = true }
dial_form_multi(args, {
{ 'GS', '$ῶς', 'sever' },
{ 'AS', '$οῦν', 'ion' }, })
end
-- irregular masculine or feminine nouns
export.inflections = function(args)
local params = m_decl_static_data.irregular.noun.masculine_feminine
if args.gender.N then
return export.inflections(args)
end
local noun = not args.adjective
-- Would DP (dual–plural) ever be used?
local number = args.number
local param_code
if number.F then
param_code = 'full'
elseif number.S then
if number.P then
param_code = 'SP'
else
param_code = 'S'
end
elseif number.D then
if number.P then
param_code = 'DP'
else
param_code = 'D'
end
elseif number.P then
param_code = 'P'
else
error('Could not decide which table of forms to use.')
end
local forms = params
args.declheader = 'irreg'
local ctable = {}
local found_forms = false
for i = 2, args.maxindex do
local code = forms
local arg1 = ine(args)
if code then
local arg2 = ine(args)
if arg1 and arg2 then
error('Two forms specified for ' .. code .. ': ' .. arg1 .. ' and ' .. arg2 '.')
end
local number_code = code:sub(2, 2)
if arg1 or arg2 then
if number then
found_forms = true
else
error('The number ' .. number_code .. ' is not specified in the form parameter.')
end
end
ctable = arg1 or arg2
elseif arg1 then
local abbr = {
S = 'singular',
D = 'dual',
P = 'plural',
}
local numbers = mw.text.listToText(require('Module:fun').map(function(code)
return abbr or code
end,
number
))
local singular = not number
error('Parameter ' .. i .. ' has been given, but only ' .. #forms ..
' parameters are needed because the number' .. (singular and '' or 's') ..
' specified in the form parameter ' .. (singular and 'is ' or 'are ') ..
numbers .. '.')
end
end
if not found_forms then
error('No displayable forms found.')
end
args.ctable = ctable
end
-- irregular neuter nouns
export.inflections = function(args) --neuter irregular nouns
local params = m_decl_static_data.irregular.noun.neuter
local number = args.number
local param_code
if number.F then
param_code = 'full'
elseif number.P then
if number.S then
param_code = 'SP'
elseif number.D then
param_code = 'DP'
else
param_code = 'P'
end
elseif number.D then
param_code = 'D'
elseif number.S then
param_code = 'S'
else
error('Could not decide which table of forms to use.')
end
local forms = params
local ctable = {}
for i = 2, args.maxindex do
local code = forms
local arg1 = args
if code then
local arg2 = args
if arg1 and arg2 then
error('Two forms given for form ' .. code .. ': ' .. arg1 ..
' in parameter ' .. i .. ' and ' .. arg2 ..
' in parameter ' .. code ..
'. Choose one or put both in the same parameter separated by slashes.')
end
ctable = arg1 or arg2
elseif arg1 then
error('Parameter ' .. i .. ', ' .. arg1 .. ', does not have a case and number assigned to it.')
end
end
for form, source in pairs(forms.redirects) do
ctable = args or ctable
end
args.ctable = ctable
end
-- irregular adjectives
export.inflections = function(args)
local params = m_decl_static_data.irregular.adjective
local number = args.number
local param_code
if number.F then
param_code = 'full'
elseif number.P then
if number.S then
param_code = 'SP'
else
param_code = 'P'
end
elseif number.S then
param_code = 'S'
else
error('Could not decide which table of forms to use.')
end
local forms = params
local atable = {}
for i = 2, args.maxindex do
local code = forms
local arg1 = ine(args)
if code then
local arg2 = ine(args)
if arg1 and arg2 then
error('Two forms given for form ' .. code .. ': ' .. arg1 ..
' in parameter ' .. i .. ' and ' .. arg2 ..
' in parameter ' .. code ..
'. Choose one or put both in the same parameter separated by slashes.')
end
atable = arg1 or arg2
else
if arg1 then
error('No gender, case, and number assigned to parameter ' ..
i .. ': ' .. tostring(arg1) .. '. Only ' ..
#forms .. ' parameters are needed.')
end
end
end
for form, source in pairs(forms.redirects) do
atable = args or atable
end
if number.D then
for target_case, source_case in pairs { A = 'N', V = 'N', D = 'G' } do
for _, gender in ipairs { 'M', 'F', 'N' } do
local target = gender .. target_case .. 'D'
atable = args or atable or atable
local target = gender .. 'VP'
atable = args or atable or atable
end
end
end
args.atable = atable
end
export.inflections = function(args)
args.ctable = deep_copy(m_paradigms.indecl)
args.stem = { args }
end
return export