Module:stringconvert
Documentation for this module may be created at Module:stringconvert/doc
local p = {}
local GRAVE = mw.ustring.char(0x0300)
local ACUTE = mw.ustring.char(0x0301)
local MACRON = mw.ustring.char(0x0304)
local BREVE = mw.ustring.char(0x0306)
local DIAER = mw.ustring.char(0x0308)
local grave_tbl =
{
["Ѐ"] = "Е", ["ѐ"] = "е",
["Ѝ"] = "И", ["ѝ"] = "и", [GRAVE] = ''
}
local function strip_grave(str)
for k, v in pairs( grave_tbl ) do
str = mw.ustring.gsub(str, "[" .. k .. "]", function(c)
return v
end)
end
return str;
end
local macron_tbl =
{
["ĀĂ"] = "A", ["āă"] = "a",
["ĒĔË"] = "E", ["ēĕë"] = "e",
["ĪĬÏ"] = "I", ["īĭï"] = "i",
["ŌŎ"] = "O", ["ōŏ"] = "o",
["ŪŬÜ"] = "U", ["ūŭü"] = "u",
["Ȳ"] = "Y", ["ȳ"] = "y",
[GRAVE .. ACUTE .. MACRON .. BREVE .. DIAER] = ''
}
--[[
strip_macrons
Эта функция удаляет макроны, знаки ударения и заменяет слоговые знаки на обычные.
Использование:
{{#invoke:stringconvert|strip_macrons|текст}}
]]
function p.strip_macrons(str)
if type(str) == 'table' then -- проверка на глобальное использование
str = str.args[1]
end
for k, v in pairs( macron_tbl ) do
str = mw.ustring.gsub(str, "[" .. k .. "]", function(c)
return v
end)
end
return str;
end
--[[
correct_accent
Эта функция убирает знак ударения в слове с одной гласной.
Использование:
{{#invoke:stringconvert|correct_accent|текст}}
]]
function p.correct_accent(str)
if type(str) == 'table' then -- проверка на глобальное использование
str = str.args[1]
end
local vowel = '[аеиоуыэюяАЕИОУЫЭЮЯ]'
local index = mw.ustring.find(str, vowel, 1, false); -- позиция гласной
if index ~= nil and mw.ustring.find(str, vowel, index+1, false) ~= nil then
return str; -- если больше одной гласной то возвращаем без изменения
else
str = mw.ustring.gsub(str, ACUTE, '') -- убираем знак ударения
return str
end
end
--[[
aslink
Эта функция преобразует разделённый запятыми текст в разделённые запятыми викиссылки.
Встречающиеся знаки ударения сохраняются в тексте ссылки, но убираются из цели.
Использование:
{{#invoke:stringconvert|aslinks|текст|разделитель}}
или
{{#invoke:stringconvert|aslinks|text=текст|delimiter=разделитель|nospace=1|hide=слово|nocomment=1|lang=код языка}}
]]
function p.aslinks ( frame )
-- разбор параметров
local function getParameters( frame_args, arg_list )
local new_args = {};
local index = 1;
local value;
for i,arg in ipairs( arg_list ) do
value = frame_args[arg]
if value == nil then
value = frame_args[index];
index = index + 1;
end
new_args[arg] = value;
end
return new_args;
end
-- определение булевого значения в параметре
function getBoolean( boolean_str )
local boolean_value = false;
if type( boolean_str ) == 'string' then
boolean_str = boolean_str:lower();
if boolean_str == 'false' or boolean_str == 'no' or boolean_str == '0' or boolean_str == '' then
boolean_value = false;
else
boolean_value = true;
end
elseif type( boolean_str ) == 'boolean' then
boolean_value = boolean_str;
end
return boolean_value
end
local new_args = getParameters( frame.args, {'text', 'delimiter', 'nospace', 'hide', 'nocomment', 'lang' } );
local hide_word = new_args['hide'] or '';
local text = new_args['text'] or '';
local delimiter = new_args['delimiter'];
if delimiter == nil or delimiter == '' then
delimiter = ',';
else
delimiter = mw.text.decode(delimiter);
end
local nospace = getBoolean( new_args['nospace']);
if not nospace then
-- добавляем пробел и неразрывный пробел
delimiter = delimiter .. ' ' .. mw.ustring.char(160);
end
local nocomment = getBoolean( new_args['nocomment']);
if nocomment then
delimiter = delimiter .. '%(%)';
end
local lang= new_args['lang'] or '';
local langlink = '';
if lang ~= '' then
local trimmed = mw.text.trim(lang);
if trimmed == 'ru' then
langlink = "Русский"
else
local languages = mw.loadData("Module:languages/data");
local l = languages[trimmed];
if l and l[2] then
langlink = l[2];
end
end
end
-- разделение строки
local function mygsplit(str, pattern)
local l = mw.ustring.len( str );
return function (state, s)
if s <= l then
local e, n = mw.ustring.find( str, pattern, s, false );
local ret;
local sep = '';
if not e then
ret = mw.ustring.sub( str, s );
s = l + 1
elseif n < e then
ret = mw.ustring.sub( str, s, e );
if e < l then
s = e + 1;
else
s = l + 1;
end
else
ret = e > s and mw.ustring.sub( str, s, e - 1 ) or '';
sep = mw.ustring.sub( str, e, n );
s = n + 1
end
return s, ret, sep
else
s = nil;
return s, ret, sep
end
end, nil, 1 -- iterator, state, initial value
end
-- поиск тэгов с учётом вложенности
local function findtag( str, s )
local ct = 1;
local start_pos = 1;
local start_tag = '';
while true do
local e, n, close_tag, tag_name, single_tag = mw.ustring.find( str, "<(/?)([A-Za-z]+).-(/?)>", s, false );
if not e then
return nil, -1
else
if single_tag ~= '' then
if start_tag == '' then
return e, n; -- найден одиночный непарный тэг
else
s = n + 1; -- пропускаем вложенный непартный тэг
end
elseif close_tag ~= '' then
if start_tag == '' then
return e, n; -- найден одиночный закрывающий тэг
elseif tag_name == start_tag then
ct = ct - 1;
if ct == 1 then
return start_pos, n; -- найден закрывающий тэг
else
s = n + 1; -- найден вложенный закрывающий тэг
end
else
s = n + 1; -- пропускаем вложенный закрывающий тэг
end
else
if start_tag == '' then
start_tag = tag_name;
start_pos = e;
ct = ct + 1;
s = n + 1; -- найден первый открывающий тэг
elseif tag_name == start_tag then
ct = ct + 1;
s = n + 1; -- найден вложенный открывающий тэг с тем же именем
else
s = n + 1; -- пропускаем вложенный открывающий тэг
end
end
end
end
end
-- разделение тэгов
local function tsplit(str)
local l = mw.ustring.len( str );
return function (state, s)
if s <= l then
local e, n = findtag( str, s );
local ret;
local sep = '';
if not e then
ret = mw.ustring.sub( str, s );
s = l + 1
elseif n < e then
ret = mw.ustring.sub( str, s, e );
if e < l then
s = e + 1;
else
s = l + 1;
end
else
ret = e > s and mw.ustring.sub( str, s, e - 1 ) or '';
sep = mw.ustring.sub( str, e, n );
s = n + 1
end
return s, ret, sep
else
s = nil;
return s, ret, sep
end
end, nil, 1 -- iterator, state, initial value
end
-- разделение оставшегося текста
local function myparser(text, level)
if text ~= '' then
if mw.ustring.find(text, '[|&]', 1, false) ~= nil or -- не обрабатываем текст, содержащий пайп и апмерсант
mw.ustring.find(text, '^[%p%d]+$', 1, false) ~= nil then -- и одиночные знаки пунктуации и цифры
return text;
else
local before, str, after = mw.ustring.match(text, "^(%s*)(.-)(%s*)$", 1);
if before ~= nil and str ~= nil and str ~= '' then
local section = ''
local link = str;
if lang == "la" then
link = p.strip_macrons(str) -- убираем диакритические знаки
else
link = mw.ustring.gsub(str, ACUTE, '') -- убираем только знак ударения
link = strip_grave(link) -- убираем гравис
if link ~= str and (lang == '' or lang == 'ru') then
section = mw.ustring.match( strip_grave(str), '[^#]+'); -- секция с ударением
end
end
local page, loc = mw.ustring.match( link, '^([^#]-)#([^#]-)$');
if page ~= nil then
loc = mw.ustring.gsub(loc, '%%28', '(');
loc = mw.ustring.gsub(loc, '%%29', ')');
if section ~= '' then
section = section .. '_'.. loc
else
section = page .. '_'.. loc
end
local num, ext = mw.ustring.match(loc, '^([XVI]+)_?(.-)$')
if num ~= nil then
-- добавляем римские цифры
str = mw.ustring.gsub( str, '^([^#]-)#([XVI]+)_?.-$', '%1<sup>%2</sup>')
if nocomment and ext ~= '' then
after = ' ' .. ext .. after
end
else
str = mw.ustring.gsub( str, '^([^#]-)#([^#]-)$', '%1');
end
link = page
end
if section == '' then
section = langlink;
end
if str ~= link or section ~= '' then
str = link .. '#' .. section .. '|'.. str;
end
text = before .. "[[" .. str .. "]]" .. after;
end
end
end
return text;
end
local handlers = {};
-- разделение текста по шаблону
local function parse(text, level)
local result = "";
local pattern = handlers[level][1];
local parser = handlers[level][2];
for i, str, sep in mygsplit(text, pattern, false ) do
if str ~= "" then
result = result .. parser(str, level+1);
end
if sep ~= "" then
result = result .. sep;
end
end
if result == "" then
return text;
else
return result;
end
end
local stripers = {};
local striped = {};
local stripid = 0;
local k = 1;
-- замена текста с сохранением
local function striptext(text)
stripid = stripid +1;
local key = '<' .. stripid .. '>';
striped[key] = text;
return key;
end
-- вырезание текста по шаблону
local function strip(text)
for j, pattern in ipairs(stripers) do
local result = "";
for i, str, sep in mygsplit(text, pattern, false ) do
if str ~= "" then
result = result .. str;
end
if sep ~= "" then
result = result .. striptext(sep);
end
end
if result ~= '' then
text = result; -- текст для следующего обработчика
end;
end
return text;
end
-- восстановление вырезанного текста по шаблону
local function unstrip(text)
local result = '';
for i, str, sep in mygsplit(text, "<%d->", false ) do
if str ~= "" then
result = result .. str;
end
if sep ~= "" then
result = result .. unstrip(striped[sep]);
end
end
return result;
end
-- вырезание тэгов
local function striptags(text)
local result = "";
for i, str, sep in tsplit(text) do
if str ~= "" then
result = result .. str;
end
if sep ~= "" then
result = result .. striptext(sep);
end
end
if result == '' then
return text;
else
return result;
end
end
-- обратная совестимость с шаблоном lang, куда иногда передается текст сразделенный шаблоном {{!}}
text = mw.ustring.gsub(text, '^%s*([^%[|,;]+|[^%]|,;]+)%s*$', '[[%1]]');
-- маскируем круглые скобки внутри ссылок
text = mw.ustring.gsub(text, '%s*[^#<>%[%]%p]-#[XVI]-%s*%([^|<>%[%]%)]-%)%s*[,;]*', function (c)
return mw.ustring.gsub(c, ' ?[%(%)]', function (ch)
if ch == ' (' then
return '_%28'
elseif ch == '(' then
return '%28'
elseif ch == ' )' or ch == ')' then
return '%29'
end
end)
end)
text = striptags(text); -- вырезаем HTML тэги
stripers = {}
table.insert(stripers, "{|.*|}"); -- исключаем таблицы
if not nocomment then
table.insert(stripers, "%b()"); -- исключаем текст в круглых скобках
end
table.insert(stripers, "%[%[.-%]%]%S*"); -- исключаем внутренние ссылки
table.insert(stripers, "%b[]"); -- исключаем внешние ссылки
table.insert(stripers, "'''[^']-'''"); -- исключаем текст в тройных одинарных кавычках
table.insert(stripers, "''[^']-''"); -- исключаем текст в двойных одинарных кавычках
text = strip(text); -- вырезаем по шаблону
local l = 1;
handlers[l] = { "\r?\n", parse }; l=l+1; -- исключаем разделители строк
handlers[l] = { "%b<>", parse }; l=l+1; -- исключаем текст в угловых скобках
handlers[l] = { "%s*[" .. delimiter .."]+%s*", myparser }; -- разделяем оставшееся
text = parse(text, 1); -- обрабатываем
if stripid ~= 0 then
text = unstrip(text); -- восстанавливаем вырезанное
end;
if hide_word ~= '' then
local cnt = 0;
-- удаление ссылки на слово
hide_word = mw.ustring.gsub( hide_word, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ); -- маскируем спецсимволы
text, cnt = mw.ustring.gsub( text, '%[%[' .. hide_word ..'%|[^%]]*%]%]%w* *,? *', '' );
if cnt == 0 then
text, cnt = mw.ustring.gsub( text, '%[%[' .. hide_word ..'%]%]%w* *,? *', '' );
end
if cnt ~= 0 then
text = mw.ustring.gsub( text, '^%p+$', '' ); -- удаляем строку состоящую только из разделительных знаков
text = mw.ustring.gsub( text, '[,;] *;', ';' ); -- удаляем запятую перед точкой с запятой
text = mw.ustring.gsub( text, ', *$', '' ); -- удаляем запятую в конце строки
text = mw.ustring.gsub( text, '^[;,] *', '' ); -- удаляем запятую или точку с запятой в начале строки
end
end
return text;
end
return p