Module:mr-verb

local export = {}

--[=[

Authorship: Aryaman Arora  based on work by Ben Wing

]=]

--[=[

TERMINOLOGY:

-- "slot" = A particular combination of person/number/gender/tense/etc. Example slot names for verbs are "inf_mp" (masculine plural infinitive), "prog" (undeclined progressive form), "pfv_ind_fut_2sm" (second-person singular	 masculine perfective indicative future). Each slot is filled with zero or more forms.

-- "form" = The conjugated Marathi form representing the value of a given slot.

-- "lemma" = The dictionary form of a given Marathi term. Generally the direct masculine singular infinitive, but may occasionally be another form if that form is missing. ]=]

--[=[

FIXME: ]=]

local lang = require("Module:languages").getByCode("mr") local m_table = require("Module:table") local m_links = require("Module:links") local m_string_utilities = require("Module:string utilities") local m_script_utilities = require("Module:script utilities") local iut = require("Module:inflection utilities") local m_para = require("Module:parameters") local com = require("Module:mr-common")

local u = require("Module:string/char") local rsplit = mw.text.split local rfind = mw.ustring.find local rmatch = mw.ustring.match local rgmatch = mw.ustring.gmatch local rsubn = mw.ustring.gsub local ulen = mw.ustring.len local usub = mw.ustring.sub local uupper = mw.ustring.upper local ulower = mw.ustring.lower

-- vowel diacritics; don't display nicely on their own local M = u(0x0901) local N = u(0x0902) local AA = u(0x093e) local AI = u(0x0948) local AU = u(0x094c) local E = u(0x0947) local EN = E .. N local I = u(0x093f) local II = u(0x0940) local IIN = II .. N local O = u(0x094b) local U = u(0x0941) local UU = u(0x0942) local UUM = UU .. M local R = u(0x0943) local VIRAMA = u(0x094d) local TILDE = u(0x0303)

-- version of rsubn that discards all but the first return value local function rsub(term, foo, bar) local retval = rsubn(term, foo, bar) return retval end

-- version of rsubn that returns a 2nd argument boolean indicating whether -- a substitution was made. local function rsubb(term, foo, bar) local retval, nsubs = rsubn(term, foo, bar) return retval, nsubs > 0 end

local function tag_text(text) return m_script_utilities.tag_text(text, lang) end

local function term_link(hi, tr) return m_links.full_link({term = hi, tr = tr, lang = lang}, "term") end

local irreg_perf = { }

local irreg_subj = { }

local irreg_polite_imp = { }

local verb_slots_impers = { inf = "inf", stem = "stem", compl = "compl", pros = "pros", inc = "inc", desid = "desid", }

local verb_slots_pers = { subj_neg = "subj_neg", }

-- Add entries for a slot with only gender/number variants. -- `slot_prefix` is the prefix of the slot, typically specifying the tense/aspect; -- `tag_suffix` is the set of inflection tags to add after the gender/number tags, -- or "-" to use "-" as the inflection tags (which indicates that no accelerator entry -- should be generated); and `verb_slots` is the table (personal or impersonal) to -- add the entries to. local function add_slot_gendered(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers verb_slots[slot_prefix .. "_ms"] = tag_suffix == "-" and "-" or "m|s|" .. tag_suffix verb_slots[slot_prefix .. "_mp"] = tag_suffix == "-" and "-" or "m|p|" .. tag_suffix verb_slots[slot_prefix .. "_fs"] = tag_suffix == "-" and "-" or "f|s|" .. tag_suffix verb_slots[slot_prefix .. "_fp"] = tag_suffix == "-" and "-" or "f|p|" .. tag_suffix verb_slots[slot_prefix .. "_ns"] = tag_suffix == "-" and "-" or "n|s|" .. tag_suffix verb_slots[slot_prefix .. "_np"] = tag_suffix == "-" and "-" or "n|p|" .. tag_suffix end

-- Same as add_slot_gendered but specifically for participles. This changes the inflection -- tags used, because the masculine singular entry is really only for direct masculine singular, -- and the masculine plural entry is also for oblique masculine singular. local function add_slot_gendered_part(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers verb_slots[slot_prefix .. "_ms"] = tag_suffix == "-" and "-" or "dir|m|s|" .. tag_suffix verb_slots[slot_prefix .. "_mp"] = tag_suffix == "-" and "-" or "m|p|" .. tag_suffix .. "|;|obl|m|s|" .. tag_suffix verb_slots[slot_prefix .. "_fs"] = tag_suffix == "-" and "-" or "f|s|" .. tag_suffix verb_slots[slot_prefix .. "_fp"] = tag_suffix == "-" and "-" or "f|p|" .. tag_suffix end

-- Compute the inflection tags associated with a given person/number/gender combination. local function personal_tags(slot_prefix, tag_suffix, persnum, gender) gender = gender and gender .. "|" or "" local suffix = gender .. tag_suffix if persnum == "2s" then -- only for imperatives return "2|s|intim|" .. suffix elseif persnum == "23s" then return "2|s|intim|" .. suffix .. "|;|3|s|" .. suffix elseif persnum == "13p" then return "13|p|" .. suffix .. "|;|2|formal|" .. suffix elseif persnum == "2p" then return "2|fam|" .. suffix elseif persnum == "3p" then -- only for imperatives return "3|p|" .. suffix .. "|;|2|formal|" .. suffix else -- 1s, 3s or 1p return persnum:gsub("^(.)(.)$", "%1|%2") .. "|" .. suffix end end

-- Return the possible person/number combinations given the slot prefix. local function persnum_values(slot_prefix) if slot_prefix:find("^imp_") then return {"2s", "2p"} else return {"1s", "2s", "3s", "1p", "2p", "3p"} end end

-- Add entries for a slot with only person/number variants. See `add_slot_gendered`. local function add_slot_personal(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers for _, persnum in ipairs(persnum_values(slot_prefix)) do local slot = slot_prefix .. "_" .. persnum if tag_suffix == "-" then verb_slots[slot] = "-" else verb_slots[slot] = personal_tags(slot_prefix, tag_suffix, persnum) end end end

-- Add entries for a slot with person/number/gender variants. See `add_slot_gendered`. local function add_slot_gendered_personal(slot_prefix, tag_suffix, verb_slots) verb_slots = verb_slots or verb_slots_pers for _, persnum in ipairs(persnum_values(slot_prefix)) do		for _, gender in ipairs({"m", "f", "n"}) do local slot = slot_prefix .. "_" .. persnum .. gender if tag_suffix == "-" then verb_slots[slot] = "-" else verb_slots[slot] = personal_tags(slot_prefix, tag_suffix, persnum, gender) end end end end

add_slot_gendered_personal("ind_hab", "hab|ind") add_slot_personal("ind_hab_neg", "hab|ind|neg") add_slot_gendered_personal("ind_perf", "perf|ind") add_slot_gendered_personal("ind_perf_neg", "perf|ind|neg") add_slot_gendered("subj", "subj") add_slot_gendered_personal("prs_prog", "prs|prog") add_slot_personal("prs_prog_neg", "prs|prog|neg") add_slot_gendered_personal("pst_prog", "pst|prog") add_slot_gendered_personal("pst_prog_neg", "pst|prog|neg")

local all_verb_slots = {} for k, v in pairs(verb_slots_impers) do	all_verb_slots[k] = v end for k, v in pairs(verb_slots_pers) do	all_verb_slots[k] = v end

-- Add one inflected form to `base.forms`, specifically to the list of forms associated with -- the slot `slot` in the table in `base.forms`. Each element of the list is an object of the -- form {form=FORM, translit=TRANSLIT, footnotes=FOOTNOTES}, where TRANSLIT is missing if no -- manual translit needs to be given and FOOTNOTES is missing if there aren't any footnotes. -- If FOOTNOTES is present it is a list of footnotes, where each footnote is e.g. "[rare or archaic]", -- i.e. surrounded by brackets, with the first character lowercase and no final period. -- (The brackets are automatically removed, the first character capitalized and a final period added.) -- -- `stem` is the Devanagari stem to add the ending to. -- `translit_stem` is the transliteration of `stem`, or nil to use the default transliteration. -- `ending` is the Devanagari ending to add to the stem, possibly with sandhi changes to the -- stem or ending. -- `footnotes` is a list of associated footnotes in the same format as FOOTNOTES above, or nil. -- `double_word` if given causes the resulting form to be doubled with a hyphen in between the two -- parts, for use with the progressive form (e.g. करते-करते of verb करना). local function add(base, stem, translit_stem, slot, ending, footnotes, double_word) local function doadd(new_stem, new_translit_stem, new_ending, slot_footnotes) new_ending = new_ending or ending if new_ending and base.notlast then -- If we're not the last verb in a multiword expression, chop off -- anything after a space. This is to handle verbs like हिलना-डुलना, -- which have e.g. nonaspectual subjunctive 1sg masc हिलूँ-डुलूँ -- but perfective future indicative 1sg masc हिला-डुला हूँगा. Also, there -- are some special cases: -- (1) the conjunctive form should be e.g. हिल-डुलकर or हिल-डुलके -- (2) the future indicative should be e.g. 1sg हिलूँ-डुलूँगा -- (3) the future imperative 3pl should be e.g. हिलिये-डुलियेगा -- (4) the agentive should be e.g. हिलने-डुलनेवाला if slot == "conj" then new_ending = "" elseif slot:find("^ind_fut") then new_ending = rsub(new_ending, "ग.$", "") -- गा, गे or गी elseif slot == "imp_fut_3p" then new_ending = rsub(new_ending, "गा$", "") elseif slot:find("^agent") then new_ending = rsub(new_ending, "वाल." .. N .. "?$", "") -- वाला, वाले, वाली, वालीं else new_ending = rsub(new_ending, " .*", "") end end com.add_form(base, new_stem or stem, new_translit_stem or translit_stem, slot,			new_ending, iut.combine_footnotes(slot_footnotes, footnotes), "link words", double_word) end

doadd end

-- Add the conjugation for a tense/aspect row with gender/number variants only. local function add_conj_gendered(base, slot_prefix, stem, translit_stem, m_s, m_p, f_s, f_p, n_s, n_p, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_ms", m_s, footnotes) add(base, stem, translit_stem, slot_prefix .. "_mp", m_p, footnotes) add(base, stem, translit_stem, slot_prefix .. "_fs", f_s, footnotes) add(base, stem, translit_stem, slot_prefix .. "_fp", f_p, footnotes) add(base, stem, translit_stem, slot_prefix .. "_ns", n_s, footnotes) add(base, stem, translit_stem, slot_prefix .. "_np", n_p, footnotes) end

-- Add the conjugation for a tense/aspect row with person/number variants only. local function add_conj_personal(base, slot_prefix, stem, translit_stem, s1, s2, s3, p1, p2, p3, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_1s", s1, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2s", s2, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3s", s3, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1p", p1, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2p", p2, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3p", p3, footnotes) end

-- Add the conjugation for a tense/aspect row with person/number/gender variants. local function add_conj_gendered_personal(base, slot_prefix, stem, translit_stem,	s1m, s2m, s3m, p1m, p2m, p3m, s1f, s2f, s3f, p1f, p2f, p3f, s1n, s2n, s3n, p1n, p2n, p3n, footnotes) if not stem then stem = base.stem translit_stem = base.stem_translit end add(base, stem, translit_stem, slot_prefix .. "_1sm", s1m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2sm", s2m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3sm", s3m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1pm", p1m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2pm", p2m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3pm", p3m, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1sf", s1f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2sf", s2f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3sf", s3f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1pf", p1f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2pf", p2f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3pf", p3f, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1sn", s1n, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2sn", s2n, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3sn", s3n, footnotes) add(base, stem, translit_stem, slot_prefix .. "_1pn", p1n, footnotes) add(base, stem, translit_stem, slot_prefix .. "_2pn", p2n, footnotes) add(base, stem, translit_stem, slot_prefix .. "_3pn", p3n, footnotes) end

local function handle_derived_slots_and_overrides(base) -- No overrides implemented currently. -- process_slot_overrides(base)

-- Compute linked versions of potential lemma slots, for use in. -- We substitute the original lemma (before removing links) for forms that -- are the same as the lemma, if the original lemma has links. -- (NOTE: Not currently used by .) for _, slot in ipairs({"inf_ms"}) do iut.insert_forms(base.forms, slot .. "_linked", iut.map_forms(base.forms[slot], function(form, translit) if form == base.orig_lemma_no_links and translit == base.lemma_translit and rfind(base.orig_lemma, "%[%[") then return base.orig_lemma, base.lemma_translit else return form, translit end end)) end end

local conjs = {} local conjprops = {}

conjs["normal"] = function(base) local function fetch_irreg(irreg_table) if irreg_table[base.stem] then local stem = irreg_table[base.stem] return stem, nil else return base.stem, base.stem_translit end end

local perf, trperf = fetch_irreg(irreg_perf) local subj, trsubj = fetch_irreg(irreg_subj) local polite_imp, tr_polite_imp = fetch_irreg(irreg_polite_imp)

-- Undeclined forms add(base, base.stem, base.stem_translit, "stem", "") add(base, base.stem, base.stem_translit, "inf", "णे") add(base, base.stem, base.stem_translit, "inf", "णं") add(base, base.stem, base.stem_translit, "compl", UU .. "न") add(base, base.stem, base.stem_translit, "pros", "णार") add(base, base.stem, base.stem_translit, "inc", UU) add(base, base.stem, base.stem_translit, "desid", AA .. "यला") add_conj_gendered_personal(base, "ind_hab", nil, nil, "तो", "तोस", "तो", "तो", "ता", "तात", "ते", "तेस", "ते", "तो", "ता", "तात", nil, nil, "ते", "तो", "ता", "तात") add_conj_personal(base, "ind_hab_neg", nil, nil, "त नाही", "त नाहीस", "त नाही", "त नाही", "त नाही", "त नाहीत") add(base, base.stem, base.stem_translit, "ind_hab_3sn", "तं") add_conj_gendered_personal(base, "ind_perf", nil, nil, "लो", "लास", "ला", "लो", "ला", "ले", "ले", "लीस", "ली", "लो", "ला", "ल्या", nil, nil, "ले", nil, nil, "ली") add(base, base.stem, base.stem_translit, "ind_perf_3sn", "लं") add(base, base.stem, base.stem_translit, "ind_perf_2pm", "लात") add(base, base.stem, base.stem_translit, "ind_perf_2pf", "लात") add_conj_gendered_personal(base, "ind_perf_neg", nil, nil, "लो नाही", "ला नाहीस", "ला नाही", "लो नाही", "ला नाहीत", "ले नाहीत", "ले नाही", "ली नाहीस", "ली नाही", "लो नाही", "ला नाहीत", "ल्या नाहीत", nil, nil, "ले नाही", nil, nil, "ली नाहीत") add(base, base.stem, base.stem_translit, "ind_perf_neg_3sn", "लं नाही") add_conj_gendered(base, "subj", nil, nil, AA .. "वा", AA .. "वे", AA .. "वी", AA .. "व्या", AA .. "वे", AA .. "वी") add(base, base.stem, base.stem_translit, "subj_mp", AA .. "वेत") add(base, base.stem, base.stem_translit, "subj_fp", AA .. "व्यात") add(base, base.stem, base.stem_translit, "subj_ns", AA .. "वं") add(base, base.stem, base.stem_translit, "subj_np", AA .. "वीत") add(base, base.stem, base.stem_translit, "subj_neg", UU .. " नए") add_conj_gendered_personal(base, "prs_prog", nil, nil, "तो आहे", "तो आहेस", "तो आहे", "तो आहोत", "ता आहात", "ता आहेत", "ते आहे", "ते आहेस", "ते आहे", "तो आहोत", "ता आहात", "ता आहेत", nil, nil, "ते आहे", "तो आहोत", "ता आहात", "ता आहेत") -- add_conj_gendered_personal(base, "prs_prog", nil, nil, "तोय", "तोयस", "तोय", "तोय", "ताय", "तायत", "त्येय", "त्येस", "त्येय", "तोय", "ताय", "तायत", nil, nil, "तंय", "तोय", "ताय", "तायत") add_conj_personal(base, "prs_prog_neg", nil, nil, "त नाहीये", "त नाहीयेस", "त नाहीये", "त नाहीयोत", "त नाहीयात", "त नाहीयेत") add_conj_gendered_personal(base, "pst_prog", nil, nil, "त होतो", "त होतास", "त होता", "त होतो", "त होता", "त होते", "त होते", "त होतीस", "त होती", "त होतो", "त होता", "त होत्या", nil, nil, "त होते", nil, nil, "त होती") add(base, base.stem, base.stem_translit, "pst_prog_3sn", "त होतं") add_conj_gendered_personal(base, "pst_prog_neg", nil, nil, "त नव्हतो", "त नव्हतास", "त नव्हता", "त नव्हतो", "त नव्हता", "त नव्हते", "त नव्हते", "त नव्हतीस", "त नव्हती", "त नव्हतो", "त नव्हता", "त नव्हत्या", nil, nil, "त नव्हते", nil, nil, "त नव्हती") add(base, base.stem, base.stem_translit, "pst_prog_neg_3sn", "त नव्हतं") end

--[=[ Parse an indicator spec (text consisting of angle brackets and zero or more dot-separated indicators within them). Return value is an object of the form

{ forms = {}, -- forms for a single spec alternant; see `forms` below

-- The following additional fields are added by other functions: orig_lemma = "ORIGINAL-LEMMA", -- as given by the user or taken from pagename orig_lemma_no_links = "ORIGINAL-LEMMA-NO-LINKS", -- links removed lemma = "LEMMA", -- `orig_lemma_no_links`, converted to singular form if plural phon_lemma = "LEMMA-PHONETIC-RESPELLING", -- as specified by the user; may be missing lemma_translit = "LEMMA-TRANSLIT", -- translit of phon_lemma (if present) forms = { SLOT = { {		form = "FORM", footnotes = {"FOOTNOTE", "FOOTNOTE", ...} -- may be missing },	 ...	},	...  },  conj = "CONJ", -- declension, e.g. "normal" (the only one currently implemented) } ]=] local function parse_indicator_spec(angle_bracket_spec) local inside = rmatch(angle_bracket_spec, "^<(.*)>$") assert(inside) local base = {overrides = {}, forms = {}} if inside ~= "" then local segments = iut.parse_balanced_segment_run(inside, "[", "]") local dot_separated_groups = iut.split_alternating_runs(segments, "%.") for i, dot_separated_group in ipairs(dot_separated_groups) do -- No indicators allowed currently. local part = dot_separated_group[1] error("Unrecognized indicator '" .. part .. "': '" .. inside .. "'") end end return base end

local function detect_indicator_spec(base) base.conj = "normal" base.stem, base.stem_translit = com.strip_ending(base, "णे") end

local function detect_all_indicator_specs(alternant_multiword_spec) iut.map_word_specs(alternant_multiword_spec, function(base)		detect_indicator_spec(base)	end)

-- Set notlast=true on verbs that aren't the last one in a multiword expression, and -- multiword=true on all verbs in multiword expressions (as well as at top level), so -- we can properly handle verbs like हिलना-डुलना. for i, alternant_or_word_spec in ipairs(alternant_multiword_spec.alternant_or_word_specs) do		if alternant_or_word_spec.alternants then for _, multiword_spec in ipairs(alternant_or_word_spec.alternants) do				for j, word_spec in ipairs(multiword_spec.word_specs) do					if j < #multiword_spec.word_specs then word_spec.notlast = true end if #multiword_spec.word_specs > 1 then word_spec.multiword = true alternant_multiword_spec.multiword = true end end end else if i < #alternant_multiword_spec.alternant_or_word_specs then alternant_or_word_spec.notlast = true end if #alternant_multiword_spec.alternant_or_word_specs > 1 then alternant_or_word_spec.multiword = true alternant_multiword_spec.multiword = true end end end end

local function conjugate_verb(base) if not conjs[base.conj] then error("Internal error: Unrecognized conjugation type '" .. base.conj .. "'") end conjs[base.conj](base) if base.multiword then -- See comment in add_variant_codes for the purpose of this. com.add_variant_codes(base) end handle_derived_slots_and_overrides(base) end

-- Compute the categories to add the verb to, as well as the annotation to display in the -- conjugation title bar. We combine the code to do these functions as both categories and -- title bar contain similar information. local function compute_categories_and_annotation(alternant_multiword_spec) local cats = {} local function insert(cattype) cattype = rsub(cattype, "~", alternant_multiword_spec.pos) m_table.insertIfNot(cats, "Marathi " .. cattype) end local annotation if alternant_multiword_spec.manual then alternant_multiword_spec.annotation = "" else local function do_word_spec(base) if base.lemma_translit and (lang:transliterate(base.lemma)) ~= base.lemma_translit then insert("~ with phonetic respelling") end end iut.map_word_specs(alternant_multiword_spec, function(base)			do_word_spec(base)		end) end alternant_multiword_spec.categories = cats end

-- Convert forms from their list/object form (see the comments to add for how this works) -- to strings that can be directly filled into the table. Approximately, each form is converted -- to a formatted link with accelerators and the results are concatenated, followed by an newline -- and then the formatted transliterations. local function show_forms(alternant_multiword_spec) local lemmas = alternant_multiword_spec.forms.inf_ms or {} local props = { lemmas = lemmas, slot_table = verb_slots_impers, lang = lang, include_translit = true, }	if alternant_multiword_spec.multiword then -- Remove variant codes that were added to ensure only parallel variants in -- multiword expressions like हिलना-डुलना get generated. See com.add_variant_codes -- for more information. props.canonicalize = function(form) return com.remove_variant_codes(form) end end iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.forms.footnote_impers = alternant_multiword_spec.forms.footnote props.slot_table = verb_slots_pers iut.show_forms(alternant_multiword_spec.forms, props) alternant_multiword_spec.forms.footnote_pers = alternant_multiword_spec.forms.footnote end

-- Generate the conjugation table. This should be called after show_forms has converted -- each form to a formatted string. local function make_table(alternant_multiword_spec) local table_spec_impersonal = [=[ Impersonal forms of {inf_raw} {\op}| class="inflection-table inflection-hi inflection-verb" data-toggle-category="inflection"
 * class="hi-tense-aspect-cell"colspan=100% | Undeclined
 * class="hi-tense-aspect-cell" colspan=2 | Stem
 * colspan="100%" | {stem}
 * class="hi-tense-aspect-cell" colspan=2 | Infinitive
 * colspan="100%" | {inf}
 * class="hi-tense-aspect-cell" colspan=2 | Completive
 * colspan="100%" | {compl}
 * class="hi-tense-aspect-cell" colspan=2 | Prospective
 * colspan="100%" | {pros}
 * class="hi-tense-aspect-cell" colspan=2 | Inceptive
 * colspan="100%" | {inc}
 * class="hi-tense-aspect-cell" colspan=2 | Desiderative
 * colspan="100%" | {desid}
 * {\cl}{notes_clause} ]=]
 * class="hi-tense-aspect-cell" colspan=2 | Inceptive
 * colspan="100%" | {inc}
 * class="hi-tense-aspect-cell" colspan=2 | Desiderative
 * colspan="100%" | {desid}
 * {\cl}{notes_clause} ]=]
 * colspan="100%" | {desid}
 * {\cl}{notes_clause} ]=]

local person_number_header_two_row = [=[ ]=]
 * - class="hi-table-header"
 * rowspan=2 |
 * rowspan=2 |
 * class="hi-mf-cell" rowspan=2 |

local person_number_header_sg_pl_headers = [=[ ]=]
 * colspan=3 | Singular
 * colspan=3 | Plural

local person_number_header_table_div = [=[ ]=]
 * - class="hi-table-header"

local person_number_header_pers_num_headers = [=[ ]=]
 * 1st मी
 * 2nd intimate तू
 * 3rd यह/वह
 * 1st आम्ही/आपण
 * 2nd तुम्ही
 * 3rd हा/तो

-- Regular person-number header used at the top of the table and in the middle. local person_number_header = person_number_header_table_div .. person_number_header_two_row .. person_number_header_sg_pl_headers .. person_number_header_table_div .. person_number_header_pers_num_headers -- Reversed person-number header used at the bottom of the table. "Reversed" means that -- the two rows are in reversed order; but internally we can't switch the order of everything -- (e.g. the double-row cells at the left side), so we need to break up the header into multiple -- parts and only reverse certain parts. local reversed_person_number_header = person_number_header_table_div .. person_number_header_two_row .. person_number_header_pers_num_headers .. person_number_header_table_div .. person_number_header_sg_pl_headers

local table_spec_personal = [=[ Personal forms of {inf_raw} {\op}| class="inflection-table inflection-hi inflection-verb" data-toggle-category="inflection" {person_number_header} {reversed_person_number_header}|{\cl}{notes_clause} ]=]
 * class="hi-sec-div" rowspan=1 colspan=100% | Non-Aspectual
 * class="hi-sec-div" rowspan=1 colspan=100% | Non-Aspectual
 * class="hi-tense-aspect-cell" rowspan=4 colspan=1 | Subjunctive
 * class="hi-polarity-cell" rowspan=3 colspan=1 | +
 * class="hi-mf-cell" | {m}
 * colspan=3 | {subj_ms}
 * colspan=3 | {subj_mp}
 * class="hi-mf-cell" | {f}
 * colspan=3 | {subj_fs}
 * colspan=3 | {subj_fp}
 * class="hi-mf-cell" | {n}
 * {subj_ns}
 * {subj_np}
 * class="hi-polarity-cell" rowspan=1 colspan=1 | –
 * class="hi-mf-cell" | {mfn}
 * colspan=6 | {subj_neg}
 * class="hi-sec-div" rowspan=1 colspan=100% | Perfective
 * class="hi-tense-aspect-cell" rowspan=6 colspan=1 | Unmarked
 * class="hi-polarity-cell" rowspan=3 colspan=1 | +
 * class="hi-mf-cell" | {m}
 * {ind_perf_1sm}
 * {ind_perf_2sm}
 * {ind_perf_3sm}
 * {ind_perf_1pm}
 * {ind_perf_2pm}
 * {ind_perf_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {ind_perf_1sf}
 * {ind_perf_2sf}
 * {ind_perf_3sf}
 * {ind_perf_1pf}
 * {ind_perf_2pf}
 * {ind_perf_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {ind_perf_3sn}
 * {ind_perf_3pn}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | –
 * class="hi-mf-cell" | {m}
 * {ind_perf_neg_1sm}
 * {ind_perf_neg_2sm}
 * {ind_perf_neg_3sm}
 * {ind_perf_neg_1pm}
 * {ind_perf_neg_2pm}
 * {ind_perf_neg_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {ind_perf_neg_1sf}
 * {ind_perf_neg_2sf}
 * {ind_perf_neg_3sf}
 * {ind_perf_neg_1pf}
 * {ind_perf_neg_2pf}
 * {ind_perf_neg_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {ind_perf_neg_3sn}
 * {ind_perf_neg_3pn}
 * class="hi-sec-div" rowspan=1 colspan=100% | Imperfective
 * class="hi-tense-aspect-cell" rowspan=4 colspan=1 | {HAB}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | +
 * class="hi-mf-cell" | {m}
 * {ind_hab_1sm}
 * {ind_hab_2sm}
 * {ind_hab_3sm}
 * rowspan = 2 | {ind_hab_1pm}
 * rowspan = 2 | {ind_hab_2pm}
 * rowspan = 3 | {ind_hab_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {ind_hab_1sf}
 * {ind_hab_2sf}
 * {ind_hab_3sf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {ind_hab_3sn}
 * class="hi-polarity-cell" rowspan=1 colspan=1 | –
 * class="hi-mf-cell" | {mfn}
 * {ind_hab_neg_1s}
 * {ind_hab_neg_2s}
 * {ind_hab_neg_3s}
 * {ind_hab_neg_1p}
 * {ind_hab_neg_2p}
 * {ind_hab_neg_3p}
 * class="hi-tense-aspect-cell" rowspan=4 colspan=1 | {PRS}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | +
 * class="hi-mf-cell" | {m}
 * {prs_prog_1sm}
 * {prs_prog_2sm}
 * {prs_prog_3sm}
 * rowspan=2 | {prs_prog_1pm}
 * rowspan=2 | {prs_prog_2pm}
 * rowspan=3 | {prs_prog_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {prs_prog_1sf}
 * {prs_prog_2sf}
 * {prs_prog_3sf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {prs_prog_3sn}
 * class="hi-polarity-cell" rowspan=1 colspan=1 | –
 * class="hi-mf-cell" | {mfn}
 * {prs_prog_neg_1s}
 * {prs_prog_neg_2s}
 * {prs_prog_neg_3s}
 * {prs_prog_neg_1p}
 * {prs_prog_neg_2p}
 * {prs_prog_neg_3p}
 * class="hi-tense-aspect-cell" rowspan=6 colspan=1 | {PST}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | +
 * class="hi-mf-cell" | {m}
 * {pst_prog_1sm}
 * {pst_prog_2sm}
 * {pst_prog_3sm}
 * {pst_prog_1pm}
 * {pst_prog_2pm}
 * {pst_prog_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {pst_prog_1sf}
 * {pst_prog_2sf}
 * {pst_prog_3sf}
 * {pst_prog_1pf}
 * {pst_prog_2pf}
 * {pst_prog_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {pst_prog_3sn}
 * {pst_prog_3pn}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | —
 * class="hi-mf-cell" | {m}
 * {pst_prog_neg_1sm}
 * {pst_prog_neg_2sm}
 * {pst_prog_neg_3sm}
 * {pst_prog_neg_1pm}
 * {pst_prog_neg_2pm}
 * {pst_prog_neg_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {pst_prog_neg_1sf}
 * {pst_prog_neg_2sf}
 * {pst_prog_neg_3sf}
 * {pst_prog_neg_1pf}
 * {pst_prog_neg_2pf}
 * {pst_prog_neg_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {pst_prog_neg_3sn}
 * {pst_prog_neg_3pn}
 * {pst_prog_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {pst_prog_1sf}
 * {pst_prog_2sf}
 * {pst_prog_3sf}
 * {pst_prog_1pf}
 * {pst_prog_2pf}
 * {pst_prog_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {pst_prog_3sn}
 * {pst_prog_3pn}
 * class="hi-polarity-cell" rowspan=3 colspan=1 | —
 * class="hi-mf-cell" | {m}
 * {pst_prog_neg_1sm}
 * {pst_prog_neg_2sm}
 * {pst_prog_neg_3sm}
 * {pst_prog_neg_1pm}
 * {pst_prog_neg_2pm}
 * {pst_prog_neg_3pm}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {f}
 * {pst_prog_neg_1sf}
 * {pst_prog_neg_2sf}
 * {pst_prog_neg_3sf}
 * {pst_prog_neg_1pf}
 * {pst_prog_neg_2pf}
 * {pst_prog_neg_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {pst_prog_neg_3sn}
 * {pst_prog_neg_3pn}
 * {pst_prog_neg_3pf}
 * - class="hi-row-f"
 * class="hi-mf-cell" | {n}
 * {pst_prog_neg_3sn}
 * {pst_prog_neg_3pn}
 * {pst_prog_neg_3sn}
 * {pst_prog_neg_3pn}
 * {pst_prog_neg_3pn}
 * {pst_prog_neg_3pn}

local pres_impf_table = [=[]=]

local pres_impf_table_missing = [=[]=]

local presum_table = [=[]=]

local combined_subj = [=[]=]

local split_subj = [=[]=]

local notes_template = [===[ {footnote} ]===]

local forms = alternant_multiword_spec.forms

local function make_gender_abbr(title, text) return ' ' .. text .. ' '	end local function make_tense_aspect_abbr(title, text) local template = [=[ {text} ]=] return m_string_utilities.format(template, {title = title, text = text}) end forms.m = make_gender_abbr("masculine gender", "m") forms.f = make_gender_abbr("feminine gender", "f") forms.n = make_gender_abbr("feminine gender", "n") forms.mfn = forms.m .. " " .. forms.f .. " " .. forms.n	forms.s = make_gender_abbr("singular number", "s") forms.p = make_gender_abbr("plural number", "p") forms.dir = make_gender_abbr("direct", "dir") forms.obl = make_gender_abbr("oblique", "obl") forms.HAB = make_tense_aspect_abbr("Habitual", "HAB") forms.PERF = make_tense_aspect_abbr("Perfect", "PERF") forms.SUBJ = make_tense_aspect_abbr("Subjunctive", "SUBJ") forms.IMPF = make_tense_aspect_abbr("Imperfect", "IMPF") forms.PST = make_tense_aspect_abbr("Past", "PST") forms.FUT = make_tense_aspect_abbr("Future", "FUT") forms.PRS = make_tense_aspect_abbr("Present", "PRS") forms.PRS_FUT = make_tense_aspect_abbr("Present/Future", "PRS FUT") forms.PRS_PST = make_tense_aspect_abbr("Present/Past", "PRS PST") forms.PRS_PST_FUT = make_tense_aspect_abbr("Present/Past/Future", "PRS PST FUT") forms.inf_raw = tag_text(forms.lemma)

-- Now format the impersonal table. forms.footnote = forms.footnote_impers forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" local formatted_table_impers = m_string_utilities.format(table_spec_impersonal, forms)

-- Now format the personal table. forms.footnote = forms.footnote_pers forms.notes_clause = forms.footnote ~= "" and m_string_utilities.format(notes_template, forms) or "" if forms.ind_pres_1s ~= "—" then -- होना forms.subj_table = m_string_utilities.format(split_subj, forms) forms.pres_impf_table = m_string_utilities.format(pres_impf_table, forms) forms.presum_table = m_string_utilities.format(presum_table, forms) else forms.subj_table = m_string_utilities.format(combined_subj, forms) forms.pres_impf_table = pres_impf_table_missing forms.presum_table = "" end forms.person_number_header = person_number_header forms.reversed_person_number_header = reversed_person_number_header local formatted_table_pers = m_string_utilities.format(table_spec_personal, forms)

-- Concatenate both. return require("Module:TemplateStyles")("Module:hi-verb/style.css") .. formatted_table_impers .. formatted_table_pers end

-- Implementation of template 'hi-verb cat'. -- NOTE: Not currently used. function export.catboiler(frame) local SUBPAGENAME = mw.title.getCurrentTitle.subpageText local params = { [1] = {},	}	local args = m_para.process(frame:getParent.args, params)

local function get_pos local pos = rmatch(SUBPAGENAME, "^Marathi.- ([^ ]*)s ") if not pos then pos = rmatch(SUBPAGENAME, "^Marathi.- ([^ ]*)s$") end if not pos then error("Invalid category name, should be e.g. \"Marathi verbs with ...\" or \"Marathi ... verbs\"") end return pos end

local function get_sort_key local pos, sort_key = rmatch(SUBPAGENAME, "^Marathi.- ([^ ]*)s with (.*)$") if sort_key then return sort_key end pos, sort_key = rmatch(SUBPAGENAME, "^Marathi ([^ ]*)s (.*)$") if sort_key then return sort_key end return rsub(SUBPAGENAME, "^Marathi ", "") end

local cats = {}, pos

-- Insert the category CAT (a string) into the categories. String will -- have "Marathi " prepended and ~ substituted for the plural part of speech. local function insert(cat, atbeg) local fullcat = "Marathi " .. rsub(cat, "~", pos .. "s") if atbeg then table.insert(cats, 1, fullcat) else table.insert(cats, fullcat) end end

local maintext while true do		if args[1] then maintext = "~ " .. args[1] pos = get_pos break end

error("Unrecognized Marathi verb category name") end

insert("~|" .. get_sort_key, "at beginning")

local categories = {} for _, cat in ipairs(cats) do		table.insert(categories, "") end

return "This category contains Marathi " .. rsub(maintext, "~", pos .. "s") .. "\n" .. mw.getCurrentFrame:expandTemplate{title="mr-categoryTOC", args={}} .. table.concat(categories, "") end

-- Externally callable function to parse and conjugate a verb given user-specified arguments. -- Return value is WORD_SPEC, an object where the conjugated forms are in `WORD_SPEC.forms` -- for each slot. If there are no values for a slot, the slot key will be missing. The value -- for a given slot is a list of objects {form=FORM, translit=TRANSLIT, footnotes=FOOTNOTES}. function export.do_generate_forms(parent_args, pos, from_headword, def) local params = { [1] = {},		title = {}, }

if from_headword then params["lemma"] = {list = true} params["id"] = {} end

local args = m_para.process(parent_args, params) local PAGENAME = mw.title.getCurrentTitle.text

if not args[1] then if PAGENAME == "mr-conj" then args[1] = def or "ऐकणे" else args[1] = PAGENAME -- If pagename has spaces in it, add links around each word if args[1]:find(" ") then args[1] = "" .. rsub(args[1], " ", " ") .. "" end end end local parse_props = { parse_indicator_spec = parse_indicator_spec, lang = lang, transliterate_respelling = com.transliterate_respelling, allow_default_indicator = true, allow_blank_lemma = true, }	local alternant_multiword_spec = iut.parse_inflected_text(args[1], parse_props) alternant_multiword_spec.title = args.title alternant_multiword_spec.pos = pos or "verbs" alternant_multiword_spec.args = args com.normalize_all_lemmas(alternant_multiword_spec) detect_all_indicator_specs(alternant_multiword_spec) local inflect_props = { slot_table = all_verb_slots, lang = lang, inflect_word_spec = conjugate_verb, -- Return the variant code that was added to ensure only parallel variants in -- multiword expressions like हिलना-डुलना get generated. See com.add_variant_codes -- for more information. get_variants = alternant_multiword_spec.multiword and com.get_variants or nil, -- We add links around the generated verbal forms rather than allow the entire multiword -- expression to be a link, so ensure that user-specified links get included as well. include_user_specified_links = true, }	iut.inflect_multiword_or_alternant_multiword_spec(alternant_multiword_spec, inflect_props) compute_categories_and_annotation(alternant_multiword_spec) return alternant_multiword_spec end

-- Entry point for. Template-callable function to parse and conjugate a verb given -- user-specified arguments and generate a displayable table of the conjugated forms. function export.show(frame) local parent_args = frame:getParent.args local alternant_multiword_spec = export.do_generate_forms(parent_args) show_forms(alternant_multiword_spec) return make_table(alternant_multiword_spec) .. require("Module:utilities").format_categories(alternant_multiword_spec.categories, lang) end

-- Concatenate all forms of all slots into a single string of the form -- "SLOT=FORM,FORM,...|SLOT=FORM,FORM,...|...". Each FORM is either a string in Devanagari or -- (if manual translit is present) a specification of the form "FORM//TRANSLIT" where FORM is the -- Devanagari representation of the form and TRANSLIT its manual transliteration. Embedded pipe symbols -- (as might occur in embedded links) are converted to <!>. If INCLUDE_PROPS is given, also include -- additional properties (currently, none). This is for use by bots. local function concat_forms(alternant_spec, include_props) local ins_text = {} for slot, _ in pairs(verb_slots_with_linked) do		local formtext = iut.concat_forms_in_slot(alternant_spec.forms[slot]) if formtext then table.insert(ins_text, slot .. "=" .. formtext) end end return table.concat(ins_text, "|") end

-- Template-callable function to parse and conjugate a verb given user-specified arguments and return -- the forms as a string of the same form as documented in concat_forms above. function export.generate_forms(frame) local include_props = frame.args["include_props"] local parent_args = frame:getParent.args local alternant_spec = export.do_generate_forms(parent_args) return concat_forms(alternant_spec, include_props) end

return export