Модуль:Transclude: различия между версиями

Материал из Тептар — свободной энциклопедии
Перейти к навигации Перейти к поиску
(Новая страница: «local M={} --------------------------------------------------------- -- Функции для работы с параметрами вызвавшего шабл...»)
 
Нет описания правки
 
Строка 1: Строка 1:
local M={}
local p={}
---------------------------------------------------------
 
-- Функции для работы с параметрами вызвавшего шаблона --
-- Вызывает внутренний шаблон с аргументами объемлющего шаблона
---------------------------------------------------------
function p.call(frame)
function M.uni(f)-- Унификация имён параметров; псевдонимы.
local template = frame.args[1]
    local tf, cs=f:getParent(), {};
local args = frame:getParent().args
     local findcs=function(a) cs[a]=f.args[a] end;
     return frame:expandTemplate{ title=template, args=args }
    (f.args['|фиксрег'] or f.args['|fixreg'] or ''):gmatch(' *([^%n]+) *', findcs);
end
    -- {{!}}фиксрег = список разделённых новыми строками аргументов, регистр которых не менять
 
    local args,am={};
-- Общая реализация для forall и call
    for a,v in pairs(tf.args) do
local function forallImpl(args, separator, conjunction, func)
        am = a:lower():gsub('[_ ]+',' ');
-- нумерованные ключи из args
        args[ cs[a] and a  or  ( f.args[am] or am ) ] = v
local keys = {}
    end
-- перебор в произвольном порядке, даже для нумерованных ключей
    return tf:expandTemplate{title=f.args[1]; args=args}
for key, value in pairs(args) do
if type(key) == 'number' and value and value ~= '' then
table.insert(keys, key)
end
end
table.sort(keys)
local results = {}
for _, key in ipairs(keys) do
local value = func(args[key])
table.insert(results, value)
end
return mw.text.listToText(results, separator, conjunction)
end
end


function M.forall(frame)-- Итератор по нумерованным аргументам вызывающего шаблона.
-- Вызывает внутренний шаблон, передавая ему нумерованные параметры объемлющего шаблона по-одному
function p.forall(frame)
local template = frame.args[1]
local template = frame.args[1]
local separator = frame.args.separator or ''
local separator = frame.args.separator or ''
local conjunction = frame.args.conjunction or separator
local conjunction = frame.args.conjunction or separator
local args = frame:getParent().args
local results = {}
local func = function(value)
for param, value in pairs(frame:getParent().args) do
return frame:expandTemplate{ title = template, args = {value} } -- или другой frame?
if type(param) == 'number' then
results[param] = frame:expandTemplate{ title = template, args = {value} }
end
end
end
return mw.text.listToText( results, separator, conjunction )
return forallImpl(args, separator, conjunction, func)
end
end


function M.escapeparams(f)-- Нормализация
-- Берёт нумерованные аргументы объемлющего шаблона и склеивает их в единую строку
-- (обезопасивание) значений параметров.
function p.join(frame)
    local i,ac,acn=0,{},{};
local separator = frame.args[1] or ''
    local function repl(s)
local conjunction = frame.args[2] or separator
        return s:gsub('{{','{{Х'):gsub('}}',"{{ЪЪ}}"):gsub('{{Х','{{ХХ}}')--: --замена фигурных скобок
local args = frame:getParent().args
          :gsub('=','{{=}}'):gsub('|','{{!}}')
local func = function(value)
    end
return value
    for k,v in pairs(f:getParent().args) do
end
        if type(k)=='number' then ac[k]=repl(v); i=i+1
return forallImpl(args, separator, conjunction, func)
        else acn[repl(k)]=repl(v)end
    end
    if i ~= #ac-1 then --нумерованные параметры не сплошные
        for k,v in pairs(ac) do
            if k>i then acn[tostring(k)] = v; ac[k]=nil end-- удалять в pairs можно
        end
    end
    for k,v in pairs(acn) do
        table.insert(ac, table.concat(f.args[2] or "\n ", k, f.args[3] or ' = ', v, f.args[4] or '') )
    end
    return table.concat(ac,'|')
end
end


function M.npc(f)-- Итератор по именованно-нумерованным параметрам.
-- Служебная функция: удаляет дубликаты из отсортированного массива с нумерованными индексами
    local tf, ac, ns = f:getParent(), {}, {};
local function deleteDuplicates(args)
    for k,v in pairs(tf.args) do
local res = {}
        local b,n = string.match(k,"^(.-)%s*(%d*)$");
for key, value in pairs(args) do
        n = tonumber(n);
if args[key+1] ~= value then
        if n then
table.insert(res, value)
            if f.args[b] then
end
                if not ac[n] then
end
                    ac[n] = mw.clone(f.args)
return res
                    setmetatable( ac[n], nil ) -- metatable ломает expandTemplate
                    table.insert(ns,n)
                end
                ac[n][b] = v
            end--if f.args[b]
        end--if n
    end--for
    table.sort(ns);
    local tmod = #f.args-1
    for n,i in ipairs(ns) do
        ns[n]=tf:expandTemplate{ title=f.args[n % tmod+1]; args=ac[i] }
    end
    return table.concat(ns)
end
end


function M.call(f)-- Просто вызывает шаблон с аргументами вызывающего.
-- Вызывает внутренний шаблон несколько раз, передавая в него блоки аргументов объемлющего шаблона
    return f:getParent():expandTemplate{ title=f.args[1]; args=f:getParent().args }
function p.npc(frame)
local args = frame:getParent().args
local templateFrame = frame:getParent()
local template = frame.args[1]
-- определение, блоки аргументов с какими номерами нужны:
-- если в объемлющем шаблоне есть "параметр12" и в вызове модуля есть "параметр", то вызывается 12-й блок
local nums = {}
for key, _ in pairs(args) do
local main, num = string.match(key, '^(.-)%s*(%d*)$')
num = tonumber(num)
-- учитывать "параметр12", только если задано "параметр"
if num and frame.args[main] then
table.insert(nums, num)
end
end
table.sort(nums)
nums = deleteDuplicates(nums)
-- проходить по нужным номерам блоков по возрастанию и однократно
-- подставлять в шаблон:
-- 1. общие аргументы данного модуля
-- 2. аргументы объемлющего шаблона вида "параметр12" как "параметр" в 12-й блок
local results = {}
for _, blockNum in ipairs(nums) do
-- общие аргументы модуля, которые передаются в каждый блок
local blockArgs = mw.clone(frame.args)
-- metatable ломает expandTemplate
setmetatable(blockArgs, nil)
for key, value in pairs(args) do
local main, num = string.match(key, '^(.-)%s*(%d*)$')
num = tonumber(num)
-- передавать "параметр12" как "параметр" в 12-й блок, только если есть "параметр" в вызове модуля
if blockNum == num and frame.args[main] then
blockArgs[main] = value
end
end
local blockText = templateFrame:expandTemplate{ title=template; args=blockArgs }
table.insert(results, blockText)
end
    return table.concat(results)
end
end


function M.join(f)-- Версия forall с разделителем вместо шаблона.
-- Действует аналогично forall по числовой переменной, изменяющейся (по умолчанию, от 1) до f.args[2].
    -- f.args[1] — разделитель.
function p.cycle(f)
     local t, tf, i = {}, f:getParent(), tonumber(f.args.from) or 1
     local tf,ac,op=f:getParent(), {}, f.args.output or 'inline';
     local k,j,m = tonumber(f.args.to),i,f.args[3]
     local sep='';
     while k and i<=k or tf.args[i] do
     if op == 'newline' then
    if (
    sep='\n';
    ({
    ['_']=function(s)return s~=''end;
    ['s']=function(s)return not tostring(s):match("^%s*$")end
    })[m] or function() return true end
    )(tf.args[i]) then
    t[j]=tf.args[i];
    j=j+1
    end;
    i=i+1
     end
     end
    return mw.text.listToText(t,f.args[1],f.args[2] or f.args[1])
end
-------------------------------------------------------
-- Функции для работы с параметрами шаблона в invoke --
-------------------------------------------------------
--[[
function M.split(f)-- Разрезает строку f.args[3]
-- указанным в f.args[2](?) разделителем
    -- и передаёт куски шаблону f.args[1].
    local tf, ac, oldi, i, e =f:getParent(), {}, 1, f.args[3]:find(f.args[1],1,true)
    -- «f.args[1]» в строке выше — точно не ошибка? --Incnis Mrsi
    while i do
        table.insert( ac,  f.args[3]:sub(oldi, i-1) ); oldi=e+1
    end
    table.insert( ac, f.args[3]:sub(oldi, #f.args[3]-1) )
    return f:getParent():expandTemplate{ title=f.args[1]; args=ac }
end
]]
function M.cycle(f)-- Действует аналогично forall по числовой переменной,
    -- изменяющейся (по умолчанию, от 1) до f.args[2].
    local tf,ac=f:getParent(),{};
     for p,k in pairs(f.args) do
     for p,k in pairs(f.args) do
         if type(p)=='number' then
         if type(p)=='number' then
Строка 127: Строка 126:
         f.args[2]:match('%.%.%s*(%S.*)%s*$') or f.args[2] or '';
         f.args[2]:match('%.%.%s*(%S.*)%s*$') or f.args[2] or '';
     fh=tonumber(fh) or fh:match('^%s*(.-)%s*$');
     fh=tonumber(fh) or fh:match('^%s*(.-)%s*$');
s=tonumber(s);
     local acr={};
     local acr={};
     if not tonumber(s) then error('Начало цикла «'..s..'» — не число') end
     if not s then error('Начало цикла «'..s..'» — не число') end
     local function dc()
     local function dc(order)
            local r=tf:expandTemplate{ title=f.args[1]; args={s,unpack(ac)} }
        local r=tf:expandTemplate{ title=f.args[1]; args={s,unpack(ac)} }
            s=s+1;
        if order == 'desc' then
            if r~='' then table.insert(acr,r); return r end
        s=s-1;
        else
        s=s+1;
    end
        if r~='' then table.insert(acr,r); return r end
     end
     end
     if type(fh)=='number' then  
     if type(fh)=='number' then
        while s<fh do dc() end
    if fh > s then
        while s<=fh do dc('asc') end
    else
        while s>=fh do dc('desc') end
    end
     elseif fh~='' then
     elseif fh~='' then
         while tf:expandTemplate{ title=fh; args={s,unpack(ac)} } do dc() end
         while tf:expandTemplate{ title=fh; args={s,unpack(ac)} } do dc('asc') end
     else
     else
         while dc() do end
         while dc('asc') do end
     end
     end
     return table.concat(acr)
     return table.concat(acr, sep)
end
end
--[[Функция не пашет как можно ждать — пробелы отсекаются после передачи параметров в expandTemplate
 
function M.pass(f)-- Передаёт шаблону параметры без подрезки.
return p
    local ac,i={},1;
    while f.args[2*i] do ac[ tonumber(f.args[2*i]) or f.args[2*i] ] = f.args[2*i+1]; i=i+1 end;
    return  f:getParent():expandTemplate{ title=f.args[1]; args=ac }
end
]]
return M

Текущая версия от 17:11, 31 января 2022

Этот модуль содержит функции, позволяющие вызывать внутренний шаблон из объемлющего шаблона после некоторой обработки параметров. Под объемлющим шаблоном имеется в виду шаблон, в котором написано {{#invoke:Transclude|..., например, {{Пиктограммы СГС}}. В вызове модуля указывается название функции и внутренний шаблон, вроде {{Пиктограммы СГС/основа}}, который собственно и обрабатывает аргументы, полученные от объемлющего шаблона.

Функция Код объемлющего шаблона Код его вызова Результат
call {{#invoke:Transclude|call|внутренний шаблон}}<br>дополнение {{объемлющий шаблон|1|2|3}} {{внутренний шаблон|1|2|3}}<br>дополнение
forall {{#invoke:Transclude|forall|внутренний шаблон}} {{объемлющий шаблон|1|2|3}} {{внутренний шаблон|1}}
{{внутренний шаблон|2}}
{{внутренний шаблон|3}}
join {{#invoke:Transclude|join|<nowiki>, </nowiki>|<nowiki> и </nowiki>}} {{объемлющий шаблон|1|2|3}} 1, 2 и 3
npc {{#invoke:Transclude|npc|внутренний шаблон|вверху=|внизу=}}
{{объемлющий шаблон
|вверху1 = 11
|внизу1  = 12

|вверху2 = 21
|внизу2  = 22
}}
{{внутренний шаблон
|вверху = 11
|внизу  = 12
}}{{внутренний шаблон
|вверху = 21
|внизу  = 22
}}
cycle TODO TODO TODO

Методы[править код]

call[править код]

{{#invoke:Transclude|call|внутренний шаблон}}

Вызывает внутренний шаблон с аргументами объемлющего шаблона (того, в коде которого указан #invoke). Нужно для того, чтобы дописать к выводу внутреннего шаблона что-то новое. Передаются как нумерованные, так и именованные аргументы.

Например, объемлющий шаблон с кодом

{{#invoke:Transclude|call|внутренний шаблон}}<br>дополнение,

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

{{внутренний шаблон|1|2|3}}<br>дополнение.

Пример использования: Шаблон:TRef.

forall[править код]

{{#invoke:Transclude|forall|внутренний шаблон}}
{{#invoke:Transclude|forall|внутренний шаблон|separator=разделитель|conjunction=последний разделитель}}

Вызывает внутренний шаблон, по одному передавая ему нумерованные параметры объемлющего шаблона (того, в коде которого указан #invoke). Параметры могут быть набраны в произвольном порядке и с пропусками, они будут отсортированы. Пустые параметры не пробрасываются.

Например, объемлющий шаблон с кодом

{{#invoke:Transclude|forall|внутренний шаблон}},

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

{{внутренний шаблон|1}}{{внутренний шаблон|2}}{{внутренний шаблон|3}}.

Можно передать разделитель и последний разделитель через параметры separator и conjunction, по умолчанию они пусты. Если указан только separator, то conjunction заполняется из него. Например, объемлющий шаблон с кодом

{{#invoke:Transclude|forall|внутренний шаблон|separator=<nowiki>, </nowiki>|conjunction=<nowiki> и </nowiki>}},

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

{{внутренний шаблон|1}}, {{внутренний шаблон|2}} и {{внутренний шаблон|3}},

а объемлющий шаблон с кодом

{{#invoke:Transclude|forall|внутренний шаблон|separator=<nowiki>, </nowiki>}},

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

{{внутренний шаблон|1}}, {{внутренний шаблон|2}}, {{внутренний шаблон|3}}.

Если требуется, чтобы разделитель содержал пробел в начале или в конце, нужно заключить его в <nowiki></nowiki>.

Примеры использования:

join[править код]

{{#invoke:Transclude|join|разделитель|последний разделитель}}

Берёт нумерованные аргументы объемлющего шаблона (того, в коде которого указан #invoke) и склеивает их в единую строку. Пустые аргументы пропускаются. Используются разделитель и последний разделитель, указанные первым и вторым параметрами в вызове модуля. Если указан только первый параметр, второй заполняется из него.

Например, объемлющий шаблон с кодом

{{#invoke:Transclude|join|<nowiki>, </nowiki>|<nowiki> и </nowiki>}},

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

1, 2 и 3,

а объемлющий шаблон с кодом

{{#invoke:Transclude|join|<nowiki>, </nowiki>}},

вызванный таким образом:

{{объемлющий шаблон|1|2|3}},

вернёт

1, 2, 3.

Если требуется, чтобы разделитель содержал пробел в начале или в конце, нужно заключить его в <nowiki></nowiki>.

Фактически аналогичен использованию #forall с внутренним шаблоном с кодом 1 и другим способом указания разделителей.

Пример использования: Шаблон:Luaman и Шаблон:Luaman/тесты (TODO: заменить на более внятный пример).

См. также: Модуль:Separated entries.

npc[править код]

{{#invoke:Transclude|npc|внутренний шаблон|параметр=|другой параметр=|…}}
{{#invoke:Transclude|npc|внутренний шаблон|общий параметр=значение|другой общий параметр=значение|…|параметр=|другой параметр=|…}}

Вызывает внутренний шаблон несколько раз, передавая в него блоки аргументов объемлющего шаблона (того, в коде которого указан #invoke). Параметр название12 передаётся как название в 12-й вызов шаблона. Для этого нужно указать в вызове модуля |название=.

Например, объемлющий шаблон с кодом

{{#invoke:Transclude|npc|внутренний шаблон|вверху=|внизу=}},

вызванный таким образом:

{{объемлющий шаблон
|вверху1 = 11
|внизу1  = 12

|вверху2 = 21
|внизу2  = 22

|вверху3 = 31
|внизу3  = 32
}}

возращает

{{внутренний шаблон
|вверху = 11
|внизу  = 12
}}{{внутренний шаблон
|вверху = 21
|внизу  = 22
}}{{внутренний шаблон
|вверху = 31
|внизу  = 32
}}

Можно также указать в вызове модуля общие аргументы, который будут передаваться в каждый вызов внутреннего шаблона. Например, объемлющий шаблон с кодом

{{#invoke:Transclude|npc|внутренний шаблон|общий={{{общий|}}}|вверху=|внизу=}},

вызванный таким образом:

{{объемлющий шаблон
|общий   = 0

|вверху1 = 11
|внизу1  = 12

|вверху2 = 21
|внизу2  = 22

|вверху3 = 31
|внизу3  = 32
}}

возращает

{{внутренний шаблон
|общий  = 0

|вверху = 11
|внизу  = 12
}}{{внутренний шаблон
|общий  = 0

|вверху = 21
|внизу  = 22
}}{{внутренний шаблон
|общий  = 0

|вверху = 31
|внизу  = 32
}}

Обратите внимание, что необходимо ставить вертикальную черту сразу после значения общего параметра, потому что вызовы модулей, в отличие от вызовов шаблонов, не обрезают пробелы:

{{#invoke:Transclude|npc|внутренний шаблон|общий={{{общий|}}}|вверху=|внизу=}}

или

{{#invoke:Transclude|npc|внутренний шаблон|
общий={{{общий|}}}|
вверху=|
внизу=}}

но не

{{#invoke:Transclude|npc|внутренний шаблон
|общий={{{общий|}}}
|вверху=
|внизу=}}

Замечания:

  • Общие параметры также учитываются вместе с остальными при выборе того, что передавать во внутренний шаблон: если в объемлющем шаблоне указан параметр общий12, он передастся в 12-й вызов внутреннего шаблона под названием общий и может заменить общий параметр.
  • Происходят вызовы внутреннего шаблона не для всех нумерованных блоков параметров, а только для непустых, так что могут быть пропуски. А именно, вызывается блок 12, если в объемлющем шаблоне есть параметр параметр12 и в вызове модуля есть параметр параметр. Рекомендуется, чтобы внутренний шаблон при вызове без параметров возвращал пустую строку.

Пример использования: Шаблон:Карточка вызывает Шаблон:Карточка/внизу.

cycle[править код]

{{#invoke:Transclude|cycle|имя шаблоан|n|список параметров…}}

Действует аналогично #forall по числовой переменной, изменяющейся от 1 до n. Другое начало можно задать в виде «-10..10» вместо n, или в обратном порядке «10..-10». Также можно задать предел цикла в виде имени шаблона (если второго параметра нет или он имеет вид «1..», шаблон раскрывается, пока не пуст). Не увлекайтесь!

Замечание: номера нумерованных параметров шаблона при записи увеличить на 1.

Параметром output можно задать как выводить шаблоны.

  • newline — каждый шаблон на новой строке.
  • inline — все шаблоны в одну строку (по-умолчанию).

Тесты шаблона[править код]

См. Модуль:Transclude/tests и Шаблон:Test transclude.

См. также[править код]


local p={}

-- Вызывает внутренний шаблон с аргументами объемлющего шаблона
function p.call(frame)
	local template = frame.args[1]
	local args = frame:getParent().args
    return frame:expandTemplate{ title=template, args=args }
end

-- Общая реализация для forall и call
local function forallImpl(args, separator, conjunction, func)
	-- нумерованные ключи из args
	local keys = {}
	-- перебор в произвольном порядке, даже для нумерованных ключей
	for key, value in pairs(args) do
		if type(key) == 'number' and value and value ~= '' then
			table.insert(keys, key)
		end
	end
	table.sort(keys)
	
	local results = {}
	for _, key in ipairs(keys) do
		local value = func(args[key])
		table.insert(results, value)
	end
	
	return mw.text.listToText(results, separator, conjunction)
end

-- Вызывает внутренний шаблон, передавая ему нумерованные параметры объемлющего шаблона по-одному
function p.forall(frame)
	local template = frame.args[1]
	local separator = frame.args.separator or ''
	local conjunction = frame.args.conjunction or separator
	local args = frame:getParent().args
	local func = function(value)
		return frame:expandTemplate{ title = template, args = {value} } -- или другой frame?
	end
	return forallImpl(args, separator, conjunction, func)
end

-- Берёт нумерованные аргументы объемлющего шаблона и склеивает их в единую строку
function p.join(frame)
	local separator = frame.args[1] or ''
	local conjunction = frame.args[2] or separator
	local args = frame:getParent().args
	local func = function(value)
		return value
	end
	return forallImpl(args, separator, conjunction, func)
end

-- Служебная функция: удаляет дубликаты из отсортированного массива с нумерованными индексами
local function deleteDuplicates(args)
	local res = {}
	for key, value in pairs(args) do
		if args[key+1] ~= value then
			table.insert(res, value)
		end
	end
	return res
end

-- Вызывает внутренний шаблон несколько раз, передавая в него блоки аргументов объемлющего шаблона
function p.npc(frame)
	local args = frame:getParent().args
	local templateFrame = frame:getParent()
	local template = frame.args[1]
	
	-- определение, блоки аргументов с какими номерами нужны:
	-- если в объемлющем шаблоне есть "параметр12" и в вызове модуля есть "параметр", то вызывается 12-й блок
	local nums = {}
	for key, _ in pairs(args) do
		local main, num = string.match(key, '^(.-)%s*(%d*)$')
		num = tonumber(num)
		-- учитывать "параметр12", только если задано "параметр"
		if num and frame.args[main] then
			table.insert(nums, num)
		end
	end
	table.sort(nums)
	nums = deleteDuplicates(nums)
	
	-- проходить по нужным номерам блоков по возрастанию и однократно
	-- подставлять в шаблон:
	-- 1. общие аргументы данного модуля
	-- 2. аргументы объемлющего шаблона вида "параметр12" как "параметр" в 12-й блок
	local results = {}
	for _, blockNum in ipairs(nums) do
		-- общие аргументы модуля, которые передаются в каждый блок
		local blockArgs = mw.clone(frame.args)
		 -- metatable ломает expandTemplate
		setmetatable(blockArgs, nil)
		
		for key, value in pairs(args) do
			local main, num = string.match(key, '^(.-)%s*(%d*)$')
			num = tonumber(num)
			-- передавать "параметр12" как "параметр" в 12-й блок, только если есть "параметр" в вызове модуля
			if blockNum == num and frame.args[main] then
				blockArgs[main] = value
			end
		end
		
		local blockText = templateFrame:expandTemplate{ title=template; args=blockArgs }
		table.insert(results, blockText)
	end
	
    return table.concat(results)
end

-- Действует аналогично forall по числовой переменной, изменяющейся (по умолчанию, от 1) до f.args[2].
function p.cycle(f)
    local tf,ac,op=f:getParent(), {}, f.args.output or 'inline';
    local sep='';
    if op == 'newline' then
    	sep='\n';
    end
    for p,k in pairs(f.args) do
        if type(p)=='number' then
            if p>2 then ac[p-1]=k end
        else ac[p]=k
        end
    end
    local s,fh = f.args[2]:match('^%s*(%-?%d+)%s*%.%.') or 1,
        f.args[2]:match('%.%.%s*(%S.*)%s*$') or f.args[2] or '';
    fh=tonumber(fh) or fh:match('^%s*(.-)%s*$');
	s=tonumber(s);
    local acr={};
    if not s then error('Начало цикла «'..s..'» — не число') end
    local function dc(order)
        local r=tf:expandTemplate{ title=f.args[1]; args={s,unpack(ac)} }
        if order == 'desc' then
        	s=s-1;
        else
        	s=s+1;
    	end
        if r~='' then table.insert(acr,r); return r end
    end
    if type(fh)=='number' then
    	if fh > s then
        	while s<=fh do dc('asc') end
    	else
        	while s>=fh do dc('desc') end
    	end
    elseif fh~='' then
        while tf:expandTemplate{ title=fh; args={s,unpack(ac)} } do dc('asc') end
    else
        while dc('asc') do end
    end
    return table.concat(acr, sep)
end

return p