Module:sa-verb

local export = {}

local m_para = require("Module:parameters") local m_links = require("Module:links") local m_utils = require("Module:utilities") local lang = require("Module:languages").getByCode("sa") local m_script_utils = require("Module:script utilities") local sa_verb_data = require("Module:sa-verb/data") local SLP_to_IAST = require("Module:sa-utilities/translit/SLP1-to-IAST") local IAST_to_SLP = require("Module:sa-utilities/translit/IAST-to-SLP1") local sa_utils = require("Module:sa-utilities") local sa_utils_translit = require("Module:sa-utilities/translit") local columns = require("Module:columns") local PAGENAME = mw.title.getCurrentTitle.text

local gsub = mw.ustring.gsub local match = mw.ustring.match local split = mw.text.split

local names = { ["pres"] = "Present", ["impf"] = "Imperfect", ["fut"] = "Future", ["pfut"] = "Periphrastic Future", ["cond"] = "Conditional", ["aor"] = "Aorist", ["bene"] = "Benedictive/Precative", ["perf"] = "Perfect", ["indic"] = "Indicative", ["imper"] = "Imperative", ["optat"] = "Optative/Potential", ["part"] = "Participles", ["inf"] = "Infinitive", ["gerund"] = "Gerund", ["gerundive_mn"] = "Masculine/Neuter Gerundive", ["gerundive_f"] = "Feminine Gerundive", ["part_mn"] = "Masculine/Neuter Past Passive Participle", ["part_f"] = "Feminine Past Passive Participle", ["part_av_mn"] = "Masculine/Neuter Past Active Participle", ["part_av_f"] = "Feminine Past Active Participle", ["nonf"] = "Nonfinite Forms" }

local tenses = { ["pres"] = { ["moods"] = { "indic", "imper", "optat", "part" }, ["voices"] = { "av", "mv" } },	["impf"] = { ["moods"] = { "indic" }, ["voices"] = { "av", "mv" } },	["fut"] = { ["moods"] = { "indic", "part" }, ["voices"] = { "av", "mv" } },	["pfut"] = { ["moods"] = { "indic" }, ["voices"] = { "av", "mv" } },	["cond"] = { ["moods"] = { "indic" }, ["voices"] = { "av", "mv" } },	["aor"] = { ["moods"] = { "indic" }, ["voices"] = { "av", "mv" } },	["bene"] = { ["moods"] = { "optat" }, ["voices"] = { "av", "mv" } },	["perf"] = { ["moods"] = { "indic", "part" }, ["voices"] = { "av", "mv" } },	["nonf"] = { ["moods"] = { "inf", "gerund", "gerundive_mn", "gerundive_f", "part_mn", "part_f", "part_av_mn", "part_av_f" } } }

local numbers = {"1_s", "2_s", "3_s", "1_d", "2_d", "3_d", "1_p", "2_p", "3_p"}

local super_nums = { [1] = "¹",   [2] = "²",    [3] = "³",    [4] = "⁴",    [5] = "⁵",    [6] = "⁶",    [7] = "⁷",    [8] = "⁸",    [9] = "⁹",    [0] = "⁰" }

local function to_super(num) local annotation = gsub(num, ".", super_nums) return annotation end

local function get_form_note_tags(form_notes, data) local output = {} if type(form_notes) ~= "table" then form_notes = {form_notes} end for _, form_note in ipairs(form_notes) do       if type(data.form_notes[form_note]) ~= "number" then table.insert(data.form_notes_out, form_note) data.form_notes[form_note] = #data.form_notes_out end table.insert(output, to_super(data.form_notes[form_note])) end return table.concat(output) end

local function make_header(args, data, title_models, sc_cache) local width = 70 local title = names[args.tense] .. ": " .. title_models

local header = { '{| class="inflection-table vsSwitcher" data-toggle-category="inflection" style="background:#F9F9F9; text-align:center; border: 1px solid #CCC; width: ' ..           width .. 'em"\n' }   table.insert(header, '|- style="background: #d9ebff;"\n') table.insert(       header,        '! class="vsToggleElement" style="text-align: left;" colspan="' .. args.colspan .. '" | ' .. title .. "\n"    ) table.insert(header, '|- class="vsHide"\n') table.insert(header, '! style="background:#eff7ff" rowspan="2" |\n')

table.insert(header, '! colspan="3" style="background:#eff7ff" | Active\n') if args.tense == "pfut" then table.insert(header, '! colspan="3" style="background:#eff7ff" | Middle\n') else table.insert(header, '! colspan="3" style="background:#eff7ff" | Mediopassive\n') end

table.insert(header, '|- class="vsHide"\n') local size = 2 for i = 1, size, 1 do       table.insert(header, '! style="background:#eff7ff" | Singular\n') table.insert(header, '! style="background:#eff7ff" | Dual\n') table.insert(header, '! style="background:#eff7ff" | Plural\n') end

return table.concat(header) end

local function make_cell(args, data, tag, arg_tag, sc_cache, col_span, voice) local forms, links, trs = {}, {}, {} if (not match(args.n, "a") and voice == "av") or (not match(args.n, "m") and voice == "mv") or (not match(args.n, "p") and voice == "pv") then forms = {"-"} elseif args[arg_tag] then forms = mw.text.split(args[arg_tag], "%s*[,]%s*") else forms = data.forms[tag] end

if not forms then return "| -\n" end for i, form in ipairs(forms) do   	form = sc_cache.tr(form) form = gsub(form, 'V', 'f') form = gsub(form, "Z", "x") -- a superscript number at the end of manually added forms should not be part of the (linked) form if match(form, "[¹²³⁴⁵⁶⁷⁸⁹]$") then extra_note_tag = gsub(form, "^.+(.)$", "%1") form = gsub(form, ".$", "") else extra_note_tag = "" end local form_note_tag = get_form_note_tags(forms["note" .. i] or {}, data) if form == "" then -- in case of form reduced to zero by 'novedic' parameter -- do nothing elseif form == "-" then table.insert(links, form) table.insert(trs, form) else table.insert(           	links,            	m_links.full_link({term = sc_cache.reverse_tr(form), tr = "-", lang = lang, sc = sc_cache.sc}) ..                	form_note_tag .. extra_note_tag    		) table.insert(trs, SLP_to_IAST.tr(form) .. form_note_tag .. extra_note_tag) end end

return table.concat { "| ",       "colspan = ", col_span, " | ",       table.concat(links, " / "), " ",       m_script_utils.tag_translit(table.concat(trs, " / "), lang, "default", 'style="color: #888;"'), "\n" } end

local function format_notes(args, data) local output = { '|- class="vsHide"', '| style="background-color:#eff7ff; font-style:italic;border-top:double #888;" | Notes', '| style="text-align:left;border-top:double #888;" colspan="' .. args.colspan .. '" |' }   if #data.form_notes_out > 0 or #data.general_notes > 0 or #args.note > 0 then for i, form_note in ipairs(data.form_notes_out) do           table.insert(output, "* " .. to_super(i) .. form_note) end for _, general_note in ipairs(data.general_notes) do           table.insert(output, "* " .. general_note) end for _, note in ipairs(args.note) do           table.insert(output, "* " .. note) end return table.concat(output, "\n") .. "\n" else return "" end end

local function make_nonf_table(args, data, output, sc_cache) local moods = tenses[args.tense].moods if data.forms["part_mn"] ~= "-" and data.forms["part_mn"] ~= nil then data.forms["part_av_mn"] = { data.forms["part_mn"][1] .. "vat" } -- these two formed directly from the given forms always data.forms["part_av_f"] = { data.forms["part_mn"][1] .. "vatī" } else data.forms["part_av_mn"] = { "-" } data.forms["part_av_f"] = { "-" } end local title_models = "" local arg_before = false local tag = moods[1] local arg_tag = moods[1] local forms if args[arg_tag] then forms = mw.text.split(args[arg_tag], "%s*[,]%s*") else forms = data.forms[tag] end for i, title_lem in ipairs(forms) do       	title_lem = sc_cache.tr(title_lem) title_lem = gsub(title_lem, 'V', 'f') title_lem = gsub(title_lem, "Z", "x") if i > 1 then title_models = title_models .. " or " end title_models = title_models .. m_links.full_link(                   {                        term = nil,                        alt = sc_cache.reverse_tr(title_lem),                        tr = SLP_to_IAST.tr(title_lem),                        lang = lang,                        sc = sc_cache.sc                    }                ) end local title = names[args.tense] .. ": " .. title_models table.insert(output, 'Forms of Sanskrit verbs are numerous and complicated. The following conjugation shows only a subset of all forms and should be treated as a guide. \n') table.insert(           output,            '{| class="inflection-table vsSwitcher" data-toggle-category="inflection" style="background:#F9F9F9; text-align:center; border: 1px solid #CCC; width: 40em"\n'        ) table.insert(output, '|- style="background: #d9ebff;"\n') table.insert(output, '! class="vsToggleElement" style="text-align: left;" colspan="4" | ' .. title .. "\n") table.insert(output, '|- class="vsHide"\n') table.insert(output, '! style="background:#eff7ff" |\n')

table.insert(output, '! colspan="2" style="background:#eff7ff" | Undeclinable\n') table.insert(output, '|- class="vsHide"\n') for _, mood in ipairs(moods) do           if mood == "gerundive_mn" then table.insert(output, '! style="background:#eff7ff" |\n') table.insert(output, '! colspan="2" style="background:#eff7ff" | Participles\n') table.insert(output, '|- class="vsHide"\n') end table.insert(output, '! style="background-color:#eff7ff;" | ' .. names[mood] .. "\n") local tag = mood local arg_tag = mood table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 1)) table.insert(output, '|- class="vsHide"\n') end table.insert(output, format_notes(args, data)) table.insert(output, "|}") if not args.nocat and #data.categories > 0 then table.insert(output, m_utils.format_categories(data.categories, lang)) end return output end

local function make_voice(title_models, args, data, sc_cache, voice) local moods = tenses[args.tense].moods local tag = moods[1] .. "_" .. voice .. "_3_s" local arg_tag = moods[1] .. "_" .. voice .. "_3_s" local forms if args[arg_tag] then forms = mw.text.split(args[arg_tag], "%s*[,]%s*") else forms = data.forms[tag] end if #title_models > 0 then table.insert(title_models, ", ") end for i, title_lem in ipairs(forms or {"-"}) do   	title_lem = sc_cache.tr(title_lem) if i > 1 then table.insert(title_models, " or ") end title_lem = gsub(title_lem, 'V', 'f') title_lem = gsub(title_lem, "Z", "x") title_lem = gsub(title_lem, "[¹²³⁴⁵⁶⁷⁸⁹]", "") table.insert(title_models,            m_links.full_link( {                   term = nil, alt = sc_cache.reverse_tr(title_lem), tr = SLP_to_IAST.tr(title_lem), lang = lang, sc = sc_cache.sc               } )       )    end end

local function make_table_verb(args, data, output, sc_cache) local moods = tenses[args.tense].moods

if args.tense == "nonf" then table.insert(data.categories, "Sanskrit verbs with nonfinite forms") return make_nonf_table(args, data, output, sc_cache) end -- local voices = tenses[args.tense].voices local voices = tenses[args.tense].voices local numbers = {"s", "d", "p"} local people = {{"3", "Third"}, {"2", "Second"}, {"1", "First"}}

local title_models = {} if match(args.n, "a") then make_voice(title_models, args, data, sc_cache, "av") end if match(args.n, "m") then make_voice(title_models, args, data, sc_cache, "mv") end if match(args.n, "p") then make_voice(title_models, args, data, sc_cache, "pv") end

table.insert(output, make_header(args, data, table.concat(title_models, ""), sc_cache)) table.insert(output, '|- class="vsHide"\n')

for _, mood in ipairs(moods) do       table.insert(output, '! style="background:#eff7ff" colspan="' .. args.colspan .. '" | ' .. names[mood] .. "\n") table.insert(output, '|- class="vsHide"\n') if mood == "part" then table.insert(output, '| style="background-color:#eff7ff; font-style:italic;" | \n') for _, voice in ipairs(voices) do               local tag = mood .. "_" .. voice local arg_tag = mood .. "_" .. voice table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 3, voice)) end else for i, person_pair in ipairs(people) do               local person, person_name = person_pair[1], person_pair[2] table.insert(output, '| style="background-color:#eff7ff; font-style:italic;" | ' .. person_name .. "\n") for _, voice in ipairs(voices) do                   for _, number in ipairs(numbers) do                        local tag = mood .. "_" .. voice .. "_" .. person .. "_" .. number local arg_tag = mood .. "_" .. voice .. "_" .. person .. "_" .. number table.insert(output, make_cell(args, data, tag, arg_tag, sc_cache, 1, voice)) end end table.insert(output, '|- class="vsHide"\n') end end end

table.insert(output, format_notes(args, data)) table.insert(output, "|}") if not args.nocat and #data.categories > 0 then table.insert(output, m_utils.format_categories(data.categories, lang)) end return output end

local function get_sc_details(args) local sc, scCode if args.sc then sc = require("Module:scripts").getByCode(args.sc) scCode = args.sc   else sc = lang:findBestScript(args.lemma) scCode = sc:getCode if scCode == "None" then sc = lang:findBestScript(PAGENAME) scCode = sc:getCode if scCode == "None" then -- error("Script code was not specified or detected.") -- Suppress; this fixes issues where docs pages get errors return get_sc_details({sc = "Deva"}) end end end

local tr, reverse_tr = sa_utils_translit.retrieve_tr_modules(scCode) return {tr = tr, reverse_tr = reverse_tr, sc = sc, scCode = scCode} end

--[=[Splits a string by commas and displays ]=] function export.split(frame) local args = frame.args args.lemma = args[1] local sc_cache = get_sc_details(args) if not sc_cache then return "Module failed on page " end local output = {} local splitted = mw.text.split(frame.args[1], ",") if args.b then table.insert(output, "") end for i, form in ipairs(splitted) do		if i > 1 and not args.b and not args.csv then table.insert(output, " ") end if i > 1 and args.csv then table.insert(output, ", ") end if args.b then table.insert(output, "") end slp = sc_cache.tr(form) if require("Module:scripts").findBestScriptWithoutLang(form):getCode == "Latn" then tr = form else tr = nil end table.insert(           output,            m_links.full_link({term = sc_cache.reverse_tr(slp), tr = tr, lang = lang, sc = sc_cache.sc})        ) if args.b then table.insert(output, "") end end if args.b then table.insert(output, "") end return table.concat(output) end

function add_form_to_output(output, form, tense, sc_cache) local function term_already_linked(term) -- This function is copied from Module:columns, and probably needs to be updated whenever that one is		-- FIXME: "<span" is an ugly hack to prevent double-linking of terms already run through : -- Thread:User talk:CodeCat/MewBot adding lang to column templates return term:find("<span") end if term_already_linked(form) then table.insert(output, form) else slp = sc_cache.tr(form) local tr = nil if require("Module:scripts").findBestScriptWithoutLang(form):getCode == "Latn" then tr = form end local pos = nil if tense ~= nil then pos = ' ' .. tense .. ' '		end table.insert(           output,            m_links.full_link({term = sc_cache.reverse_tr(slp), tr = tr, pos = pos, lang = lang, sc = sc_cache.sc})        ) end end

function add_root_deriv(output, param, tense, sc_cache) local function term_already_linked(term) -- This function is copied from Module:columns, and probably needs to be updated whenever that one is		-- FIXME: "<span" is an ugly hack to prevent double-linking of terms already run through : -- Thread:User talk:CodeCat/MewBot adding lang to column templates return term:find("<span") end if term_already_linked(param) then add_form_to_output(output, param, tense, sc_cache) else local splitted = mw.text.split(param, ",") for i, form in ipairs(splitted) do			add_form_to_output(output, form, tense, sc_cache) end end end

function handle_deriv_param(output, frame, param_name, tense, sc_cache) if frame.args[param_name] ~= nil then local pos = frame.args[param_name .. "_pos"] or tense add_root_deriv(output, frame.args[param_name], pos, sc_cache) local i = 2 while frame.args[param_name .. i] ~= nil do pos = frame.args[param_name .. i .. "_pos"] or tense add_root_deriv(output, frame.args[param_name .. i], pos, sc_cache) i = i + 1 end end end

function export.rootderiv(frame) frame.args = frame:getParent.args local output = ' Primary Verbal Forms ' local sc_cache = get_sc_details(frame.args) if not sc_cache then return "Module failed on page " end -- if frame.args["pres"] == nil then return "Module failed on page - no present tense" end local args = { [1] = "sa" } handle_deriv_param(args, frame, "pres", "Present", sc_cache) handle_deriv_param(args, frame, "opt", "Optative Mood", sc_cache) handle_deriv_param(args, frame, "imper", "Imperative Mood", sc_cache) handle_deriv_param(args, frame, "impf", "Imperfect", sc_cache) handle_deriv_param(args, frame, "fut", "Future", sc_cache) handle_deriv_param(args, frame, "pfut", "Periphrastic Future", sc_cache) handle_deriv_param(args, frame, "cond", "Conditional", sc_cache) handle_deriv_param(args, frame, "aor", "Aorist", sc_cache) handle_deriv_param(args, frame, "bene", "Benedictive", sc_cache) handle_deriv_param(args, frame, "perf", "Perfect", sc_cache) output = output .. columns.display_from({ ["columns"] = 4 }, args, nil) args = { [1] = "sa" } handle_deriv_param(args, frame, "pass", "Passive", sc_cache) handle_deriv_param(args, frame, "pass_aor", "Passive Aorist", sc_cache) handle_deriv_param(args, frame, "caus", "Causative", sc_cache) handle_deriv_param(args, frame, "caus_aor", "Causative Aorist", sc_cache) handle_deriv_param(args, frame, "pass_caus", "Passive of Causative", sc_cache) handle_deriv_param(args, frame, "pass_aor_caus", "Passive Aorist of Causative", sc_cache) handle_deriv_param(args, frame, "desid", "Desiderative", sc_cache) handle_deriv_param(args, frame, "desid_aor", "Desiderative Aorist", sc_cache) handle_deriv_param(args, frame, "desid_fut", "Desiderative Future", sc_cache) handle_deriv_param(args, frame, "pass_desid", "Passive of Desiderative", sc_cache) handle_deriv_param(args, frame, "caus_desid", "Causative of Desiderative", sc_cache) handle_deriv_param(args, frame, "desid_caus", "Desiderative of Causative", sc_cache) handle_deriv_param(args, frame, "desid_desid", "Desiderative of Desiderative", sc_cache) handle_deriv_param(args, frame, "intens", "Intensive", sc_cache) handle_deriv_param(args, frame, "int", "Intensive", sc_cache) handle_deriv_param(args, frame, "freq", "Intensive", sc_cache) if args[2] ~= nil then output = output .. ' Secondary Forms ' output = output .. columns.display_from({ ["columns"] = 4 }, args, nil) end args = { [1] = "sa" } handle_deriv_param(args, frame, "inf", "Infinitive", sc_cache) handle_deriv_param(args, frame, "gerund", "Gerund", sc_cache) handle_deriv_param(args, frame, "gerundive", "Gerundive", sc_cache) handle_deriv_param(args, frame, "ppp", "Past Participle", sc_cache) handle_deriv_param(args, frame, "mpp", "Mediopassive Participle", sc_cache) handle_deriv_param(args, frame, "ap", "Active Participle", sc_cache) handle_deriv_param(args, frame, "caus_ppp", "Causative Past Participle", sc_cache) handle_deriv_param(args, frame, "caus_inf", "Causative Infinitive", sc_cache) handle_deriv_param(args, frame, "caus_gerund", "Causative Gerund", sc_cache) handle_deriv_param(args, frame, "caus_gerundive", "Causative Gerundive", sc_cache) handle_deriv_param(args, frame, "desid_part", "Desiderative Participle", sc_cache) handle_deriv_param(args, frame, "desid_caus_part", "Desiderative Participle of Causative", sc_cache) handle_deriv_param(args, frame, "desid_part_caus", "Desiderative Participle of Causative", sc_cache) if args[2] ~= nil then output = output .. ' Non-Finite Forms ' output = output .. columns.display_from({ ["columns"] = 4 }, args, nil) end local n_args = { [1] = "sa" } local v_args = { [1] = "sa" } local switched_to_verbs = false for k, v in ipairs(frame.args) do		v = gsub(v, "^%s*(.-)%s*$", "%1") if v == "/" then switched_to_verbs = true else if switched_to_verbs then add_form_to_output(v_args, v, nil, sc_cache) else add_form_to_output(n_args, v, nil, sc_cache) end end end if n_args[2] ~= nil then output = output .. ' Derived Nominal Forms ' output = output .. columns.display_from({ ["columns"] = 4 }, n_args, nil) end if v_args[2] ~= nil then output = output .. ' Prefixed Root Forms ' output = output .. columns.display_from({ ["columns"] = 4 }, v_args, nil) end return output end

function export.show(frame) local params = { lemma = {default = PAGENAME}, n = {default = "amp"}, sc = {}, [1] = {required = true}, [2] = {alias_of = "lemma"}, [3] = {},       [4] = {},        auto_sandhi = {default = true, type = "boolean"}, set = {default = false, type = "boolean" }, -- set vs anit roots novedic = {default = false, type = "boolean" }, -- disable extra Vedic forms note = {list = true}, o = {default = false, type = "boolean"}, mono = {default = false, type = "boolean"}, -- internal sandhi arg j_to_z = {default = false, type = "boolean"}, -- internal sandhi arg h_to_g = {default = false, type = "boolean"}, -- internal sandhi arg diaspirate = {default = false, type = "boolean"}, -- internal sandhi arg no_syncope = {default = false, type = "boolean"}, -- internal sandhi arg accent_override = {default = false, type = "boolean"}, -- internal sandhi arg no_retroflex_root_s = {default = true, type = "boolean"}, -- internal sandhi arg; default = true only for verbs aor = {default = nil}, -- aorist class (if not automatically detected) class = {default = nil}, -- present class (if not automatically detected) extra_1p_stem = {default = nil}, -- stem for 1p if different from 3p and (e.g.) 2p }   for tense, tense_l in pairs(tenses) do    	if tense == "nonf" then for _, mood in pairs(tense_l["moods"]) do   			params[mood] = {} end else for _, mood in pairs(tense_l["moods"]) do   			for _, voice in pairs(tense_l["voices"]) do    				if mood == "part" then params[mood .. "_" .. voice] = {} else for _, number in pairs(numbers) do params[mood .. "_" .. voice .. "_" .. number] = {} end end end end end end local data = { forms = {}, categories = {}, decl_type = nil, form_notes = {}, form_notes_out = {}, general_notes = {} }   local args = m_para.process(frame:getParent.args, params)

local sc_cache = get_sc_details(args) if not sc_cache then return "Module failed on page " end args.lemma = sc_cache.tr(args.lemma) args.has_accent = match(args.lemma, sa_utils.accent) if args[3] ~= nil then args[3] = sc_cache.tr(args[3]) end if args[4] ~= nil then args[4] = sc_cache.tr(args[4]) end

local output = {} args.tense = args[1] -- conjugate(args, data) args.colspan = 7 args.n = gsub(args.n, "p", "") args.strong_lemma = args.lemma -- split(args.lemma, ",") args.weak_lemma = args[3] -- and split(args[3], ",") args.passive_lemma = args[4] -- and split(args[4], ",") if not args.o then sa_verb_data[args.tense](args, data) end if args.tense == "pfut" then if args.n and args.n == "a" then -- do nothing else args.note = {"The middle forms of the periphrastic future rarely occur."} end end make_table_verb(args, data, output, sc_cache) return table.concat(output) end

return export