Модуль:Морфо
Описание
Модуль для использования в шаблонах вида {{морфо-ru}}
Механизм data-модулей для хранения правил:
Тестирование
Для удобного тестирования можно использовать следующую страничку: Шаблон:морфо-ru/tests
Категории ошибок
- Категория:Ошибка в морфемном разборе (неизвестный аффикс)/ru
- Категория:Ошибка в морфемном разборе (нет корня)/ru
- Категория:Ошибка в морфемном разборе (недопустимые символы)/ru
- Категория:Ошибка в морфемном разборе (не соответствует исходному слову)/ru
-- Модуль:морфо v1.2
-- 2016-04-19
local export = {}
local u = require("Module:utils")
local wu = require("Module:wiki-utils")
-- TODO: трансфиксы в арабском, и инфиксы в латыни // отменяется?
-- TODO: источники вынести в data-модуль // переносится на потом
local data = {}
local title
local morphemes = {}
local reference = ''
local categories = ''
local category_replacements = {}
local skip_categories = false
local DEBUG_CATEGORIES = false
local singular = {
root = 'корень',
prefixoid = 'префиксоид',
prefix = 'приставка',
suffix = 'суффикс',
postfix = 'постфикс',
suffixsoid = 'суффиксоид',
interfix = 'интерфикс',
ending = 'окончание',
verb_ending = 'глагольное окончание',
}
local plural = {
prefixoid = 'префиксоиды',
prefix = 'приставки',
suffix = 'суффиксы',
postfix = 'постфиксы',
suffixsoid = 'суффиксоиды',
interfix = 'интерфиксы',
ending = 'окончания',
verb_ending = 'глагольное окончания',
}
local plural_key = {
prefixoid = 'prefixoids',
prefix = 'prefixes',
suffix = 'suffixes',
postfix = 'postfixes',
suffixsoid = 'suffixsoids',
interfix = 'interfixes',
ending = 'endings',
verb_ending = 'verb_endings',
}
local instrumental = {
prefixoid = 'префиксоидом',
prefix = 'приставкой',
suffix = 'суффиксом',
postfix = 'постфиксом',
suffixsoid = 'суффиксоидом',
interfix = 'интерфиксом',
}
local notation = {
root = 'R',
prefixoid = 'R',
prefix = 'pr',
suffix = 's',
postfix = 'pt',
suffixsoid = 'R',
interfix = 'i',
ending = 'f',
verb_ending = 'f',
}
-- Утилитная функция для формирования wiki-кода для категории
function set_category(name)
return ' {{#ifeq:{{NAMESPACE}}|{{ns:0}}|[[Категория:' .. name .. ']]}}'
end
function add_morpheme(m_type, m_value)
table.insert(morphemes, {m_type=m_type, m_value=m_value})
end
function add_category(name)
categories = categories .. set_category(name)
if DEBUG_CATEGORIES then
categories = categories .. wu.span_silver(' [Категория:' .. name .. ']')
end
end
function process_data()
for m_type, items in pairs(data['category_replacements']) do
category_replacements[m_type] = {}
for _, values in pairs(items) do
if type(values) == 'table' then
category_replacements[m_type][values[1]] = values[2]
end
end
end
end
function parse_morphemes(args)
for key, value in pairs(args) do
cleared_value = mw.ustring.gsub(value, '[()]', '')
if type(key) == 'number' then
if mw.ustring.match(value, "^%-%-") ~= nil then
value = mw.ustring.sub(value, 2)
add_morpheme('suffixsoid', value)
elseif mw.ustring.match(value, "%-%-$") ~= nil then
value = mw.ustring.sub(value, 1, -2)
add_morpheme('prefixoid', value)
elseif mw.ustring.match(value, "^%-.*%-$") ~= nil then
add_morpheme('interfix', value)
elseif mw.ustring.match(value, "%-$") ~= nil then
value = mw.ustring.sub(value, 1, -2)
add_morpheme('prefix', value..'-')
elseif mw.ustring.match(value, "^%-") ~= nil then
if u.contains(data['known_morphemes']['postfixes'], cleared_value) then
add_morpheme('postfix', value)
else
add_morpheme('suffix', value)
end
elseif mw.ustring.match(value, "^%+") ~= nil then
fix_value = '-'..mw.ustring.sub(value, 2)
if u.contains(data['known_morphemes']['verb_endings'], cleared_value) then
add_morpheme('verb_ending', fix_value)
else
add_morpheme('ending', fix_value)
end
elseif value ~= '' then
add_morpheme('root', '-'..value..'-')
end
else
if key == 'и' then
if value == 'т' then
reference = '{{Тихонов}}'
elseif value == 'т2' or value == 'т-ся' then
if u.endswith(title, 'ся') then
reference = '{{Тихонов|' .. mw.ustring.sub(title, 1, -3) .. '(ся)}}'
else
reference = '{{Тихонов|2=(ся)}}'
end
elseif value == 'т-сь' then
if u.endswith(title, 'сь') then
reference = '{{Тихонов|' .. mw.ustring.sub(title, 1, -3) .. '(сь)}}'
else
reference = '{{Тихонов|2=(сь)}}'
end
elseif u.startswith(value, 'т:') then
reference = '{{Тихонов|' .. mw.ustring.sub(value, 3) .. '}}'
elseif u.contains({'т3'}, value) then
reference = '{{Тихонов-3}}'
elseif u.contains({'е'}, value) then
reference = '{{Ефремова}}'
skip_categories = true
elseif u.contains({'к', 'ке', 'к,е', 'к, е'}, value) then
reference = '{{Кузнецова и Ефремова}}'
skip_categories = true
else
-- reference = wu.span_red('(неизвестный источник)')
reference = ''
end
end
end
end
end
function prepare_output()
if #morphemes == 0 then
return "Корень: '''--'''." .. set_category('Нет сведений о составе слова/ru')
end
local items = {}
local structure = {}
local has_root = false
local unknown_affix = false
local wrong_characters = false
local check_title = ''
for i, morpheme in pairs(morphemes) do
local m_type = morpheme['m_type'] -- тип морфемы
local m_value = morpheme['m_value'] -- преобразованное значение для отображения
local m_cat_value = mw.ustring.lower(mw.ustring.gsub(m_value, '[()]', '')) -- убираем скобки для категории, а также нижний регистр
if mw.ustring.match(mw.ustring.lower(m_value), '^[-+а-яёj∅’()]*$') == nil and mw.ustring.match(m_value, '^-[A-Za-z]*-$') == nil then
wrong_characters = true
end
local check_morpheme = mw.ustring.gsub(m_value, '[-+j∅]', '')
check_morpheme = mw.ustring.gsub(check_morpheme, '%(.+%)', '')
check_title = check_title .. check_morpheme
local m_title = singular[m_type]
if u.contains({'prefix', 'prefixoid', 'suffix', 'suffixoid', 'postfix', 'interfix'}, m_type) then
if category_replacements[m_type] and category_replacements[m_type][m_cat_value] then
m_cat_value = category_replacements[m_type][m_cat_value]
end
end
-- local m_cat_value = mw.ustring.gsub(m_value, '%/.*', '')
-- m_value = mw.ustring.gsub(m_value, '%/', '{{!}}')
table.insert(structure, notation[m_type])
if u.contains({'prefixoid', 'prefix', 'suffix', 'postfix', 'interfix', 'suffixsoid'}, m_type) then
if m_type ~= 'interfix' and not skip_categories then
add_category('Русские слова с ' .. instrumental[m_type] .. ' ' .. m_cat_value) -- категоризация по типам морфем
m_value = '[[' .. m_cat_value .. '|' .. m_value .. ']]' -- добавление ссылки для морфемы
end
if i > 1 and morphemes[i-1]['m_type'] == m_type then
m_title = '' -- пропуск заголовка, если предыдущая морфема была того же типа
elseif i < #morphemes and morphemes[i+1]['m_type'] == m_type then
m_title = plural[m_type] -- использование множественного числа, если несколько подряд морфем одного типа
end
end
if u.contains({'prefixoid', 'prefix', 'suffix', 'postfix', 'interfix', 'ending'}, m_type) and not skip_categories then
key = plural_key[m_type]
value = m_cat_value
if m_type == 'ending' then
value = mw.ustring.gsub(m_cat_value, '^%-', '+')
elseif m_type == 'prefixoid' then
value = mw.ustring.gsub(m_cat_value, '%-$', '--')
end
if not u.contains(data['known_morphemes'][key], value) then
unknown_affix = true -- категоризация неизвестных морфем
m_value = m_value .. wu.span_red("<sup>?</sup>") -- добавление красного вопросика
end
end
if m_type == 'root' then
has_root = true
end
if m_title ~= '' then
item = m_title .. ': ' .. "'''" .. m_value .. "''';"
table.insert(items, item)
else
if m_type == 'interfix' and mw.ustring.sub(items[#items], -5, -5) ~= '>' then
items[#items] = mw.ustring.sub(items[#items], 1, -6) .. m_value .. "''';"
else
items[#items] = mw.ustring.sub(items[#items], 1, -5) .. m_value .. "''';"
end
end
-- TODO: add semantic annotations to items but only if the page title is the same as the parsed word.
end
if not skip_categories then
add_category('Русские слова, тип морфемного строения ' .. table.concat(structure, '-'))
if unknown_affix then
add_category('Ошибка в морфемном разборе (неизвестный аффикс)/ru')
end
end
if not has_root then
add_category('Ошибка в морфемном разборе (нет корня)/ru')
end
if wrong_characters then
add_category('Ошибка в морфемном разборе (недопустимые символы)/ru')
end
local title_without_hyphens = mw.ustring.gsub(title, '[-]', '')
-- mw.log('"' .. title_without_hyphens .. '"')
-- mw.log('"' .. check_title .. '"')
if check_title ~= title_without_hyphens then
if mw.ustring.lower(check_title) ~= mw.ustring.lower(title_without_hyphens) then
add_category('Ошибка в морфемном разборе (не соответствует исходному слову)/ru')
else
categories = categories .. '<span style="display: none;">{{отслеживание/не-совпадает-регистр-в-морфемном-разборе}}</span>'
--add_category('Ошибка в морфемном разборе (не соответствует регистру исходного слова)/ru')
end
end
local output = table.concat(items, ' ')
local result = mw.ustring.sub(u.capfirst(output), 1, -2)
if reference ~= '' then
result = result .. ' ' .. reference
end
result = result .. '.' .. categories
return result
end
function generate_docs()
result = ''
for i, m_type in pairs({'prefix', 'prefixoid', 'interfix', 'suffix', 'suffixsoid', 'ending', 'verb_ending', 'postfix'}) do
result = result .. "===" .. u.capfirst(plural[m_type]) .. "===" .. '\n'
result = result .. "{{кол|5}}\n"
values = data['known_morphemes'][plural_key[m_type]]
empty = true
for i, value in pairs(values) do
empty = false
value = mw.ustring.gsub(value, '%-%-', '-')
value = mw.ustring.gsub(value, '%+', '-')
if u.contains({'prefixoid', 'prefix', 'suffix', 'postfix', 'suffixsoid'}, m_type) then
cat_name = 'Русские слова с ' .. instrumental[m_type] .. ' ' .. value
result = result .. "# '''[[" .. value .. "]]'''" .. ' ([[:Категория:' .. cat_name .. "|кат]])\n" -- {{PAGESINCAT:" .. cat_name .. "}}
else
result = result .. "# '''" .. value .. "'''\n"
end
end
result = result .. "{{конец кол}}\n"
if empty then
result = result .. "# ''Пусто''\n"
end
end
return result
end
-- Главная функция, которая вызывается в модуле.
function export.get(frame)
local lang = frame.args['язык']
local args = u.clone(frame:getParent().args)
title = u.get_base()
data = mw.loadData("Module:морфо/data/" .. lang);
process_data()
parse_morphemes(args)
return frame:preprocess(prepare_output())
end
-- Функция, для генерации документации дата-модуля.
function export.docs(frame)
local lang = frame.args['язык']
data = mw.loadData("Module:морфо/data/" .. lang);
return frame:preprocess(generate_docs())
end
return export