This module generates inflection tables for Hebrew nouns, used by {{he-decl}}
, see its documentation to learn more.
local m_links = require("Module:links")
local m_strutils = require("Module:string utilities")
local com = require("Module:he-common")
local lang = require("Module:languages").getByCode("he")
local export = {}
local function process_arg(arg)
arg = com.fix_nikud(arg)
local i = mw.ustring.find(arg, "")
if i then
arg = {mw.text.trim(mw.ustring.sub(arg, 1, i - 1)), mw.text.trim(mw.ustring.sub(arg, i + 1))}
end
return arg
end
local function getv(x)
if type(x) == "table" then
return x
else
return x
end
end
local function getnv(x)
if type(x) == "table" then
return x
else
return mw.ustring.gsub(x, "*+%-?]+", function(c1) return (lang:makeEntryName(c1)) end)
end
end
local function getnvrx(x)
return mw.ustring.gsub(getnv(x), "%?", "")
end
local function append_parts_2(a, b)
if type(a) == "table" then
if type(b) == "table" then
return {a .. b, a .. b}
else
return {a .. (lang:makeEntryName(b)), a .. b}
end
else
if type(b) == "table" then
return {(lang:makeEntryName(a)) .. b, a .. b}
else
return a .. b
end
end
end
local function append_parts(a, ...)
for _, b in ipairs({...}) do
a = append_parts_2(a, b)
end
return a
end
local function equal(a, b)
if type(a) == "table" then
return type(b) == "table" and a == b and a == b
else
return a == b
end
end
local function gsub_form(form, regex, repl, const_repl)
if type(form) == "table" or type(regex) == "table" or (not const_repl and type(repl) == "table") then
local replv = repl
local replnv = repl
if not const_repl then
replv = getv(repl)
replnv = getnv(repl)
end
local retv = mw.ustring.gsub(getv(form), getv(regex), replv)
local retnv = mw.ustring.gsub(getnv(form), getnvrx(regex), replnv)
return {retnv, retv}
else
return mw.ustring.gsub(form, regex, repl)
end
end
local function match_form(form, regex)
if type(form) == "table" or type(regex) == "table" then
local retv = mw.ustring.match(getv(form), getv(regex))
local retnv = mw.ustring.match(getnv(form), getnvrx(regex))
if retv and retnv then
return {retnv, retv}
else
return nil
end
else
return mw.ustring.match(form, regex)
end
end
local final_to_unfinal = {
= "כ",
= "מ",
= "נ",
= "פ",
= "צ",
}
local function unfinalize(form)
if match_form(form, "ַ$") or match_form(form, "הּ?$") then
return gsub_form(form, "()??$", "%1")
elseif match_form(form, "$") or match_form(form, "ךְ$") then
return gsub_form(form, "()?$", final_to_unfinal, true)
else
return form
end
end
local function make_link(x, is_construct)
local dolink = true
if SUPPRESS_LINKS then
dolink = nil
end
local maqaf = is_construct and "־" or ""
if type(x) == "table" then
local pg = (lang:makeEntryName(x))
return m_links.full_link({lang = lang, allow_self_link = false, term = dolink and pg, alt = (pg ~= (lang:makeEntryName(x)) and pg .. maqaf .. " / " or "") .. x .. maqaf, tr = "-"})
else
if x == "-" then
return "—" -- m-dash
else
return m_links.full_link({lang = lang, term = dolink and x, alt = x .. maqaf, tr = "-", allow_self_link = false})
end
end
end
local numbers = {
= "singular",
= "dual",
= "plural",
= "masculine singular",
= "masculine dual",
= "masculine plural",
= "feminine singular",
= "feminine dual",
= "feminine plural",
}
local singulars = {
= true,
= true,
= true,
}
local function split_defv(form)
local ret = mw.ustring.gsub(form, "^הַ(?)ּ", "%1")
if ret ~= form then return ret end
ret = mw.ustring.gsub(form, "^הַ()", "%1")
if ret ~= form then return ret end
ret = mw.ustring.gsub(form, "^הָ(ר)", "%1")
if ret ~= form then return ret end
ret = mw.ustring.gsub(form, "^ה()", "%1")
if ret ~= form then return ret end
return nil
end
local function split_def(form)
if type(form) == "table" then
local wv = split_defv(form)
local nv = mw.ustring.gsub(form, "^ה", "")
if wv and (nv ~= form) then
return {nv, wv}
else
return nil
end
else
return split_defv(form)
end
end
local function attach_def(form)
if match_form(form, "^") then
return append_parts("הַ", form)
elseif match_form(form, "^") then
return append_parts("הָ", form)
elseif match_form(form, "^ו") then
return gsub_form(form, "^ו", {"הוו", "הַוּ"})
elseif match_form(form, "^") or match_form(form, "^ש") then
return gsub_form(form, "^(?)", "הַ%1ּ")
else
error("Unrecognized initial consonant in indefinite form.")
end
end
local function remove_maqaf(x)
if type(x) == "table" then
return {mw.ustring.gsub(x, "־$", ""), mw.ustring.gsub(x, "־$", "")}
else
return mw.ustring.gsub(x, "־$", "")
end
end
local function process_args(args)
for key, val in pairs(args) do
val = mw.text.trim(val)
if val == "" then val = nil end
args = val
end
local forms = {}
if not args then return forms end
local i = 1
local num = "s"
while num do
local section = { = num}
local touched = false
local likely_def = nil
local initial_hes = nil
while args and not numbers] do
local form = process_arg(args)
local wv = getv(form)
if likely_def then
local cur_initial_hes = mw.ustring.len(mw.ustring.match(getnv(form), "^ה*"))
if cur_initial_hes < initial_hes then
section = likely_def
elseif cur_initial_hes > initial_hes and split_defv(wv) then
section = likely_def
else
error("Definite form must either be first or second.")
end
likely_def = nil
end
if (not section) and split_defv(wv) then
if touched then
section = form
else
likely_def = form
initial_hes = mw.ustring.len(mw.ustring.match(getnv(form), "^ה*"))
end
elseif not touched then
section = form
elseif (not section) and mw.ustring.match(wv, "ךָ$") then
section = form
elseif (not section) and mw.ustring.match(wv, "כֶם$") then
section = form
elseif (not section) and (mw.ustring.match(wv, "וֹ$") or mw.ustring.match(wv, "ו$") or mw.ustring.match(wv, "הוּ$")) then
section = form
elseif (not section) and (mw.ustring.match(wv, "־$") or mw.ustring.match(getnv(form), "־$")) then
section = remove_maqaf(form)
else
error("Unrecognized or duplicate form: " .. wv)
end
touched = true
i = i + 1
end
if likely_def then
if section then error("Unrecognized or duplicate form: " .. getv(section)) end
section = likely_def
end
if touched then
table.insert(forms, section)
end
num = args
i = i + 1
end
return forms
end
local endings_c = {
= false,
= "ִי",
= "ְךָ",
= "ֵךְ",
= "וֹ",
= "ָהּ",
= "ֵנוּ",
= "ְכֶם",
= "ְכֶן",
= "ָם",
= "ָן",
}
-- most have alternative forms that are not yet supported
local endings_e = {
= true,
= "ִי",
= "ֶךָ",
= "ֵךְ",
= "ֵהוּ",
= "ֶהָ",
= "ֵנוּ",
= "ֵכֶם",
= "ֵכֶן",
= "ֵהֶם",
= "ֵהֶן",
}
local endings_i = {
= true,
= "",
= "ךָ",
= "ךְ",
= "ו", -- and "הוּ", for when I allow multiple forms
= "הָ",
= "נוּ",
= "כֶם",
= "כֶן",
= "הֶם",
= "הֶן",
}
-- endings for ים ending
local endings_pm = {
= true,
= {"יי", "ַי"},
= "ֶיךָ",
= {"ייך", "ַיִךְ"},
= "ָיו",
= "ֶיהָ",
= "ֵינוּ",
= "ֵיכֶם",
= "ֵיכֶן",
= "ֵיהֶם",
= "ֵיהֶן",
}
-- endings for ות ending
local endings_pf = {
= true,
= {"יי", "ַי"},
= "ֶיךָ",
= {"ייך", "ַיִךְ"},
= "ָיו",
= "ֶיהָ",
= "ֵינוּ",
= "ֵיכֶם",
= "ֵיכֶן",
= "ָם",--add extra יהם ending
= "ָן",--add extra יהן ending
}
local light_endings = {
"1s",
"2ms",
"2fs",
"3ms",
"3fs",
"1p",
}
local heavy_endings = {
"2mp",
"2fp",
}
local maybe_endings = {
"3mp",
"3fp",
}
local function attach_ending(stem, ending)
if match_form(ending, "^ְ") and match_form(stem, "$") then
ending = gsub_form(ending, "^ְ", "ֲ")
elseif match_form(ending, "^ָ") and match_form(stem, "ַח$") then
stem = gsub_form(stem, "ַח$", "ֶח")
elseif type(ending) == "table" and mw.ustring.match(getnv(stem), "י$") and mw.ustring.len(mw.ustring.match(getnv(ending), "^י*")) > mw.ustring.len(mw.ustring.match(getnv(getv(ending)), "^י*")) then
ending = gsub_form(ending, {"^י", "^"}, "")
end
return append_parts(stem, ending)
end
local function infer_forms(forms)
for _, section in pairs(forms) do
local light_stem = nil
local heavy_stem = nil
local endings = nil
if section then
section = section or split_def(section)
elseif section then
section = section or attach_def(section)
else
error("Must have either definite or indefinite form.")
end
if singulars] then
local is_fem = match_form(section, "ָה$")
local is_e = match_form(section, "ֶה$")
if not section then
if is_fem then
section = gsub_form(section, "ָה$", "ַת")
elseif is_e then
section = gsub_form(section, "ֶה$", "ֵה")
elseif match_form(section, "ָ?$") then
section = gsub_form(section, "ָ(?)$", "ַ%1")
else
section = section
end
end
if section then
if match_form(section, "וֹ$") then
light_stem = gsub_form(section, "וֹ$", "")
endings = endings_c
elseif match_form(section, "ֵהוּ$") then
light_stem = gsub_form(section, "ֵהוּ$", "")
endings = endings_c
elseif match_form(section, "ִיהוּ$") then
light_stem = gsub_form(section, "הוּ$", "")
endings = endings_i
elseif match_form(section, "ִיו$") then
light_stem = gsub_form(section, "ו$", "")
endings = endings_i
else
error("Unrecognized 3ms suffix pattern.")
end
elseif match_form(section, "ֵה$") then
if is_e then
light_stem = gsub_form(section, "ֶה$", "")
else
light_stem = gsub_form(section, "ֵה$", "")
end
endings = endings_e
elseif match_form(unfinalize(section), "ַ?$") then
light_stem = gsub_form(unfinalize(section), "ַ(?)$", "ָ%1")
endings = endings_c
else
light_stem = unfinalize(section)
if match_form(section, "ִי$") then
endings = endings_i
else
endings = endings_c
end
end
if section then
if endings == endings_c then
if not match_form(section, "ְכֶם$") and not match_form(section, "ֲכֶם$") then
error("Unrecognized 2mp suffix pattern.")
end
heavy_stem = gsub_form(section, "כֶם$", "")
else
if not match_form(section, "כֶם$") then
error("Unrecognized 2mp suffix pattern.")
end
heavy_stem = gsub_form(section, "כֶם$", "")
end
elseif section and endings == endings_c and match_form(section, "ְךָ$") then
heavy_stem = gsub_form(section, "ְךָ$", "")
elseif match_form(light_stem, "ּ$") or match_form(light_stem, "ְ?$") then
heavy_stem = light_stem
elseif match_form(section, "ֵה$") then
if endings == endings_e then
heavy_stem = gsub_form(section, "ה$", "")
else
heavy_stem = light_stem
end
else
heavy_stem = unfinalize(section)
end
else
endings = endings_pm
if not section then
if match_form(section, "וֹת$") then
endings = endings_pf
section = section
elseif match_form(section, "ִים$") then
section = gsub_form(section, "ִים$", "ֵי")
elseif match_form(section, {"יי?ם$", "ַיִם$"}) then
section = gsub_form(section, {"יי?ם$", "ַיִם$"}, "ֵי")
else
error("Unrecognized plural pattern.")
end
end
if match_form(section, "ֵי$") then
heavy_stem = gsub_form(section, "ֵי$", "")
if match_form(section, "ִים$") then
light_stem = gsub_form(section, "ִים$", "")
elseif match_form(section, {"יי?ם$", "ַיִם$"}) then
light_stem = gsub_form(section, {"יי?ם$", "ַיִם$"}, "")
else
light_stem = heavy_stem
end
elseif match_form(section, "וֹת$") then
endings = endings_pf
heavy_stem = section
light_stem = heavy_stem
else
error("Unrecognized plural construct pattern.")
end
end
local maybe_stem = endings and heavy_stem or light_stem
for _, p in pairs(light_endings) do
section = section or attach_ending(light_stem, endings)
end
for _, p in pairs(heavy_endings) do
section = section or attach_ending(heavy_stem, endings)
end
for _, p in pairs(maybe_endings) do
section = section or attach_ending(maybe_stem, endings)
end
end
end
local example_s = {
= "s",
= "בַּיִת",
= "הַבַּיִת",
= "בֵּית",
= "בֵּיתִי",
= "בֵּיתְךָ",
= "בֵּיתֵךְ",
= "בֵּיתוֹ",
= "בֵּיתָהּ",
= "בֵּיתֵנוּ",
= "בֵּיתְכֶם",
= "בֵּיתְכֶן",
= "בֵּיתָם",
= "בֵּיתָן",
}
local example_p = {
= "p",
= "בָּתִּים",
= "הַבָּתִּים",
= "בָּתֵּי",
= {"בתיי", "בָּתַּי"},
= "בָּתֶּיךָ",
= {"בתייך", "בָּתַּיִךְ"},
= "בָּתָּיו",
= "בָּתֶּיהָ",
= "בָּתֵּינוּ",
= "בָּתֵּיכֶם",
= "בָּתֵּיכֶן",
= "בָּתֵּיהֶם",
= "בָּתֵּיהֶן",
}
local example = {example_s, example_p}
local table_top = [===[<div><div class="NavFrame" style="display:inline-block;min-width:30em">
<div class="NavHead" align="left" style="min-width:30em">{title}</div>
<div class="NavContent" align="center" style="min-width:30em">
{\op}| class="wikitable inflection-table" style="text-align:center;margin:0"
! rowspan="3" | Number !! colspan="2" | Isolated forms !! colspan="5" | With possessive pronouns
|-
! rowspan="2" | State !! rowspan="2" | Form !! rowspan="2" | Person !! colspan="2" | singular !! colspan="2" | plural
|-
! m. !! f. !! m. !! f.
]===]
local table_section = [===[|-
! rowspan="3" | {number} !! indefinite
| {i}
! first
| colspan="2" | {1s} || colspan="2" | {1p}
|-
! definite
| {d}
! second
| {2ms} || {2fs} || {2mp} || {2fp}
|-
! construct
| {c}
! third
| {3ms} || {3fs} || {3mp} || {3fp}
]===]
local table_bottom = [===[|{\cl}
</div></div></div>]===]
local forms_names = {
"i",
"d",
"c",
"1s",
"2ms",
"2fs",
"3ms",
"3fs",
"1p",
"2mp",
"2fp",
"3mp",
"3fp",
}
local function make_table(forms)
local title = forms and forms and forms and ("Declension of " .. make_link(forms)) or "Declension"
local output = {}
table.insert(output, m_strutils.format(table_top, { = title}))
for _, section in ipairs(forms) do
section = numbers]
for _, form_name in pairs(forms_names) do
section = make_link(section or "-", section and form_name == "c")
end
table.insert(output, m_strutils.format(table_section, section))
end
table.insert(output, m_strutils.format(table_bottom, {}))
return table.concat(output)
end
function export.show(frame)
local args = frame:getParent().args
PAGENAME = mw.title.getCurrentTitle().text
NAMESPACE = mw.title.getCurrentTitle().nsText
if args and args ~= "" then
SUPPRESS_LINKS = true
end
local forms = nil
if frame.args == "example" then
forms = example
else
forms = process_args(args)
infer_forms(forms)
end
return make_table(forms)
end
return export