模組:templateparser
Guā-māu
可在模組:templateparser/doc建立此模組的說明文件
local export = {}
-- local function strip_whitespace(text)
-- local text, _ = mw.ustring.gsub(text, "%s*(.*)%s*", "%1")
-- return text
-- end
local strip_whitespace = mw.text.trim
local parseTemplateSearchPattern = "(([{|}%[%]=<]).?)"
local parseTemplateSearchPatternNoEq = "(([{|}%[%]<]).?)"
local function invalidSyntax()
error("Invalid syntax detected!")
end
function export.parseTemplate(text)
local text, ok = text:gsub("^{{(.+)}}$", "%1")
if ok == 0 then
return nil
end
local prev = 1
local pos = 1, mend, found, f1
local in_template = 0
local in_table = 0
local in_link = 0
local search = parseTemplateSearchPattern
local has_key = false
local eq_index = 1
local name = nil
local args = {}
local next_i = 1
local function add_param(x, has_key, eq)
if name == nil then
name = strip_whitespace(x)
return
end
if has_key then
local key = strip_whitespace(x:sub(1, eq))
local value = strip_whitespace(x:sub(eq + 2))
local num = tonumber(key, 10)
if num ~= nil and num > 0 then
key = num
end
args[key] = value
else
args[next_i] = x
next_i = next_i + 1
end
end
while true do
pos, mend, found, f1 = text:find(search, pos)
if pos == nil then break end
if found == "{{" then
-- start of subtemplate
in_template = in_template + 1
pos = pos + 2
elseif found == "{|" then
-- start of a table
in_table = in_table + 1
pos = pos + 2
elseif found == "[[" then
-- start of link
in_link = in_link + 1
pos = pos + 2
elseif found == "<n" and text:sub(pos, pos + 7) == "<nowiki>" then
pos = text:find("</nowiki>", pos)
if pos == nil then return nil end
pos = pos + 8
elseif found == "<-" and text:sub(pos, pos + 3) == "<!--" then
pos = text:find("-->", pos + 4)
if pos == nil then return nil end
pos = pos + 3
--elseif found == "}}" and in_template == 0 then
-- invalidSyntax()
elseif in_template == 0 and in_table == 0 and in_link == 0 then
if f1 == "|" then
-- parameter separator
add_param(text:sub(prev, pos - 1), has_key, eq_index - prev)
has_key = false
search = parseTemplateSearchPattern -- allow equals sign again
prev = pos + 1
elseif f1 == "=" and not has_key then
-- parameter key/value separator
eq_index = pos
has_key = true
search = parseTemplateSearchPatternNoEq -- do not allow further equals signs
end
pos = pos + 1
elseif found == "}}" then
-- end of subtemplate
if in_template > 0 then in_template = in_template - 1 end
pos = pos + 2
elseif found == "|}" then
-- end of table
if in_table > 0 then
in_table = in_table - 1
pos = pos + 2
-- end of subtemplate with final pipe
elseif text:sub(pos + 2, pos + 2) == "}" and in_template > 0 then
in_template = in_template - 1
pos = pos + 3
end
elseif found == "]]" then
-- end of link
if in_link > 0 then in_link = in_link - 1 end
pos = pos + 2
else
pos = pos + 1
end
end
if in_template ~= 0 or in_table ~= 0 or in_link ~= 0 then
invalidSyntax()
end
add_param(text:sub(prev), has_key, eq_index - prev)
return name, args
end
function export.findTemplates(text)
-- Remove HTML comments.
text = text
:gsub("<!%-%-.-%-%->", "")
:gsub("<!%-%-.*", "")
local next = 1
local function findNextTemplate()
local pos, mend, found
local in_template, in_argument = 0, 0
local temp_start = 1
pos = next
while true do
pos, mend, found = text:find("([{}<][{}n!])", pos)
if pos == nil then break end
if found == "{{" then
if text:sub(pos, pos + 2) == "{{{" then
pos = pos + 3
in_argument = in_argument + 1
else
if in_template == 0 then
temp_start = pos
end
pos = pos + 2
in_template = in_template + 1
end
elseif found == "}}" then
if text:sub(pos, pos + 2) == "}}}" and in_argument > 0 then
in_argument = in_argument - 1
pos = pos + 3
else
if in_template > 0 then
in_template = in_template - 1
if in_template == 0 then
next = pos + 2
local src = text:sub(temp_start, pos + 1)
local ok, name, args = pcall(export.parseTemplate, src)
if ok and name ~= nil then
local modifier, type, func
for prefix in name:gmatch("%s*(.-):") do
if prefix == "subst" or prefix == "safesubst" then
view = prefix
name = name:gsub(prefix .. ":", "", 1)
elseif prefix:match("^#") then
if prefix == "#invoke" then
type = "module"
func = args[1]
for i = 2, #args do
args[i-1] = args[i]
end
args[#args] = nil
else
type = "parser function"
end
name = name:gsub(prefix .. ":", "", 1)
end
end
name = name:gsub("^%s*(.*)%s*$", "%1")
view = view or "transclude"
type = type or "template"
return name, args, src, temp_start, view, type, func
end
end
end
pos = pos + 2
end
elseif found == "<n" and text:sub(pos, pos + 7) == "<nowiki>" then
pos = text:find("</nowiki>", pos)
if pos == nil then break end
pos = pos + 8
elseif found == "<!" and text:sub(pos, pos + 3) == "<!--" then
pos = text:find("-->", pos + 4)
if pos == nil then break end
pos = pos + 3
else
pos = pos + 1
end
end
end
return findNextTemplate
end
return export