Module:User:Vitalik/inflection/units/ru-noun

-- Inflection unit for Russian nouns -- Version 2.0.38 -- Date: 2015-08-25

local dev_prefix = '' dev_prefix = 'User:Vitalik/' -- comment this on active version

local export = {} local _ = require('Module:' .. dev_prefix .. 'inflection-tools') -- local lang = require("Module:languages").getByCode("ru") -- local m_links = require("Module:links") -- local strutils = require("Module:string utilities")

-- constants: local unstressed = 1 local stressed = 2

function export.template(base, args) return dev_prefix .. 'ru-decl-noun-table-z' end

local function get_standard_endings return { m = { -- masculine endings hard = { nom_sg = '', gen_sg = 'а', dat_sg = 'у', ins_sg = 'ом', nom_pl = 'ы', gen_pl = {'ов', 'ов'}, -- possibly we can join them together again (m_hard_gen_pl stressed and unstressed) },			soft = { nom_sg = 'ь', gen_sg = 'я', dat_sg = 'ю', ins_sg = {'ем', 'ём'}, nom_pl = 'и', gen_pl = {'ей', 'ей'}, },		},		f = { -- feminine endings hard = { nom_sg = 'а', gen_sg = 'ы', dat_sg = 'е', acc_sg = 'у', ins_sg = 'ой', nom_pl = 'ы', gen_pl = {, }, },			soft = { nom_sg = 'я', gen_sg = 'и', dat_sg = {'е', 'е'}, acc_sg = 'ю', ins_sg = {'ей', 'ёй'}, nom_pl = 'и', gen_pl = {'ь', 'ей'}, },		},		n = { -- neuter endings hard = { nom_sg = 'о', gen_sg = 'а', dat_sg = 'у', ins_sg = 'ом', nom_pl = 'а', gen_pl = {, }, },			soft = { nom_sg = 'е', -- was: {'е', 'ё'} gen_sg = 'я', dat_sg = 'ю', ins_sg = {'ем', 'ём'}, nom_pl = 'я', gen_pl = {'ь', 'ей'}, },		},		common = { -- common endings hard = { prp_sg = {'е', 'е'}, dat_pl = 'ам', ins_pl = 'ами', prp_pl = 'ах', },			soft = { prp_sg = {'е', 'е'}, dat_pl = 'ям', ins_pl = 'ями', prp_pl = 'ях', },		}	} end

local function get_stress_flags(stress_type) return { stem = { sg    = _.equals(stress_type, {"a", "c", "e"})                          and stressed or unstressed, acc_sg = _.equals(stress_type, {"a", "c", "e", "d'", "f'"})             and stressed or unstressed, ins_sg = _.equals(stress_type, {"a", "c", "e", "b'", "f''"})            and stressed or unstressed, pl    = _.equals(stress_type, {"a", "d", "d'"})                         and stressed or unstressed, nom_pl = _.equals(stress_type, {"a", "d", "d'", "e", "f", "f'", "f''"}) and stressed or unstressed, },		ending = { sg    = _.equals(stress_type, {"b", "b'", "d", "d'", "f", "f'", "f''"}) and stressed or unstressed, acc_sg = _.equals(stress_type, {"b", "b'", "d", "f", "f''"})            and stressed or unstressed, ins_sg = _.equals(stress_type, {"b", "d", "d'", "f", "f'"})             and stressed or unstressed, pl    = _.equals(stress_type, {"b", "b'", "c", "e", "f", "f'", "f''"})  and stressed or unstressed, nom_pl = _.equals(stress_type, {"b", "b'", "c"})                        and stressed or unstressed, }	} end

local function get_stem_type(z) if _.endswith(z.stem, '[гкх]') then z.stem_type = 'velar' elseif _.endswith(z.stem, '[жчшщ]') then z.stem_type = 'sibilant' elseif _.endswith(z.stem, 'ц') then z.stem_type = 'letter-ц' elseif _.endswith(z.stem, {'[йь]', '[аоеёуыэюя]'}) then z.stem_type = 'vowel' elseif _.endswith(z.stem, 'и') then z.stem_type = 'letter-и' else if z.gender == 'm' then if z.stem == z.word or _.endswith(z.word, 'ы') then z.stem_type = 'hard' elseif _.endswith(z.word, 'путь') then z.stem_type = 'm-3rd' elseif _.endswith(z.word, 'ь') or _.endswith(z.word, 'и') then z.stem_type = 'soft' elseif _.endswith(z.word, 'а') then z.gender = 'f'				z.stem_type = 'hard' elseif _.endswith(z.word, 'я') then z.gender = 'f'				z.stem_type = 'soft' end elseif z.gender == 'f' then if _.endswith(z.word, 'а') or _.endswith(z.word, 'ы') then z.stem_type = 'hard' elseif _.endswith(z.word, 'я') or _.endswith(z.word, 'и') then z.stem_type = 'soft' elseif _.endswith(z.word, 'ь') then -- conflict in pl				z.stem_type = 'f-3rd' end elseif z.gender == 'n' then if _.endswith(z.word, 'о') or _.endswith(z.word, 'а') then z.stem_type = 'hard' elseif _.endswith(z.word, 'е') or _.endswith(z.word, 'я') then z.stem_type = 'soft' elseif _.endswith(z.word, 'мя') or _.endswith(z.word, 'мена') then z.stem_type = 'n-3rd' end end end if z.gender == 'm' then if _.endswith(z.word, {'а', 'я'}) then z.gender = 'f'		end end

if z.gender == 'f' and z.stem_type == 'sibilant' and _.endswith(z.word, 'ь') then z.stem_type = 'f-3rd-sibilant' end if z.stem_type == '' then z.stem_type = 'hard' end end

local function change_endings_for_other_stem_types(z) if _.equals(z.stem_type, {'velar', 'sibilant'}) then -- Replace "ы" to "и" z.e['f']['hard']['gen_sg'] = 'и' z.e['m']['hard']['nom_pl'] = 'и' z.e['f']['hard']['nom_pl'] = 'и' end

if _.equals(z.stem_type, {'sibilant', 'letter-ц'}) then -- Replace unstressed "о" to "е" if z.stress_flags['ending']['sg'] == unstressed then z.e['n']['hard']['nom_sg'] = 'е' end if z.stress_flags['ending']['ins_sg'] == unstressed then z.e['m']['hard']['ins_sg'] = 'ем' z.e['n']['hard']['ins_sg'] = 'ем' z.e['f']['hard']['ins_sg'] = 'ей' end if z.stress_flags['ending']['pl'] == unstressed then z.e['m']['hard']['gen_pl'] = {'ев', 'ев'} -- TODO: should we change stressed value here? end end

if _.equals(z.stem_type, 'sibilant') then -- Replace "ов", "ев", "ёв" and null to "ей" z.e['m']['hard']['gen_pl'] = {'ей', 'ей'} z.e['n']['hard']['gen_pl'][stressed] = 'ей' -- z.e['n']['hard']['gen_pl']_unstressed = '' this is just don't changed z.e['f']['hard']['gen_pl'][stressed] = 'ей' -- z.e['f']['hard']['gen_pl']_unstressed = '' this is just don't changed end

if _.equals(z.stem_type, {'vowel', 'letter-и'}) then -- Replace "ь" to "й" z.e['m']['soft']['nom_sg'] = 'й' z.e['n']['soft']['gen_pl'][unstressed] = 'й' z.e['f']['soft']['gen_pl'][unstressed] = 'й' end

if _.equals(z.stem_type, {'vowel', 'letter-и'}) then -- Replace "ей" to "ев/ёв", and "ь,ей" to "й" z.e['m']['soft']['gen_pl'] = {'ев', 'ёв'} z.e['n']['soft']['gen_pl'] = {'й', 'й'} z.e['f']['soft']['gen_pl'] = {'й', 'й'} end

if _.equals(z.stem_type, 'letter-и') then z.e['f']['soft']['dat_sg'][unstressed] = 'и' z.e['common']['soft']['prp_sg'][unstressed] = 'и' end

if _.equals(z.stem_type, 'm-3rd') then z.e['m']['soft']['gen_sg'] = 'и' z.e['m']['soft']['dat_sg'] = 'и' z.e['common']['soft']['prp_sg'] = {'и', 'и'} end

if _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then z.e['f']['soft']['nom_sg'] = 'ь' z.e['f']['soft']['dat_sg'] = {'и', 'и'} z.e['f']['soft']['acc_sg'] = 'ь' z.e['f']['soft']['ins_sg'] = {'ью', 'ью'} z.e['common']['soft']['prp_sg'] = {'и', 'и'} z.e['f']['soft']['gen_pl'] = {'ей', 'ей'} end

if _.equals(z.stem_type, 'f-3rd-sibilant') then z.e['common']['soft']['dat_pl'] = 'ам' z.e['common']['soft']['ins_pl'] = 'ами' z.e['common']['soft']['prp_pl'] = 'ах' end end

local function apply_specific_1_2(z) -- If we have specific (1) or (2) if _.contains(z.specific, '%(1%)') then z.e['m']['hard']['nom_pl'] = 'а' z.e['m']['soft']['nom_pl'] = 'я' z.e['n']['hard']['nom_pl'] = 'ы' z.e['n']['soft']['nom_pl'] = 'и' if _.equals(z.stem_type, {'velar', 'sibilant'}) then -- Replace "ы" to "и" z.e['n']['hard']['nom_pl'] = 'и' end end if _.contains(z.specific, '%(2%)') then z.e['m']['hard']['gen_pl'] = {, } z.e['m']['soft']['gen_pl'] = {'ь', 'ь'} z.e['n']['hard']['gen_pl'] = {'ов', 'ов'} z.e['n']['soft']['gen_pl'] = {'ев', 'ёв'} z.e['f']['hard']['gen_pl'] = {'ей', 'ей'} z.e['f']['soft']['gen_pl'] = {'ей', 'ей'} if _.equals(z.stem_type, {'sibilant', 'letter-ц'}) then -- Replace unstressed "о" to "е" z.e['n']['hard']['gen_pl'][unstressed] = 'ев' end --[=[ Possibly we don't need this: -- Replace "ов", "ев", "ёв" and null to "ей" if z.stem_type = {'sibilant'}} z.e['n']['hard']['gen_pl'] = {'ей', 'ей'} z.e['m']['hard']['gen_pl'][stressed] = 'ей' end -- Replace "ь" to "й" if z.stem_type = {'vowel', 'letter-и'}} z.e['m']['soft']['gen_pl'][stressed] = {'й', 'й'} end -- Replace "ей" to "ев/ёв", and "ь,ей" to "й" if z.stem_type = {'vowel', 'letter-и'}} z.e['f']['soft']['gen_pl'][unstressed] = {'ев', 'ёв'} z.e['m']['soft']['gen_pl'][stressed] = {'й', 'й'} end ]=]--	end end

local function choose(endings, case, type) endings[case] = endings[case][type] end

local function choose_stress_for_endings(z) local s	s = z.stress_flags['ending']['sg'] choose(z.e['f']['soft'], 'dat_sg', s)	choose(z.e['common']['hard'], 'prp_sg', s)	choose(z.e['common']['soft'], 'prp_sg', s)

s = z.stress_flags['ending']['ins_sg'] choose(z.e['m']['soft'], 'ins_sg', s)	choose(z.e['n']['soft'], 'ins_sg', s)	choose(z.e['f']['soft'], 'ins_sg', s)

s = z.stress_flags['ending']['pl'] choose(z.e['m']['hard'], 'gen_pl', s)	choose(z.e['m']['soft'], 'gen_pl', s)	choose(z.e['n']['hard'], 'gen_pl', s)	choose(z.e['n']['soft'], 'gen_pl', s)	choose(z.e['f']['hard'], 'gen_pl', s)	choose(z.e['f']['soft'], 'gen_pl', s) end

local function choose_endings_by_stem_type(z) local base_stem_type if _.equals(z.stem_type, {'hard', 'soft'}) then base_stem_type = z.stem_type elseif _.equals(z.stem_type, {'velar', 'sibilant', 'letter-ц'}) then base_stem_type = 'hard' elseif _.equals(z.stem_type, {'vowel', 'letter-и', 'm-3rd', 'f-3rd', 'f-3rd-sibilant'}) then base_stem_type = 'soft' else return {error_msg = 'Unexpected internal error: Unknown stem type'} end for key, value in pairs(z.e['common'][base_stem_type]) do		z.e[z.gender][base_stem_type][key] = value end z.endings = z.e[z.gender][base_stem_type] end

local function add_stress(endings, case) endings[case] = _.replace(endings[case], '^({vowel})', '%1́ ') end

local function apply_stress_type(z) if z.stress_flags['stem']['sg'] == stressed then z.stems['sg'] = z.stem_stressed else z.stems['sg'] = z.stem add_stress(z.endings, 'nom_sg') add_stress(z.endings, 'gen_sg') add_stress(z.endings, 'dat_sg') add_stress(z.endings, 'prp_sg') end

if z.stress_flags['stem']['ins_sg'] == stressed then z.stems['ins_sg'] = z.stem_stressed else z.stems['ins_sg'] = z.stem add_stress(z.endings, 'ins_sg') end

if z.gender == 'f' then if z.stress_flags['stem']['acc_sg'] == stressed then z.stems['acc_sg'] = z.stem_stressed else z.stems['acc_sg'] = z.stem add_stress(z.endings, 'acc_sg') end end

if z.stress_flags['stem']['nom_pl'] == stressed then z.stems['nom_pl'] = z.stem_stressed else z.stems['nom_pl'] = z.stem add_stress(z.endings, 'nom_pl') end

if z.stress_flags['stem']['pl'] == stressed then z.stems['pl'] = z.stem_stressed else z.stems['pl'] = z.stem add_stress(z.endings, 'gen_pl') add_stress(z.endings, 'dat_pl') add_stress(z.endings, 'ins_pl') add_stress(z.endings, 'prp_pl') end end

local function apply_specific_degree(z) -- If degree sign ° if _.endswith(z.word, '[ая]нин') and z.animacy == 'an' and z.word ~= 'семьянин' then z.stems['pl'] = _.replace(z.stems['pl'], '([ая])ни́ н$', '%1́ н') z.stems['pl'] = _.replace(z.stems['pl'], '([ая]́ ?н)ин$', '%1') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], '([ая])ни́ н$', '%1́ н') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], '([ая]́ ?н)ин$', '%1') z.endings['nom_pl'] = 'е' z.endings['gen_pl'] = '' z.specific = z.specific .. '°'	end if _.endswith(z.word, {'ёнок', 'онок', 'ёночек', 'оночек'}) then if _.endswith(z.word, 'ёнок') then z.stems['pl'] = _.replace(z.stems['pl'], 'ёнок$', 'я́т') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], 'ёнок$', 'я́т') end if _.endswith(z.word, 'онок') then z.stems['pl'] = _.replace(z.stems['pl'], 'о́нок$', 'а́т') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], 'о́нок$', 'а́т') end if _.endswith(z.word, 'ёночек') then z.stems['pl'] = _.replace(z.stems['pl'], 'ёночек$', 'я́тк') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], 'ёночек$', 'я́тк') end if _.endswith(z.word, 'оночек') then z.stems['pl'] = _.replace(z.stems['pl'], 'о́ночек$', 'а́тк') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], 'о́ночек$', 'а́тк') end z.endings['nom_pl'] = z.e['f']['hard']['nom_pl'] z.endings['gen_pl'] = z.e['f']['hard']['gen_pl'] z.specific = z.specific .. '*'		z.specific = z.specific .. '°'	end end

local function apply_specific_reducable(z) if _.contains(z.specific, '%*') then if z.gender == 'm' then reduced = 'A'		elseif z.gender == 'n' then reduced = 'B'		elseif z.gender == 'f' then if _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then reduced = 'A'			else reduced = 'B'			end end if reduced == 'A' then reduced_letter = _.extract(z.word, '({vowel+ё}){consonant}+$') if reduced_letter == 'о' then z.stems['sg'] = _.replace(z.stems['sg'], 'о́ ?([^о]+)$', '%1') if not _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then z.stems['ins_sg'] = _.replace(z.stems['ins_sg'], 'о́ ?([^о]+)$', '%1') end z.stems['pl'] = _.replace(z.stems['pl'], 'о́ ?([^о]+)$', '%1') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], 'о́ ?([^о]+)$', '%1') elseif _.equals(reduced_letter, {'е', 'ё'}) then prev = _.extract(z.word, '(.)[её][^её]+$') if _.contains(prev, '{vowel+ё}') then                                    -- 1).					z.stems['sg'] = _.replace(z.stems['sg'], '[её]́ ?([^её]+)$', 'й%1')					if not _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then  						z.stems['ins_sg'] = _.replace(z.stems['ins_sg'], '[её]́ ?([^её]+)$', 'й%1')					end					z.stems['pl'] = _.replace(z.stems['pl'], '[её]́ ?([^её]+)$', 'й%1')					z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], '[её]́ ?([^её]+)$', 'й%1')				elseif z.stem_type == 'vowel'                                                  -- 2) а).						or z.stem_type == 'velar' and _.contains(prev, '[^аеёиоуыэюяшжчщц]')   -- 2) б).						or not _.equals(z.stem_type, {'vowel', 'velar'}) and prev == 'л' then  -- 2) в).					z.stems['sg'] = _.replace(z.stems['sg'], '[её]́ ?([^её]*)$', 'ь%1')					if not _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then  						z.stems['ins_sg'] = _.replace(z.stems['ins_sg'], '[её]́ ?([^её]*)$', 'ь%1') end z.stems['pl'] = _.replace(z.stems['pl'], '[её]́ ?([^её]*)$', 'ь%1') z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], '[её]́ ?([^её]*)$', 'ь%1') else                                                                        -- 3).					z.stems['sg'] = _.replace(z.stems['sg'], '[её]́ ?([^её]*)$', '%1')					if not _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then  						z.stems['ins_sg'] = _.replace(z.stems['ins_sg'], '[её]́ ?([^её]*)$', '%1')					end					z.stems['pl'] = _.replace(z.stems['pl'], '[её]́ ?([^её]*)$', '%1')					z.stems['nom_pl'] = _.replace(z.stems['nom_pl'], '[её]́ ?([^её]*)$', '%1')				end			end		end  -- reduced A		-- TODO: pcerhaps this line is redundant?		z.stems['gen_pl'] = z.stems['pl']  -- apply changes in stems['pl'] for stems['gen_pl']		if reduced == 'B' and 			not (z.stem_type == 'soft' and _.equals(z.stress_type, {'b', 'f'}) -- we should ignore asterix for 2*b and 2*f (so to process it just like 2b or 2f) or _.contains(z.specific, '(2)') and _.equals(z.stem_type, {'velar', 'letter-ц', 'vowel'})) -- and also the same for (2)-specific and 3,5,6 stem-types		then 			if z.stem_type == 'vowel' then  -- 1). if _.equals(z.stress_type, {'b', 'c', 'e', 'f', "f'", "b'" }) then -- gen_pl ending stressed  -- TODO: special vars for that z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], 'ь$', 'е́') else z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], 'ь$', 'и') end elseif _.contains(z.stem, '[ьй]{consonant}$') then -- 2).				if z.stem_type == 'letter-ц' or _.equals(z.stress_type, {'a', 'd', "d'"}) then  -- gen_pl ending unstressed  -- TODO: special vars for that					z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '[ьй]({consonant})$', 'е%1')				else					z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '[ьй]({consonant})$', 'ё%1')				end			else  -- 3). prev = _.extract(z.stem, '(.){consonant}$') if z.stem_type == 'velar' and _.contains(prev, '[^жшчщц]') -- 3). а). or _.contains(prev, '[кгх]') then               -- 3). б). z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '(.)({consonant})$', '%1о%2') else -- 3). в). if z.stem_type == 'letter-ц' then z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '(.)({consonant})$', '%1е%2') else if _.equals(z.stress_type, {'b', 'c', 'e', 'f', "f'", "b'" }) then -- gen_pl ending stressed  -- TODO: special vars for that if _.contains(prev, '[жшчщ]') then z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '(.)({consonant})$', '%1о́%2') else z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '(.)({consonant})$', '%1ё%2') end else z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], '(.)({consonant})$', '%1е%2') end end end end if z.stem_type == 'soft' and _.endswith(z.word, 'ня') and z.stress_type == 'a' then z.endings['gen_pl'] = '' end end -- reduced B	end  -- specific * end

local function choose_accusative_forms(z, forms) forms['acc_sg_in'] = '' forms['acc_sg_an'] = '' forms['acc_pl_in'] = '' forms['acc_pl_an'] = '' if z.gender == 'n' then forms['acc_sg'] = forms['nom_sg'] elseif z.gender == 'm' then if z.animacy == 'in' then forms['acc_sg'] = forms['nom_sg'] elseif z.animacy == 'an' then forms['acc_sg'] = forms['gen_sg'] else forms['acc_sg_in'] = forms['nom_sg'] forms['acc_sg_an'] = forms['gen_sg'] end elseif z.gender == 'f' then if _.equals(z.stem_type, {'f-3rd', 'f-3rd-sibilant'}) then forms['acc_sg'] = forms['nom_sg'] else forms['acc_sg'] = z.stems['acc_sg'] .. z.endings['acc_sg'] end end

if z.animacy == 'in' then forms['acc_pl'] = forms['nom_pl'] elseif z.animacy == 'an' then forms['acc_pl'] = forms['gen_pl'] else forms['acc_pl_in'] = forms['nom_pl'] forms['acc_pl_an'] = forms['gen_pl'] end end

function get_zaliznyak_index(z) local stem_types = { ['hard'] = '1', ['soft'] = '2', ['velar'] = '3', ['sibilant'] = '4', ['letter-ц'] = '5', ['vowel'] = '6', ['letter-и'] = '7', ['m-3rd'] = '8', ['f-3rd'] = '8', ['f-3rd-sibilant'] = '8', ['n-3rd'] = '8', }	local index = z.gender_animacy .. ' ' .. stem_types[z.stem_type] if _.contains(z.specific, '°') then index = index .. '°'	elseif _.contains(z.specific, '%*') then index = index .. '*'	end index = index .. _.replace(z.stress_type, "'", "&#39;") if _.contains(z.specific, '%(1%)') then index = index .. '①'	end if _.contains(z.specific, '%(2%)') then index = index .. '②'	end if _.contains(z.specific, '%(3%)') then index = index .. '③'	end if _.contains(z.specific, 'ё') then index = index .. ', ё' end return index end

function export.forms(base, args) _.clear_stash _.add_stash('{vowel}', '[аеиоуыэюяАЕИОУЫЭЮЯ]') _.add_stash('{vowel+ё}', '[аеёиоуыэюяАЕЁИОУЫЭЮЯ]') _.add_stash('{consonant}', '[^аеёиоуыэюяАЕЁИОУЫЭЮЯ]')

local z = {} z.e = get_standard_endings

z.word_stressed = args['word_stressed'] if _.contains_several(z.word_stressed, '{vowel+ё}') and not _.contains(z.word_stressed, '[́ ё]') then return {error_msg = 'Error in template : You should add stress mark for the argument "word_stressed"'} end z.word = _.replace(z.word_stressed, '́ ', '')

-- Parse "gender_animacy" argument and get values for "gender" and "animacy"' z.gender_animacy = args['gender_animacy'] z.gender = _.extract(z.gender_animacy, '([mnf])%-[a-z]+') z.animacy = _.extract(z.gender_animacy, '[mnf]%-([a-z]+)') z.stress_type = args['stress_type'] if not _.equals(z.stress_type, {'a', 'b', "b'", 'c', 'd', "d'", 'e', 'f', "f'", "f''"}) then return {error_msg = 'Error in template : Wrong value for the argument "stress_type"'} end z.stress_flags = get_stress_flags(z.stress_type)

z.specific = args['specific']

-- Remove ending (-а, -е, -ё, -о, -я, -й, -ь) to get stem z.stem = _.replace(z.word, '[аеёояйьиы]$', '') z.stem_stressed = _.replace(z.word_stressed, '[аеёиоыяйь]́ ?$', '')

-- Add stress to stem_stressed if stress is absent (i.e. there is only one syllable or stress was on the ending) if not _.contains(z.stem_stressed, '[́ ё]') then if _.equals(z.stress_type, {"f", "f'"}) then z.stem_stressed = _.replace(z.stem_stressed, '^({consonant}*)({vowel})', '%1%2́ ') else -- if _.equals(stress_type, {'a', "b'", 'b', 'c', 'd', "d'", 'e'}) then z.stem_stressed = _.replace(z.stem_stressed, '({vowel})({consonant}*)$', '%1́ %2') end -- TODO: process cases with * (stress on penultimate syllable) end

-- Determination of stem type get_stem_type(z)

-- Special changes in endings for velar, sibilant, vowel etc. stem types change_endings_for_other_stem_types(z)

-- apply special cases (1) or (2) in specific apply_specific_1_2(z)

-- Resolve stressed/unstressed cases of endings choose_stress_for_endings(z)

if z.gender == 'n' and _.endswith(z.word, 'ё') then z.e['n']['soft']['nom_sg'] = 'ё' end

-- Choose endings by stem type and gender (either 'hard' or 'soft' key from changed "z.e") choose_endings_by_stem_type(z)

-- If we have "ё" specific if _.contains(z.specific, 'ё') then z.stem_stressed = _.replace(z.stem_stressed, 'е́?([^е]*)$', 'ё%1') end

z.stems = {} apply_stress_type(z)

apply_specific_degree(z)

-- Changes for reduceable cases z.stems['nom_sg'] = z.stems['sg'] -- *save* unchanged stem value for nominative singular z.stems['gen_pl'] = z.stems['pl'] -- set default stem value for genitive plural apply_specific_reducable(z)

-- If we have "ё" specific if _.contains(z.specific, 'ё') and not _.contains(z.endings['gen_pl'], '{vowel+ё}') and not _.contains(z.stems['gen_pl'], 'ё') then z.stems['gen_pl'] = _.replace(z.stems['gen_pl'], 'е́?([^е]*)$', 'ё%1') z.specific = z.specific .. 'ё' end

-- Generate forms local forms = { nom_sg = z.stems['nom_sg'] .. z.endings['nom_sg'], gen_sg = z.stems['sg'] .. z.endings['gen_sg'], dat_sg = z.stems['sg'] .. z.endings['dat_sg'], acc_sg = '', ins_sg = z.stems['ins_sg'] .. z.endings['ins_sg'], prp_sg = z.stems['sg'] .. z.endings['prp_sg'], nom_pl = z.stems['nom_pl'] .. z.endings['nom_pl'], gen_pl = z.stems['gen_pl'] .. z.endings['gen_pl'], dat_pl = z.stems['pl'] .. z.endings['dat_pl'], acc_pl = '', ins_pl = z.stems['pl'] .. z.endings['ins_pl'], prp_pl = z.stems['pl'] .. z.endings['prp_pl'], }

-- Add stress if there is no one if _.contains_several(forms['nom_sg'], '{vowel}') and not _.contains(forms['nom_sg'], '[́ ё]') then -- perhaps this is redundant for nom_sg? forms['nom_sg'] = _.replace(forms['nom_sg'], '({vowel})({consonant}*)$', '%1́ %2') end if _.contains_several(forms['gen_pl'], '{vowel+ё}') and not _.contains(forms['gen_pl'], '[́ ё]') then forms['gen_pl'] = _.replace(forms['gen_pl'], '({vowel})({consonant}*)$', '%1́ %2') end

for key, value in pairs(forms) do		-- Remove stress if there is only one syllable if _.contains_once(value, '{vowel+ё}') then forms[key] = _.replace(value, '́ ', '') end -- Replace 'ё' with 'е' when unstressed if _.contains_once(value, 'ё') and _.contains(value, '́ ') then forms[key] = _.replace(value, 'ё', 'е') end end

choose_accusative_forms(z, forms)

-- partitive case if args['par'] and args['par'] ~= '' then forms['par'] = forms['dat_sg'] end

-- locative case if args['loc'] and args['loc'] ~= '' then loc = forms['dat_sg'] loc = _.replace(loc, '́ ', '') loc = _.replace(loc, 'ё', 'е') loc = _.replace(loc, '({vowel})({consonant}*)$', '%1́ %2') forms['loc'] = loc if args['loc'] == 'в' or args['loc'] == 'на' then forms['loc_preposition'] = '(' .. args['loc'] .. ')' else forms['loc_preposition'] = '(в, на) ' end end

forms['stem_type'] = z.stem_type -- for testcases forms['stress_type'] = z.stress_type -- for categories forms['index'] = get_zaliznyak_index(z) forms['dev'] = dev_prefix

local keys = { 'nom_sg', 'gen_sg', 'dat_sg', 'acc_sg', 'ins_sg', 'prp_sg', 'nom_pl', 'gen_pl', 'dat_pl', 'acc_pl', 'ins_pl', 'prp_pl', 'voc', 'n', 'note', }	for i, key in pairs(keys) do		if args[key] and args[key] ~= '' then forms[key] = args[key] end --[=[ -- I think we don't need this here, let's do it in template if form_key == 'nom_sg' then if forms[form_key] and forms[form_key] ~= '' then value = forms[form_key] transliterated = lang:transliterate(m_links.remove_links(value)) link = m_links.full_link(value, nil, lang, nil, nil, nil, {tr = "-"}, false) forms[form_key] = strutils.format("{link} {transliterated} ", {link=link, transliterated=transliterated}) else forms[form_key] = '&mdash;' end end ]=]--	end return forms end

return export