Module:template utilities
Appearance
- The following documentation is located at Module:template utilities/documentation. [edit]
- Useful links: subpage list • links • transclusions • testcases • sandbox
This module provides functions for finding and parsing template invocations found in wikitext, though it can also be used for other purposes.
find_bracket(str, brackets, p_start)
- Finds a substring with balanced brackets. This means that, if one reads the string from left to right, counting +1 for a left bracket and -1 for a right bracket, the ending right bracket is the first right bracket where the count reaches 0. This function is similar to Lua search pattern
'%b'
, but accepts multi-character tokens as brackets.str
: the string to be searchedbrackets
: the left and right brackets to be searched for, given as a hash table with the left brackets being keys and the right being values. They are interpreted as Lua patterns:
{
['{{'] = '}}',
['%[%['] = ']]',
}
p_start
: where instr
to start the search- Return value: If it finds a match, returns indices of where this bracket starts and ends, and the whole bracket substring (including the brackets); otherwise returns
nil
.
gfind_bracket(str, brackets)
- Iterates through all top-level
brackets
found instr
.- The parameters are the same as in
find_bracket()
. - Return value: Returns an iterator function. It yields indices of where this bracket starts and ends, and the whole bracket substring.
- The parameters are the same as in
find_ignoring_brackets(str, brackets, pat, init, ...)
- Searches
str
usingstring.find()
, but ignore all text insidebrackets
.pat, init, ...
: the same parameters forstring.find()
- Other parameters are the same as in
find_bracket()
. - Return value: Returns the result of
string.find()
.
gsplit_ignoring_brackets
- Splits
str
into substrings at boundaries that match the patternsep
and are not in anybrackets
, and iterates through them.sep
: the pattern matching the boundaries. If it matches the empty string, s will be split into individual characters.- Other parameters are the same as in
find_bracket()
. - Return value: Returns an iterator function. It yields each section substring.
parse_temp(str)
- Parses
str
as a template invocation and returns the result table.str
: the string of a template invocation- Return value: If success, returns a table containing the template name as a string
['title']
and the parameters as a table['args']
; otherwise, returnsnil
.
iter_num(t)
- Iterates through all extant numbered parameters of a template. Named parameters are ignored. Nonexistent numbered parameters are skipped.
t
: a table containing the template name as a string['title']
and the parameters as a table['args']
- Return value: Returns an iterator function. It yields the index and values of all number-indexed non-nil content of the table in order.
glue_temp(t)
- Serializes a template information table to template invocation wikitext. The inverse operation of
parse_temp(str)
.t
: the same as initer_num(t)
- Return value: Returns a string of template invocation wikitext. Raises errors when it fails.
local export = {}
export.brackets_temp = { ['{{'] = '}}' }
export.brackets_temp_and_link = { ['{{'] = '}}', ['%[%['] = ']]' }
function export.find_bracket(str, brackets, p_start)
local function find_left(pos_start)
local p1_result, cap_result, right_result = str:len() + 1
local cap_this
for k, v in pairs(brackets) do
cap_this = {str:find(k, pos_start)}
if cap_this[1] and cap_this[1] < p1_result then
p1_result, cap_result, right_result = cap_this[1], cap_this, v
end
end
if not cap_result then return nil end
local p2_result = cap_result[2]
cap_result[2] = str:sub(p1_result, p2_result)
local t = type(right_result)
if t == 'string' then
return p1_result, p2_result, right_result:gsub('%%(.)', function(m1)
if m1:match'%d' then
return cap_result[m1 + 2]
else return m1 end
end)
end
local repl
if t == 'function' then
cap_result[3] = cap_result[3] or cap_result[2]
repl = right_result(select(3, unpack(cap_result)))
elseif t == 'table' then
repl = right_result[cap_result[3] or cap_result[2]]
else error('bad right bracket type: ' .. t) end
if not repl then repl = cap_result[2]
elseif type(repl) == 'number' then repl = tostring(repl) end
return p1_result, p2_result, repl
end
local p_init, p0, str_brac = find_left(p_start)
if p_init == nil then return nil end
local nest = {str_brac}
local p1, p2, p3, p4
repeat
p0 = p0 + 1
p1, p2 = str:find(str_brac, p0)
if p1 == nil then return nil end
if p1 > p2 then error'Any bracket must not have zero length.' end
p3, p4, str_brac = find_left(p0)
if p3 == nil then
local n = #nest - 1
while n > 0 do
p1, p2 = str:find(nest[n], p2 + 1)
if p1 == nil then return nil end
n = n - 1
end
p0 = p2
break
else
if p3 > p4 then error'Any bracket must not have zero length.' end
if p3 < p1 then
table.insert(nest, str_brac)
p0 = p4
else
table.remove(nest)
str_brac = nest[#nest]
p0 = p2
end
end
until #nest == 0
return p_init, p0, str:sub(p_init, p0)
end
function export.gfind_bracket(str, brackets)
local p0 = 0
return function()
p0 = p0 + 1
local p1, p2, text_b = export.find_bracket(str, brackets, p0)
p0 = p2
return p1, p2, text_b
end
end
function export.find_ignoring_brackets(str, brackets, pat, init, ...)
local find_result = {str:find(pat, init, ...)}
local p1, p2 = find_result[1], find_result[2]
if p1 == nil then return nil end
local p3, p4 = export.find_bracket(str, brackets)
while p4 and p4 <= p2 do p3, p4 = export.find_bracket(str, brackets, p4 + 1) end
while p3 and p3 <= p2 do
find_result = {str:find(pat, p4 + 1, ...)}
p1, p2 = find_result[1], find_result[2]
if p1 == nil then return nil end
while p4 and p4 <= p2 do p3, p4 = export.find_bracket(str, brackets, p4 + 1) end
end
return unpack(find_result)
end
function export.gsplit_ignoring_brackets(str, brackets, sep)
local p0 = 0
local empty = 0
return function()
if p0 == nil then return nil end
p0 = p0 + empty
if p0 > str:len() then return nil end
p0 = p0 + 1
local p1, p2 = export.find_ignoring_brackets(str, brackets, sep, p0)
p0, p2 = p2, p0 - empty
if p1 then
empty = p1 > p0 and 1 or 0
return str:sub(p2, p1 - 1)
else return str:sub(p2) end
end
end
function export.parse_temp(str)
if str:sub(1, 2) ~= '{{' or str:sub(-2) ~= '}}' then return nil end
str = str:sub(3, -3)
local p_title_end = export.find_ignoring_brackets(str, export.brackets_temp_and_link, '|')
if not p_title_end then return { title = str, args = {} }
end
local args = {}
local count = 0
for arg in export.gsplit_ignoring_brackets(str:sub(p_title_end + 1), export.brackets_temp_and_link, '|') do
local p_eqsign = export.find_ignoring_brackets(arg, export.brackets_temp_and_link, '=')
if p_eqsign then
local arg_name = arg:sub(1, p_eqsign - 1)
local arg_name_num = tonumber(arg_name)
if arg_name_num and arg_name_num > 0 and arg_name_num == math.floor(arg_name_num) then
arg_name = arg_name_num
else
arg_name = arg_name:match'^%s*(%S-)%s*$'
end
args[arg_name] = arg:sub(p_eqsign + 1):match'^%s*(%S-)%s*$'
else
count = count + 1
args[count] = arg
end
end
return { title = str:sub(1, p_title_end - 1), args = args }
end
function export.iter_num(t)
local i = 0
local index_max = 0
for k, _ in pairs(t.args) do
if type(k) == 'number' and index_max < k then
index_max = k
end
end
return function()
local v
repeat
i = i + 1
if i > index_max then return nil end
v = t.args[i]
until v
return i, v
end
end
function export.glue_temp(t)
local content = { t.title }
for i, v in export.iter_num(t) do
if i == #content then
table.insert(content, v)
else
table.insert(content, i .. '=' .. v)
end
end
for k, v in pairs(t.args) do
if type(k) == 'string' then
table.insert(content, k .. '=' .. v)
end
end
return '{{' .. table.concat(content, '|') .. '}}'
end
return export