ФЭНДОМ


local library = {}
 
library.toTable = function( var )
    if type( var ) == 'table' then
        return var
    else
        return { var }
    end
end
library.errorPrint = function( err )
    local wrapper = mw.html.create( 'div' )
    wrapper:css( 'color', 'red' )
    wrapper:wikitext( err..'[[Категория:Pages with script errors]]' )
    return wrapper
end
 
library.string = {}
--ФУНКЦИИ ДЛЯ СТРОК
--[[
    разбиение строки на массив подстрок по определенной группе символов
    str - строка, подлежащая разбиению,
    separator - строка или, если pattern == tue, образец
    возвращает массив, с подстроками если str == separator, то пустой массив
    два сепаратора, идущие подряд в строке, рассматриваются как один
]]--
library.string.split = function( str, separator, pattern, condition )
    local result = {}
    if pattern then
        pattern = false
    else
        pattern = true
    end
    local s, e, pE, pS = 0, 0, 0, 0
    local length = mw.ustring.len( str )
    while e ~= length do
        s, e = mw.ustring.find( str, separator, pE + 1, pattern )
        if ( not s or not e ) then
            result[ #result + 1 ] = mw.ustring.sub( str, pS + 1, length )
            break
        elseif not condition or condition( str, {s, e, pE, pS} ) then
            local substring = mw.ustring.sub( str, pS + 1, s - 1 )
            if substring ~= ''  then
                result[ #result + 1 ] = substring
            end
            pS = e
        end
        pE = e
    end
    return result
end
library.string.join = function( arr, separator, condition, associative )
    local result = ''
    local checkAndAddition = function( val, key )
        if not condition or condition( val, key ) then
            if result ~= '' then
                result = result..separator
            end
            result = result..val
        end
    end
    if associative then
        for key, val in pairs( arr ) do
            checkAndAddition( val, key )
        end
    else
        for i, val in ipairs( arr ) do
            checkAndAddition( val, i )
        end
    end
    return result
end
 
library.parseLinks = function( text, jsep, ssep )
    if not jsep then
        jsep = ''
    end
    if not ssep then
        ssep = ','
    end
    local fragments = {}
    local cS, cE, pE = 0, 0, 0
    local length = mw.ustring.len( text )
    while cE ~= length do
        cS, cE = mw.ustring.find( text, '[^\\]'..ssep, cE + 1 )
        if not cS then
            fragments[ #fragments + 1 ] = mw.ustring.sub( text, pE + 1, length )
            break
        end
        fragments[ #fragments + 1 ] = mw.ustring.sub( text, pE + 1, cE - 1 )
        pE = cE
    end
    local result = {}
    for i, val in ipairs( fragments ) do
        --снос сепараторов вначале и пробелов в начале
        local str = mw.ustring.gsub( val, '^['..ssep..'%s]*', '' )
        --снос пробелов в конце
        str = mw.ustring.gsub( str, '%s*$', '' )
        if str ~= '' then
            if mw.ustring.sub( str, 1, 2 ) == '\\w' then
                result[ #result + 1 ] = mw.ustring.sub( str, 3 )
            else
                --замена разделителей
                str = mw.ustring.gsub( str, '\\!', '|' )
                --снос неэкранированных обратных слешей
                str = mw.ustring.gsub( str, '([^\\])[\\]', '%1' )
                if mw.ustring.sub( str, 1, 1 ) == '\\' then
                    str = mw.ustring.sub( str, 2 )
                end
                result[ #result + 1 ] = '[['..str..']]'
            end
        end
    end
    return library.string.join( result, jsep )
end
 
--ФУНКЦИИ ДЛЯ ТАБЛИЦ
library.table = {}
library.table.indexOf = function( table, searchElement, fromIndex )
    local startIndex
    if fromIndex then
        startIndex = fromIndex
    else
        startIndex = 1
    end
    for i = startIndex, #table do
        if table[ i ] == searchElement then
            return i
        end
    end
    return nil
end
library.table.getKeys = function( asTable )
    local keys = {}
    for key, val in pairs( asTable ) do
        table.insert( keys, key )
    end
    return keys
end
library.table.mergeTables = function( ... )
    local result = {}
    for i, table in ipairs( arg ) do
        for key, val in pairs( table ) do
            result[ key ] = val
        end
    end
    return result
end
 
--ФУНКЦИИ ДЛЯ АРГУМЕНТОВ
library.arguments = {}
library.arguments.err = function( argName, listAllowVals )
    if not listAllowVals then
        error( 'Параметр "'..argName..'" должен быть определён' )
    else
        listAllowVals = library.toTable( listAllowVals )
        local allowVals = library.string.join( listAllowVals, ' or ', nil, true )
        error( 'Параметр "'..argName..'" должен быть: "'..allowVals..'"' )
    end
end
library.arguments.check = function( argName, allowVals, forbidNil )
    local self = library.arguments
    local arg = mw.getCurrentFrame().args[ argName ]
    if not arg then
        if not forbidNil then
            return
        else
            self.err( argName )
        end
    end
    if not allowVals then
        return
    end
    allowVals = library.toTable( allowVals )
    for i, val in ipairs( allowVals ) do
        if arg == val then
            return
        end
    end
    self.err( argName, allowVals )
end
library.arguments.checkAndReturn = function( argName, allowArgs, default, forbidNil )
    library.arguments.check( argName, allowArgs, forbidNil )
    local arg = mw.getCurrentFrame().args[ argName ]
    if arg then
        return arg
    else
        return default
    end
end
library.arguments.checkAndTranslate = function( argName, dictionary, default, forbidNil )
    local allowList = library.table.getKeys( dictionary )
    library.arguments.check( argName, allowList, forbidNil )
    local arg = mw.getCurrentFrame().args[ argName ]
    if arg then
        return dictionary[ arg ]
    else
        return default
    end
end
 
--ПАРСЕР
library.parser = {}
--[[
Создание настройки:
1. создание объекта с нуля
1.1 создание обекта шаблонов - patterns. все свойства строковые. все свойства обязательны.
1.1.1. patterns.esc - экранирующий символ.
1.1.2. patterns.skip - пропуск фрагмента.
1.1.3. patterns.skipAll - пропуск всей строки.
1.1.4. patterns.seq1 - начальная часть последовательностей.
1.1.5. patterns.seq2 - конечная часть последовательностей.
1.1.6. patterns.sep1 - начальная часть сепараторов.
1.1.7. patterns.sep2 - конечная часть сепараторов.
1.2  sequences - управляющие последовательности - ассоциативный массив. значение - строка, либо функция, возвращающая строку. В функцию передается сама последовательность.
1.3 separators - массив разделителей.Состоит из массивов по первым элементам которых, строка делится на фрагменты, а по вторым склеивается. Сначала строка делится на фрагменты по первому сепаратору, потом полученные фрагменты делятся по следующему сепаратору и т.д. Склеивание происходит в обратном порядке.
1.4 textHandler - функция, которая вызывается для каждого фрагмента деления по последнему сепаратору, перед склейкой.
Объект должен в себя включать patterns. Прочие значения необязательны.
2. создание объекта на базе имеющегося.
2.1 baseSettingName - имя базовой настройки. строка.
2.2 patterns - содержащиеся свойства заменяют свойства базовой настройки.
2.3 actions - действия, совершаемые над объектами sequences, separators и textHandler. Для sequences и separators: set - редактирование, replace - замена. Для textHandler - remove удаление, replace - замена. Если действие пропущено, соответствующий объект не изменяется.
2.4 sequences - при замене создается новый объект, при редактировании, если свойство строка - добавляется новое свойство, либо заменяется текущее, если свойство - false.
2.5 separators - при замене создается новый массив, при редактировании, если свойство объект - добавляется новое свойство, либо заменяется текущее, если свойство - false. при этом, все элементы массива, номером выше удаляемого, становятся недоступны.
Объект должен включать в себя baseSettingName, прочие значения необязательны.
Для добавления нового объекта используется функция addSetting, первый аргумент - настройка setting, а вторая - название.
 
В случае пропуска фрагмента, он не обрабатывается и остается как есть. Исключение - экранированные сепараторы, стоящие перед ними экранирующие символы обрабатываются в общем порядке.
]]--
 
--настройки
library.parser._settings = {}
library.parser._settings.standart = {
    patterns = {
        esc = '\\',
        skip = 'w',
        skipAll = 'W',
        seq1 = '\\',
        seq2 = '',
        sep1 = '',
        sep2 = ''
    },
    sequences = {
        ['!'] = '|',
        ['n'] = '<br />'
    },
    separators = {
        [1] = {',', ' • ' }
    },
    textHandler = function( text )
        local featuredText = mw.ustring.gsub( text, '^%s*', '' )
        featuredText = mw.ustring.gsub( featuredText, '%s*$', '' )
        return '[['..featuredText..']]'
    end
}
library.parser._settings.test1 = {
    baseSettingName = 'standart',
    actions = {
        sequences = 'replace',
        separators = 'set',
        textHandler = 'remove'
    },
    patterns = {
        seq2 = ' ',
        sep1 = '_',
        sep2 = '_'
    },
    sequences = {--удаление - false
        ['!'] = '?',
        ['*'] = '#'
    },
    separators = {--удаление - false все элементы старше номером удаляемого, будут недоступны.
        [2] = { ':', '--' }
    }
}
function library.parser.addSetting( setting, settingName )
    local newSetting = mw.clone( setting )
    if not setting.sequences then
        setting.sequences = {}
    end
    if not setting.separators then
        setting.separators = {}
    end
    if setting.baseSettingName and not setting.actions then
        setting.actions = {}
    end
    library.parser._settings[ settingName ] = setting
end
function library.parser.create( setting )
    local self = library.parser
    local obj = {}
    obj._library = library
    obj._settings = library.parser._settings
    setmetatable( obj, self )
    self.__index = self
    if not setting then
        obj:loadSettings( 'standart' )
    else
        obj:loadSettings( setting )
    end
    return obj
end
function library.parser:loadSettings( setting, recursion )
    local settings = library.parser._settings
    local currentSetting
    if type( setting ) == 'table' then
        currentSetting = setting
    elseif type( setting ) == 'string' and settings[ setting ] then
        currentSetting = settings[ setting ]
    end
    local newSetting = {}
    if currentSetting.baseSettingName then
        --1. погружение до корневой настройки
        newSetting = self:loadSettings( currentSetting.baseSettingName, true )
        --3.1 редактирование корневой настройки на каждом этапе всплытия
        if currentSetting.patterns then
            for key, pattern in pairs( currentSetting.patterns ) do
                newSetting.patterns[ key ] = pattern
            end
        end
        if currentSetting.actions.sequences == 'set' then
            for key, sequence in pairs( currentSetting.sequences ) do
                if sequence then
                    newSetting.sequences[ key ] = sequence
                else
                    newSetting.sequences[ key ] = nil
                end
            end
        elseif currentSetting.actions.sequences == 'replace' then
            newSetting.sequences = mw.clone( currentSetting.sequences )
        end
        if currentSetting.actions.separators == 'set' then
            for key, separator in pairs( currentSetting.separators ) do
                if separator then
                    newSetting.separators[ key ] = separator
                else
                    newSetting.separators[ key ] = nil
                end
            end
        elseif currentSetting.actions.separators == 'replace' then
            newSetting.separators = mw.clone( currentSetting.separators )
        end
        if currentSetting.actions.textHandler == 'replace' then
            newSetting.textHandler = currentSetting.textHandler
        elseif currentSetting.actions.textHandler == 'remove' then
            newSetting.textHandler = nil
        end
    else
        --2. взятие корневой настройка
        newSetting.patterns = mw.clone( currentSetting.patterns )
        newSetting.sequences = mw.clone( currentSetting.sequences )
        newSetting.separators = mw.clone( currentSetting.separators )
        newSetting.textHandler = currentSetting.textHandler
    end
    if recursion then
        --3. всплытие до требуемой настройки
        return newSetting
    else
        --Запись итоговой настройки
        self._sequences = newSetting.sequences
        self._separators = newSetting.separators
        self._textHandler = newSetting.textHandler
        self:_loadPatterns( newSetting.patterns )
    end
end
function library.parser:_loadPatterns( patterns )
    local pat = patterns
    local esc = '('..pat.esc..'*)'
    self._patterns = {
        seq1 = esc..pat.seq1,
        seq2 = pat.seq2,
        skip1 = esc..pat.seq1..pat.skip,
        skip2 = pat.seq2,
        skipAll = esc..pat.seq1..pat.skipAll..pat.seq2,
        sep1 = pat.sep1,
        sep2 = pat.sep2,
        esc = pat.esc
    }
end
function library.parser:_checkScreening( arg1, arg2 )
    if arg2 then
        local text = arg1
        local symbolPosition = arg2
        local numberOfScrs = 0
        local currentScrPosition = symbolPosition - 1
        local scrSymbol = mw.ustring.sub( text, currentScrPosition, currentScrPosition )
        while currentScrPosition > 0 and scrSymbol == self._patterns.esc do
            numberOfScrs = numberOfScrs + 1
            currentScrPosition = currentScrPosition - 1
            scrSymbol = mw.ustring.sub( text, currentScrPosition, currentScrPosition )
        end
        if numberOfScrs % 2 ~= 0 then
            return true
        end
    else
        local esc = arg1
        local length = mw.ustring.len( esc )
        if length % 2 ~= 0 then
            return true
        end
    end
end
--проверка на отключение парсера и удаление управляющих этим элементов разметки
function library.parser:_checkSkip( text, pattern )
    local skip = false
    local finalText = mw.ustring.gsub(text, pattern, function( esc )
        if not self:_checkScreening( esc ) and not skip then
            skip = true
            return esc
        end
    end)
    return finalText, skip
end
function library.parser:parse( text, data )
--высший уровень
    --проверка на полное отключение парсера для строки
    local finalText, skipAll = self:_checkSkip( text, self._patterns.skipAll )
    --если отключен, возврат строки без изменений
    if skipAll then
        return finalText
    end
    --отправка строки на нижний уровень
    return self:_parse( text, 1, data )
end
function library.parser:_parse( text, no, data )
    local pat = self._patterns
    local sep = self._separators[no]
    local seq = self._sequences
    --Проверка на отключение парсера для участка строки
    local finalText, globalSkip = self:_checkSkip( text, pat.skip1..no..pat.skip2 )
    --если отключен, вернуть на верхний уровень без изменений
    if globalSkip then
        return finalText
    end
    --если есть разделитель
    if sep then
        --разделение строки
        local fragments = self._library.string.split(text, pat.sep1..sep[1]..pat.sep2, false, function(text, points)
            return not self:_checkScreening( text, points[1] )
        end)
        --проброска на нижний уровень
        local results = {}
        for i, fragment in ipairs( fragments ) do
            --очистка символов, экранирующих незадействованные разделители 
            fragment = mw.ustring.gsub(fragment, '('..pat.esc..'+)('..sep[1]..')', function( escSymbs, sep )
                local symbsLen = mw.ustring.len( escSymbs )
                local finalSymbsLen  = math.floor( symbsLen / 2 )
                if finalSymbsLen == 0 then
                    return sep
                else
                    return mw.ustring.sub( escSymbs, 1, finalSymbsLen )..sep
                end
            end)
            results[ #results + 1 ] = self:_parse( fragment, no + 1, data )
        end
        --склейка строки из результатов работы функций нижнего уровня
        return self._library.string.join(results, sep[2] , function( text )
            if text ~= '' then
                return true
            end
        end)
    --если разделителя нету обработка строки на низшем уровне
    else
        --проверка на отключение парсера для участка строки
        local finalText, localSkip = self:_checkSkip( text, pat.skip1..pat.skip2 )
        --если отключен, вернуть на верхний уровень без обработки
        if localSkip then
            return finalText
        end
        --обработка управляющих последовательностей
        local optionParametres = {}
        for key, val in pairs( seq ) do
            finalText = mw.ustring.gsub(finalText, pat.seq1..'('..key..')'..pat.seq2, function( esc, seq )
                if self:_checkScreening( esc ) then
                    return
                end
                optionParametres[ key ] = seq
                if type( val ) == 'function' then
                    return esc..val( seq, data )
                else
                    return esc..val
                end
            end)
        end
        --очистка лишних экранирующих символов
        finalText = mw.ustring.gsub(finalText, pat.esc..'*', function( escSymbols )
            local symbsLen = mw.ustring.len( escSymbols )
            local finalSymbsLen  = math.floor( symbsLen / 2 )
            if finalSymbsLen == 0 then
                return ''
            else
                return mw.ustring.sub( escSymbols, 1, finalSymbsLen )
            end
        end)
        --обработка результата в функции, если она есть, и возврат его на верхний уровень
        if self._textHandler then
            return self._textHandler( finalText, optionParametres, data )
        else
            return finalText
        end
    end
end
 
function library:test()
    local test3 = {
        patterns = {
            esc = '\\',
            skip = 'w',
            skipAll = 'W',
            seq1 = '\\',
            seq2 = '',
            sep1 = '',
            sep2 = ''
        },
        sequences = {
            ['!'] = '|'
        },
        separators = {
            [1] = {',', ' • ' }
        },
        textHandler = function( text )
            return '[['..text..']]'
        end
    }
    local test2 = {
        baseSettingName = 'test3',
        actions = {
            sequences = 'set',
            separators = 'set'
        },
        patterns = {
            seq2 = ' ',
            sep1 = '_',
            sep2 = '_'
        },
        sequences = {--удаление - false
            ['!'] = false
        },
        separators = {--удаление - false все элементы старше номером удаляемого, будут недоступны.
            [1] = false
        },
        textHandler = function( text )
            return '['..text..']'
        end
    }
    local test4 = {
        baseSettingName = 'standart',
        actions = {
            separators = 'set'
        },
        separators = {
            [2] = {':', ' ! ' }
        }
    }
    self.parser.addSetting( test3, 'test3' )
    self.parser.addSetting( test2, 'test2' )
    self.parser.addSetting( test4, 'test4' )
    local p = self.parser:create('test4')
    local s = '    1 1 1\\!          ,             2 2 2            '
    local s2 = '\\* \\! 111\\! _,_33_:_222_-_33'
    local s3 = '\\w 1\\\\\\:11:22\\,2,333:444'
    return p:parse(s3)
end
 
return library

Обнаружено использование расширения AdBlock.


Викия — это свободный ресурс, который существует и развивается за счёт рекламы. Для блокирующих рекламу пользователей мы предоставляем модифицированную версию сайта.

Викия не будет доступна для последующих модификаций. Если вы желаете продолжать работать со страницей, то, пожалуйста, отключите расширение для блокировки рекламы.

Также на ФЭНДОМЕ

Случайная вики