def apply_specific_3(func, i): # export r = i.result # local # Специфика по (3) if _.contains(i.rest_index, '%(3%)') or _.contains(i.rest_index, '③'): if _.endswith(r['prp-sg'], 'и'): r['prp-sg'] = r['prp-sg'] + ' //<br />' + _.replaced(r['prp-sg'], 'и$', 'е') # end if i.gender == 'f' and _.endswith(r['dat-sg'], 'и'): r['dat-sg'] = r['dat-sg'] + ' //<br />' + _.replaced(r['dat-sg'], 'и$', 'е') # end # end _.ends(module, func)
def apply_adj_specific_1_2(func, i): # export p = i.parts # local if i.calc_sg: if not _.endswith(p.stems['srt-sg'], 'нн'): # todo: log some error? return _.ends(module, func) # end if _.contains(i.rest_index, ['%(1%)', '①']): if i.gender == 'm': _.replace(p.stems, 'srt-sg', 'нн$', 'н') # end # end # end if _.contains(i.rest_index, ['%(2%)', '②']): if i.calc_sg: _.replace(p.stems, 'srt-sg', 'нн$', 'н') # end if i.calc_pl: _.replace(p.stems, 'srt-pl', 'нн$', 'н') # end # end _.ends(module, func)
def add_comparative(func, i): # export # todo: move to `modify` (и сделать через основы и окончания) r = i.result # local if _.contains(i.rest_index, '~'): r['comparative'] = '-' return _.ends(module, func) # end if i.stem.type == '3-velar': new_stem = i.stem.unstressed if _.endswith(new_stem, 'к'): new_stem = _.replaced(new_stem, 'к$', 'ч') elif _.endswith(new_stem, 'г'): new_stem = _.replaced(new_stem, 'г$', 'ж') elif _.endswith(new_stem, 'х'): new_stem = _.replaced(new_stem, 'х$', 'ш') else: pass # todo: some error here # end # ударение на предпоследний слог: new_stem = _.replaced(new_stem, '({vowel})({consonant}*)$', '%1́ %2') r['comparative'] = new_stem + 'е' else: if _.contains(i.rest_index, ['%(2%)', '②']): # todo: special variable for this r['comparative'] = i.parts.stems['nom-pl'] + 'ее' r['comparative2'] = i.parts.stems['nom-pl'] + 'ей' else: if _.equals(i.stress_type, ['a', 'a/a']): r['comparative'] = i.stem.stressed + 'ее' r['comparative2'] = i.stem.stressed + 'ей' else: r['comparative'] = i.stem.unstressed + 'е́е' r['comparative2'] = i.stem.unstressed + 'е́й' # end # end # end _.ends(module, func)
def voc_case(func, i): # Звательный падеж r = i.result # local if _.has_value(i.args, 'З'): r['voc-sg'] = i.args['З'] elif _.contains(i.index, 'З'): if _.endswith(i.word.unstressed, ['а', 'я']): r['voc-sg'] = r['gen-pl'] else: r['error'] = 'Ошибка: Для автоматического звательного падежа, слово должно оканчиваться на -а/-я' # end # end _.ends(module, func)
def init_stem(func, i): # export # todo rename to `init_stem` # INFO: Исходное слово без ударения: i.word.unstressed = _.replaced(i.word.stressed, '́ ', '') # todo: move outside this function # INFO: Исходное слово вообще без ударений (в т.ч. без грависа): i.word.cleared = _.replaced( _.replaced(_.replaced(i.word.unstressed, '̀', ''), 'ѐ', 'е'), 'ѝ', 'и') if i.adj: if _.endswith(i.word.stressed, 'ся'): i.postfix = True i.stem.unstressed = _.replaced(i.word.unstressed, '{vowel}[йяе]ся$', '') i.stem.stressed = _.replaced(i.word.stressed, '{vowel}́ ?[йяе]ся$', '') else: i.stem.unstressed = _.replaced(i.word.unstressed, '{vowel}[йяе]$', '') i.stem.stressed = _.replaced(i.word.stressed, '{vowel}́ ?[йяе]$', '') # end else: # INFO: Удаляем окончания (-а, -е, -ё, -о, -я, -й, -ь), чтобы получить основу: i.stem.unstressed = _.replaced(i.word.unstressed, '[аеёийоьыя]$', '') i.stem.stressed = _.replaced(i.word.stressed, '[аеёийоьыя]́ ?$', '') # end _.log_value(i.word.unstressed, 'i.word.unstressed') _.log_value(i.stem.unstressed, 'i.stem.unstressed') _.log_value(i.stem.stressed, 'i.stem.stressed') # INFO: Случай, когда не указано ударение у слова: several_vowels = _.contains_several(i.word.stressed, '{vowel+ё}') # local has_stress = _.contains(i.word.stressed, '[́ ё]') # local if several_vowels and not has_stress: _.log_info('Ошибка: Не указано ударение в слове') e.add_error(i, 'Ошибка: Не указано ударение в слове') i.result.error_category = 'Ошибка в шаблоне "сущ-ru" (не указано ударение в слове)' # end _.ends(module, func)
def additional_arguments(func, i): r = i.result # local # RU (склонение) if _.contains(i.rest_index, '0'): r['скл'] = 'не' elif i.adj: r['скл'] = 'а' elif i.pronoun: r['скл'] = 'мс' elif _.endswith(i.word.unstressed, '[ая]'): r['скл'] = '1' else: if i.gender == 'm' or i.gender == 'n': r['скл'] = '2' else: r['скл'] = '3' # end # end # RU (чередование) if _.contains(i.index, '%*'): r['чередование'] = '1' # end if i.pt: r['pt'] = '1' # end # RU ("-" в индексе) # TODO: Здесь может быть глюк, если случай глобального `//` и `rest_index` пуст (а исходный `index` не подходит, т.к. там может быть не тот дефис -- в роде) if i.rest_index: if _.contains(i.rest_index, ['%-', '—', '−']): r['st'] = '1' r['затрудн'] = '1' # end else: pass # TODO # end _.ends(module, func)
def apply_specific_degree(func, i): # export # If degree sign ° p = i.parts # local word = i.word.unstressed # local if _.contains(i.rest_index, '°') and _.endswith(word, '[ая]нин'): _.replace(p.stems, 'all-pl', '([ая])ни́ н$', '%1́ н') _.replace(p.stems, 'all-pl', '([ая]́ ?н)ин$', '%1') p.endings['nom-pl'] = 'е' p.endings['gen-pl'] = '' return _.returns(module, func, i.rest_index) # end if _.contains(i.rest_index, '°') and _.endswith(word, 'ин'): _.replace(p.stems, 'all-pl', 'и́ ?н$', '') if not _.contains(i.rest_index, ['%(1%)', '①']): p.endings['nom-pl'] = 'е' # end p.endings['gen-pl'] = '' # end if _.contains(i.rest_index, '°') and _.endswith(word, ['ёнок', 'онок']): _.replace(p.stems, 'all-pl', 'ёнок$', 'я́т') _.replace(p.stems, 'all-pl', 'о́нок$', 'а́т') # INFO: Эмуляция среднего рода `1a` для форм мн. числа p.endings['nom-pl'] = 'а' p.endings['gen-pl'] = '' reducable.apply_specific_reducable(i, i.gender, i.rest_index + '*', True) return _.returns(module, func, i.rest_index) # end if _.contains(i.rest_index, '°') and _.endswith(word, ['ёночек', 'оночек']): _.replace(p.stems, 'all-pl', 'ёночек$', 'я́тк') _.replace(p.stems, 'all-pl', 'о́ночек$', 'а́тк') # INFO: Черездование для единичной формы (возможно применится также и для множественной, но это не страшно, потом заменится по идее) reducable.apply_specific_reducable(i, i.gender, i.rest_index + '*', False) # INFO: По сути должно примениться только к мн. формам (случай `B`) reducable.apply_specific_reducable(i, 'f', i.rest_index + '*', False) p.endings['gen-pl'] = '' # INFO: Странный фикс, но он нужен.. <_< return _.returns(module, func, i.rest_index) # end if _.contains(i.rest_index, '°') and i.gender == 'n' and _.endswith( word, 'мя'): _.replace(p.stems, 'all-sg', 'м$', 'мен') _.replace(p.stems, 'ins-sg', 'м$', 'мен') _.replace(p.stems, 'all-pl', 'м$', 'мен') p.endings['nom-sg'] = 'я' p.endings['gen-sg'] = 'и' p.endings['dat-sg'] = 'и' p.endings['ins-sg'] = 'ем' p.endings['prp-sg'] = 'и' # end return _.returns(module, func, i.rest_index)
def generate_adj_stress_schemas(func): # INFO: Вычисление схемы ударения stress_types = [ # todo: special variables for that? 'a', "a'", 'b', "b'", 'c', 'a/a', 'a/b', 'a/c', "a/a'", "a/b'", "a/c'", "a/c''", 'b/a', 'b/b', 'b/c', "b/a'", "b/b'", "b/c'", "b/c''", ] res = dict() # dict for j, stress_type in enumerate(stress_types): stress_schema = dict() # dict stress_schema['stem'] = dict() # dict stress_schema['ending'] = dict() # dict # local cases cases = [ 'nom-sg', 'gen-sg', 'dat-sg', 'acc-sg', 'ins-sg', 'prp-sg', 'nom-pl', 'gen-pl', 'dat-pl', 'acc-pl', 'ins-pl', 'prp-pl', ] # list # пустышки в падежи, чтобы они шли раньше кратких форм в результате types = ['stem', 'ending'] # local for j, type in enumerate(types): for j, case in enumerate(cases): stress_schema[type][case] = '...' # end # end # общий подход следующий: # если схема среди перечисленных, значит, элемент под ударением (stressed), иначе — нет (unstressed) stress_schema['stem']['full'] = _.startswith(stress_type, ["a", "a/"]) stress_schema['stem']['srt-sg-m'] = True stress_schema['stem']['srt-sg-f'] = _.endswith( stress_type, ["/a", "/a'"]) or _.equals(stress_type, ['a', "a'"]) stress_schema['stem']['srt-sg-n'] = _.endswith( stress_type, ["/a", "/c", "/a'", "/c'", "/c''"]) or _.equals( stress_type, ['a', "a'"]) stress_schema['stem']['srt-pl'] = _.endswith( stress_type, ["/a", "/c", "/a'", "/b'", "/c'", "/c''" ]) or _.equals(stress_type, ['a', "a'", "b'"]) stress_schema['ending']['full'] = _.startswith(stress_type, ["b", "b/"]) stress_schema['ending']['srt-sg-m'] = False stress_schema['ending']['srt-sg-f'] = _.endswith( stress_type, ["/b", "/c", "/a'", "/b'", "/c'", "/c''" ]) or _.equals(stress_type, ['b', "a'", "b'"]) stress_schema['ending']['srt-sg-n'] = _.endswith( stress_type, ["/b", "/b'", "/c''"]) or _.equals( stress_type, ['b', "b'"]) stress_schema['ending']['srt-pl'] = _.endswith( stress_type, ["/b", "/b'", "/c'", "/c''"]) or _.equals( stress_type, ['b', "b'"]) types = ['stem', 'ending'] # local for j, type in enumerate(types): value = stress_schema[type]['full'] # local for j, case in enumerate(cases): stress_schema[type][case] = value # end del stress_schema[type]['full'] # end res[stress_type] = stress_schema # end dump_data('adj', res) return _.returns(module, func, res)
def get_stem_type(func, i): # export # INFO: Определение типа основы word = i.word.unstressed # local stem = i.stem.unstressed # local i.stem.type = '' if _.endswith(stem, '[гкх]'): i.stem.type = '3-velar' elif _.endswith(stem, '[жчшщ]'): i.stem.type = '4-sibilant' elif _.endswith(stem, 'ц'): i.stem.type = '5-letter-ц' elif _.endswith(stem, ['[йь]', '[аоеёуыэюя]']): i.stem.type = '6-vowel' elif _.endswith(stem, 'и'): i.stem.type = '7-letter-и' else: if i.adj: if _.endswith(word, ['ый', 'ой', 'ая', 'ое', 'ые']): i.stem.type = '1-hard' elif _.endswith(word, ['ий', 'яя', 'ее', 'ие']): i.stem.type = '2-soft' # end elif i.gender == 'm': if stem == word or _.endswith(word, 'ы'): i.stem.type = '1-hard' elif _.endswith(word, 'путь'): i.stem.type = '8-third' elif _.endswith(word, 'ь') or _.endswith(word, 'и'): i.stem.type = '2-soft' elif _.endswith(word, 'а'): i.stem.type = '1-hard' # i.gender = 'f' ?? elif _.endswith(word, 'я'): i.stem.type = '2-soft' # i.gender = 'f' ?? # end elif i.gender == 'f': if _.endswith(word, 'а') or _.endswith(word, 'ы'): i.stem.type = '1-hard' elif _.endswith(word, 'я'): i.stem.type = '2-soft' elif _.endswith(word, 'и') and _.contains( i.rest_index, '2'): # todo: а что если нет индекса?? i.stem.type = '2-soft' elif _.endswith(word, 'и') and _.contains(i.rest_index, '8'): i.stem.type = '8-third' elif _.endswith(word, 'ь'): # conflict in pl i.stem.type = '8-third' # end elif i.gender == 'n': if _.endswith(word, 'о') or _.endswith(word, 'а'): i.stem.type = '1-hard' elif _.endswith(word, 'мя') or _.endswith(word, 'мена'): i.stem.type = '8-third' elif _.endswith(word, 'е') or _.endswith(word, 'я'): i.stem.type = '2-soft' # end # end # end # if gender == 'm': # if _.endswith(word, ['а', 'я']): # i.gender = 'f' # # end # # end if i.gender == 'f' and i.stem.type == '4-sibilant' and _.endswith( word, 'ь'): i.stem.type = '8-third' # end if i.stem.type == '': i.stem.type = '1-hard' # e.add_error(i, 'Неизвестный тип основы') -- fixme ? # return _.ends(module, func) # end # INFO: Выбор подходящего `stem_type` из двух базовых типов: '1-hard' и '2-soft' i.stem.base_type = get_stem_base_type(i) _.ends(module, func)
def apply_specific_reducable(func, i, gender, rest_index, only_sg): # export # local reduced, reduced_letter, f8_third, prev # local case_2_a, case_2_b, case_2_c, case_3_a, case_3_b # local skip_b_1, skip_b_2, skip_b_3, force_b # local case p = i.parts # local if _.contains(rest_index, '%*'): reduced = '?' if i.adj: if gender == 'm': if _.contains(rest_index, ['%(1%)', '①']): if gender == 'm' and i.adj and _.endswith( i.word.unstressed, 'ний' ) and p.endings[ 'srt-sg'] == 'ь': # fixme: temporary duplicated with the same code at the ending of function... p.endings['srt-sg'] = '' # вместо `ь` для `2*a` # end return _.ends(module, func) # end if _.contains(rest_index, ['%(2%)', '②']): return _.ends(module, func) # end reduced = 'B' else: return _.ends(module, func) # end elif gender == 'm' or i.pronoun: reduced = 'A' elif gender == 'n': reduced = 'B' elif gender == 'f': if i.stem.type == '8-third': reduced = 'A' else: reduced = 'B' # end # end _.log_info('Случай чередования: ' + str(reduced)) if reduced == 'A': reduced_letter = _.extract(i.word.unstressed, '({vowel+ё}){consonant}+$') f8_third = gender == 'f' and i.stem.type == '8-third' _.log_value(reduced_letter, 'reduced_letter') if reduced_letter == 'о': _.replace(p.stems, 'all-sg', '(.)о́ ?([^о]+)$', '%1%2') # # local stem_gen_pl # # У этих имён последняя гласная основы исходной формы заменяется на нуль, о или й во всех формах, не совпадающих с исходной (кроме Т. ед. на -ью). # # if p.endings['gen-pl'] == '': -- ботинок, глазок # if _.contains(rest_index, ['%(2%)', '②']): # stem_gen_pl = p.stems['gen-pl'] # # end if not only_sg: _.replace(p.stems, 'all-pl', '(.)о́ ?([^о]+)$', '%1%2') # end # if stem_gen_pl: # ботинок, глазок # p.stems['gen-pl'] = stem_gen_pl # # end if not f8_third: _.replace(p.stems, 'ins-sg', '(.)о́ ?([^о]+)$', '%1%2') # end elif reduced_letter == 'и': # бывает только в подтипе мс 6* _.replace(p.stems, 'all-sg', '(.)и́ ?([^и]+)$', '%1ь%2') if not only_sg: _.replace(p.stems, 'all-pl', '(.)и́ ?([^и]+)$', '%1ь%2') # end elif _.In(reduced_letter, ['е', 'ё']): prev = _.extract(i.word.unstressed, '(.)[её][^её]+$') case_2_a = i.stem.type == '6-vowel' # 2) а). case_2_b = i.stem.type == '3-velar' and _.contains( prev, '[^аеёиоуыэюяшжчщц]') # 2) б). case_2_c = not _.equals( i.stem.type, ['6-vowel', '3-velar']) and prev == 'л' # 2) в). if _.contains(prev, '{vowel+ё}'): # 1). _.log_info('Подслучай A.1).') _.replace(p.stems, 'all-sg', '[её]́ ?([^её]+)$', 'й%1') if not f8_third: _.replace(p.stems, 'ins-sg', '[её]́ ?([^её]+)$', 'й%1') # end if not only_sg: _.replace(p.stems, 'all-pl', '[её]́ ?([^её]+)$', 'й%1') # end elif case_2_a or case_2_b or case_2_c: # 2). _.log_info('Подслучай A.2).') _.replace(p.stems, 'all-sg', '[её]́ ?([^её]*)$', 'ь%1') if not f8_third: _.replace(p.stems, 'ins-sg', '[её]́ ?([^её]*)$', 'ь%1') # end if not only_sg: _.replace(p.stems, 'all-pl', '[её]́ ?([^её]*)$', 'ь%1') # end else: # 3). _.log_info('Подслучай A.3).') _.replace(p.stems, 'all-sg', '[её]́ ?([^её]*)$', '%1') if not f8_third: _.replace(p.stems, 'ins-sg', '[её]́ ?([^её]*)$', '%1') # end if not only_sg: _.replace(p.stems, 'all-pl', '[её]́ ?([^её]*)$', '%1') # end # end # end # end # reduced A if only_sg: return _.ends( module, func ) # ниже всё равно обрабатывается только множественное число уже # end # we should ignore asterix for 2*b and 2*f (so to process it just like 2b or 2f) skip_b_1 = i.stem.type == '2-soft' and _.In(i.stress_type, ['b', 'f']) # and also the same for (2)-specific and 3,5,6 stem-types skip_b_2 = _.contains(rest_index, ['%(2%)', '②']) and ( _.In(i.stem.type, {'2-soft'}) # '2-soft' из сходня 2*a(2) # TODO: Разобраться, почему это нужно было на самом деле? # _.In(i.stem.type, ['3-velar', '5-letter-ц', '6-vowel']) # так было раньше, без прочих skip ) # TODO: Разобраться, почему это нужно на самом деле? skip_b_3 = _.contains(rest_index, ['%(2%)', '②']) and gender == 'n' # temp fix stem = i.stem.unstressed # local force_b = False if _.contains(rest_index, ['%(2%)', '②']): gender = 'n' # fixme: ???? i.forced_stem = p.stems['gen-pl'] stem = p.stems['gen-pl'] _.log_info('New force stem (gen-pl): ' + str(stem)) force_b = True # end # TODO: б) в словах прочих схем ударения — на последний слог основы, если основа не содержит беглой гласной, и на предпоследний слог основы, если основа содержит беглую гласную, на¬пример: величина, тюрьма, полотно (схема d) — И.мн. величины, тюрьмы, полотна, Р.мн. ве¬личин, тюрем, полотен. # это для глАзок if (reduced == 'B' or force_b) and not skip_b_1 and not skip_b_2 and not skip_b_3: if i.adj: case = 'srt-sg' else: case = 'gen-pl' # end _.log_info('Зашли в случай чередования B') if i.stem.type == '6-vowel': # 1). _.log_info('Подслучай B.1).') if _.In(i.stress_type, { 'b', 'c', 'e', 'f', "f'", "b'" }): # gen-pl ending stressed -- TODO: special vars for that _.replace(p.stems, case, 'ь$', 'е́') else: _.replace(p.stems, case, 'ь$', 'и') # end elif _.contains(stem, '[ьй]{consonant}$'): # 2). _.log_info('Подслучай B.2).') if i.adj: e = i.stem.type == '5-letter-ц' or not _.contains( i.stress_type, 'b') or _.endswith( i.stress_type, ['/b', "/b'"]) # todo: fix only "b" for srt... else: e = i.stem.type == '5-letter-ц' or _.equals( i.stress_type, ['a', 'd', "d'"] ) # gen_pl ending unstressed -- TODO: special vars for that # end if e: _.replace(p.stems, case, '[ьй]({consonant})$', 'е%1') else: _.replace(p.stems, case, '[ьй]({consonant})$', 'ё%1') # end else: # 3). prev = _.extract(stem, '(.){consonant}$') case_3_a = i.stem.type == '3-velar' and _.contains( prev, '[^жшчщц]') # 3). а). case_3_b = _.contains(prev, '[кгх]') # 3). б). if case_3_a or case_3_b: _.log_info('Подслучай B.3). а,б).') _.replace(p.stems, case, '(.)({consonant})$', '%1о%2') else: # 3). в). _.log_info('Подслучай B.3). в).') if i.stem.type == '5-letter-ц': _.log_info('i.stem.type == "letter-ц"') _.replace(p.stems, case, '(.)({consonant})$', '%1е%2') else: if i.adj: e = _.equals(i.stress_type, 'b') or _.endswith( i.stress_type, ['/b', "/b'"]) # TODO: special vars for that else: e = _.In( i.stress_type, {'b', 'c', 'e', 'f', "f'", "b'"} ) # gen_pl ending stressed -- TODO: special vars for that # end if e: _.log_info('в `' + case + '` ударение на окончание') p.stems[case] = stem if _.contains(prev, '[жшчщ]'): _.log_info('предыдущая [жшчщ]') _.replace(p.stems, case, '(.)({consonant})$', '%1о́%2') else: _.log_info('предыдущая не [жшчщ]') _.replace(p.stems, case, '(.)({consonant})$', '%1ё%2') # end else: _.log_info('ударение на основу в ["' + case + '"] ') _.replace(p.stems, case, '(.)({consonant})$', '%1е%2') # end # end # end # end if i.stem.type == '2-soft' and _.endswith( i.word.unstressed, 'ня' ) and i.stress_type == 'a' and p.endings['gen-pl'] == 'ь': p.endings['gen-pl'] = '' # вместо `ь` для `2*a` # end if gender == 'm' and i.adj and _.endswith( i.word.unstressed, 'ний') and p.endings['srt-sg'] == 'ь': p.endings['srt-sg'] = '' # вместо `ь` для `2*a`
def get_endings(func, i): # export # INFO: Выбор базовых окончаний по роду и типу основы ('1-hard' или '2-soft') p = i.parts # local unit = '' # todo: get from i.unit ? if i.adj: unit = 'adj' elif i.pronoun: unit = 'pronoun' else: unit = 'noun' # end _.log_value(unit, 'unit') _.log_value(i.unit, 'i.unit') all_endings = json_load( '../modules/dev/dev_py/ru/declension/data/endings/' + unit + '.json') gender = i.gender # local if i.unit == 'noun' and i.adj and i.gender == '': # случай вида "мозоленогие" (сущ.), когда ед. числа нет и рода нет gender = 'pl' i.calc_sg = False # todo: put this somewhere in the `parse` ? # end endings = all_endings[gender][i.stem.type] # local # клонирование окончаний # через mw.clone не работает, ошибка: "table from mw.loadData is read-only" p.endings = dict() # dict for key, value in endings.items(): p.endings[key] = value # end if i.unit == 'noun' and i.adj and i.gender != '': # для случая адъективного склонения существительных # нужно добавить не только текущий род, но и множественное число for key, value in all_endings['pl'][i.stem.type].items(): p.endings[key] = value # end # end # стр. 29: для 8-го типа склонения: # после шипящих `я` в окончаниях существительных заменяется на `а` if i.stem.type == '8-third' and _.endswith(i.stem.unstressed, '[жчшщ]'): p.endings['dat-pl'] = 'ам' p.endings['ins-pl'] = 'ами' p.endings['prp-pl'] = 'ах' # end # apply special cases (1) or (2) in index if not i.adj and not i.pronoun: # todo: move outside here (into `modify` package) noun_circles.apply_noun_specific_1_2(i) # end # Resolve stressed/unstressed cases of endings choose_endings_stress(i) # INFO: Особые случаи: `копьё с d*` и `питьё с b*` if i.gender == 'n' and i.stem.base_type == '2-soft' and _.endswith( i.word.unstressed, 'ё'): p.endings['nom-sg'] = 'ё' # end _.ends(module, func)