Module:template parser: Difference between revisions
Jump to navigation
Jump to search
Incorporate Module:title/makeTitle + some changes to Module:title/newTitle, and replace is_internal_title() with is_title() + a check for the isExternal key.
No edit summary |
(Incorporate Module:title/makeTitle + some changes to Module:title/newTitle, and replace is_internal_title() with is_title() + a check for the isExternal key.) |
||
| Line 14: | Line 14: | ||
local parser_extension_tags_data_module = "Module:data/parser extension tags" | local parser_extension_tags_data_module = "Module:data/parser extension tags" | ||
local parser_module = "Module:parser" | local parser_module = "Module:parser" | ||
local scribunto_module = "Module:Scribunto" | |||
local string_pattern_escape_module = "Module:string/patternEscape" | |||
local string_replacement_escape_module = "Module:string/replacementEscape" | |||
local string_utilities_module = "Module:string utilities" | local string_utilities_module = "Module:string utilities" | ||
local | local table_length_module = "Module:table/length" | ||
local table_shallow_copy_module = "Module:table/shallowCopy" | |||
local table_sorted_pairs_module = "Module:table/sortedPairs" | |||
local title_is_title_module = "Module:title/isTitle" | |||
local title_make_title_module = "Module:title/makeTitle" | |||
local title_new_title_module = "Module:title/newTitle" | |||
local title_redirect_target_module = "Module:title/redirectTarget" | |||
local require = require | local require = require | ||
| Line 38: | Line 47: | ||
local is_node = m_parser.is_node | local is_node = m_parser.is_node | ||
local lower = string.lower | local lower = string.lower | ||
local match = string.match | local match = string.match | ||
local next = next | local next = next | ||
local pairs = pairs | local pairs = pairs | ||
| Line 47: | Line 54: | ||
local pcall = pcall | local pcall = pcall | ||
local rep = string.rep | local rep = string.rep | ||
local select = select | local select = select | ||
local sub = string.sub | local sub = string.sub | ||
| Line 57: | Line 63: | ||
--[==[ | --[==[ | ||
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] | Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==] | ||
local function decode_entities(...) | |||
decode_entities = require(string_utilities_module).decode_entities | |||
return decode_entities(...) | |||
end | |||
local function encode_entities(...) | |||
encode_entities = require(string_utilities_module).encode_entities | |||
return encode_entities(...) | |||
end | |||
local function get_link_target(...) | |||
get_link_target = require(pages_module).get_link_target | |||
return get_link_target(...) | |||
end | |||
local function is_title(...) | |||
is_title = require(title_is_title_module) | |||
return is_title(...) | |||
end | |||
local function load_data(...) | |||
load_data = require(load_module).load_data | |||
return load_data(...) | |||
end | end | ||
local function pattern_escape(...) | local function make_title(...) | ||
make_title = require(title_make_title_module) | |||
return make_title(...) | |||
end | end | ||
local function php_trim(...) | local function new_title(...) | ||
new_title = require(title_new_title_module) | |||
return new_title(...) | |||
end | |||
local function replacement_escape(...) | local function pattern_escape(...) | ||
pattern_escape = require(string_pattern_escape_module) | |||
return pattern_escape(...) | |||
end | |||
local function php_htmlspecialchars(...) | |||
php_htmlspecialchars = require(scribunto_module).php_htmlspecialchars | |||
return php_htmlspecialchars(...) | |||
end | |||
local function php_ltrim(...) | |||
php_ltrim = require(scribunto_module).php_ltrim | |||
return php_ltrim(...) | |||
end | |||
local function php_trim(...) | |||
php_trim = require(scribunto_module).php_trim | |||
return php_trim(...) | |||
end | |||
local function redirect_target(...) | |||
redirect_target = require(title_redirect_target_module) | |||
return redirect_target(...) | |||
end | |||
local function replacement_escape(...) | |||
replacement_escape = require(string_replacement_escape_module) | |||
return replacement_escape(...) | |||
end | |||
local function scribunto_parameter_key(...) | |||
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key | |||
return scribunto_parameter_key(...) | |||
end | |||
local function shallow_copy(...) | |||
shallow_copy = require(table_shallow_copy_module) | |||
return shallow_copy(...) | |||
end | |||
local function sorted_pairs(...) | |||
sorted_pairs = require(table_sorted_pairs_module) | |||
return sorted_pairs(...) | |||
end | |||
local function split(...) | |||
split = require(string_utilities_module).split | |||
return split(...) | |||
end | |||
local function table_len(...) | |||
table_len = require(table_length_module) | |||
return table_len(...) | |||
end | |||
local function uupper(...) | |||
uupper = require(string_utilities_module).upper | |||
return uupper(...) | |||
end | |||
--[==[ | --[==[ | ||
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] | Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==] | ||
local data | |||
local function get_data() | |||
data, get_data = load_data(data_module), nil | |||
return data | |||
end | |||
local frame | |||
local function get_frame() | |||
frame, get_frame = mw.getCurrentFrame(), nil | |||
return frame | |||
end | |||
local magic_words | |||
local function get_magic_words() | |||
magic_words, get_magic_words = load_data(magic_words_data_module), nil | |||
return magic_words | |||
end | |||
local | local parser_extension_tags | ||
local function get_parser_extension_tags() | |||
parser_extension_tags, get_parser_extension_tags = load_data(parser_extension_tags_data_module), nil | |||
return parser_extension_tags | |||
end | |||
------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ||
| Line 161: | Line 190: | ||
------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ||
Node. | local Node = m_parser.node() | ||
local new_node = Node.new | |||
local function expand(obj, frame_args) | local function expand(obj, frame_args) | ||
| Line 178: | Line 208: | ||
local Wikitext = Node:new_class("wikitext") | local Wikitext = Node:new_class("wikitext") | ||
-- force_node ensures the output will always be a node. | -- force_node ensures the output will always be a Wikitext node. | ||
function Wikitext:new(this, force_node) | function Wikitext:new(this, force_node) | ||
if type(this) ~= "table" then | if type(this) ~= "table" then | ||
return force_node and | return force_node and new_node(self, {this}) or this | ||
elseif #this == 1 then | elseif #this == 1 then | ||
local this1 = this[1] | local this1 = this[1] | ||
return force_node and | return force_node and class_else_type(this1) ~= "wikitext" and new_node(self, this) or this1 | ||
end | end | ||
local success, str = pcall(concat, this) | local success, str = pcall(concat, this) | ||
if success then | if success then | ||
return force_node and | return force_node and new_node(self, {str}) or str | ||
end | end | ||
return | return new_node(self, this) | ||
end | end | ||
| Line 209: | Line 239: | ||
this = {this[1], this2} | this = {this[1], this2} | ||
end | end | ||
return | return new_node(self, this) | ||
end | end | ||
| Line 221: | Line 251: | ||
function Parameter:get_name(frame_args) | function Parameter:get_name(frame_args) | ||
return | return scribunto_parameter_key(expand(self[1], frame_args)) | ||
end | end | ||
| Line 237: | Line 267: | ||
end | end | ||
local name = expand(self[1], frame_args) | local name = expand(self[1], frame_args) | ||
local val = frame_args[ | local val = frame_args[scribunto_parameter_key(name)] -- Parameter in use. | ||
if val ~= nil then | if val ~= nil then | ||
return val | return val | ||
| Line 249: | Line 279: | ||
local Argument = Node:new_class("argument") | local Argument = Node:new_class("argument") | ||
function Argument:new(this) | |||
local key = this._parse_data.key | |||
this = Wikitext:new(this) | |||
if key == nil then | |||
return this | |||
end | |||
return new_node(self, {Wikitext:new(key), this}) | |||
end | |||
function Argument:__tostring() | function Argument:__tostring() | ||
| Line 272: | Line 311: | ||
-- FIXME: Some parser functions have special argument handling (e.g. {{#SWITCH:}}). | -- FIXME: Some parser functions have special argument handling (e.g. {{#SWITCH:}}). | ||
do | do | ||
local templates, parser_variables, parser_functions = {}, {}, {} | |||
local templates = {} | |||
local function retrieve_magic_word_data(chunk) | local function retrieve_magic_word_data(chunk) | ||
| Line 290: | Line 323: | ||
return mgw_data | return mgw_data | ||
end | end | ||
end | end | ||
| Line 303: | Line 330: | ||
-- exists (e.g. "Template:PAGENAME" becomes "T:PAGENAME"). | -- exists (e.g. "Template:PAGENAME" becomes "T:PAGENAME"). | ||
local function get_template_invocation_name(title, shortcut) | local function get_template_invocation_name(title, shortcut) | ||
if not (title and | if not (is_title(title) and not title.isExternal) then | ||
error("Template invocations require a valid page title, which cannot contain an interwiki prefix.") | error("Template invocations require a valid page title, which cannot contain an interwiki prefix.") | ||
end | end | ||
| Line 310: | Line 337: | ||
-- mainspace). | -- mainspace). | ||
if namespace ~= 10 then | if namespace ~= 10 then | ||
return | return get_link_target(title, shortcut) | ||
end | end | ||
-- If in the template namespace and it shares a name with a magic word, | -- If in the template namespace and it shares a name with a magic word, | ||
-- it needs the prefix "Template:". | -- it needs the prefix "Template:". | ||
local text = title.text | local text, fragment = title.text, title.fragment | ||
if fragment and fragment ~= "" then | |||
text = text .. "#" .. fragment | |||
end | |||
local colon = find(text, ":", nil, true) | local colon = find(text, ":", nil, true) | ||
if not colon then | if not colon then | ||
local mgw_data = retrieve_magic_word_data(text) | local mgw_data = retrieve_magic_word_data(text) | ||
return mgw_data and mgw_data.parser_variable and | return mgw_data and mgw_data.parser_variable and get_link_target(title, shortcut) or text | ||
end | end | ||
local mgw_data = retrieve_magic_word_data(sub(text, 1, colon - 1)) | local mgw_data = retrieve_magic_word_data(sub(text, 1, colon - 1)) | ||
if mgw_data and (mgw_data.parser_function or mgw_data.transclusion_modifier) then | if mgw_data and (mgw_data.parser_function or mgw_data.transclusion_modifier) then | ||
return | return get_link_target(title, shortcut) | ||
end | end | ||
-- Also if "Template:" is necessary for disambiguation (e.g. | -- Also if "Template:" is necessary for disambiguation (e.g. | ||
-- "Template:Category:Foo" can't be called with "Category:Foo"). | -- "Template:Category:Foo" can't be called with "Category:Foo"). | ||
local check = new_title(text, namespace) | local check = new_title(text, namespace) | ||
return check and title_equals(title, check) and text or | return check and title_equals(title, check) and text or get_link_target(title, shortcut) | ||
end | end | ||
export.getTemplateInvocationName = get_template_invocation_name | export.getTemplateInvocationName = get_template_invocation_name | ||
function parse_template_name(name, has_args, fragment, force_transclusion) | function parse_template_name(name, has_args, fragment, force_transclusion) | ||
local chunks, colon, start, n, p = {}, find(name, ":", nil, true), 1, 0, 0 | local chunks, colon, start, n, p = {}, find(name, ":", nil, true), 1, 0, 0 | ||
while colon do | while colon do | ||
local mgw_data = retrieve_magic_word_data(php_ltrim(sub(name, start, colon - 1))) | |||
local mgw_data = retrieve_magic_word_data( | |||
if not mgw_data then | if not mgw_data then | ||
break | break | ||
| Line 375: | Line 397: | ||
end | end | ||
end | end | ||
-- | -- Get the template title with the custom new_title() function in | ||
-- [[Module:title/newTitle]], with `allowOnlyFragment` set to false | |||
-- (e.g. "{{#foo}}" is invalid) and `allowRelative` set to true, for | |||
-- relative links for namespaces with subpages (e.g. "{{/foo}}"). | |||
local title = new_title(name, 10, false, true) | |||
if not (title and not title.isExternal) then | |||
local title = new_title(name, 10) | |||
if not (title and | |||
return nil | return nil | ||
end | end | ||
-- Resolve any redirects. If the redirect target is an interwiki link, | |||
-- the template won't fail, but the redirect does not get resolved (i.e. | |||
-- the redirect page itself gets transcluded, so the template name | |||
-- should not be normalized to the target). | |||
local redirect = redirect_target(title, force_transclusion) | |||
-- Resolve any redirects. | if redirect and not redirect.isExternal then | ||
title = redirect | |||
local redirect = | |||
end | end | ||
-- If `fragment` is not true, unset it from the title object to prevent | |||
-- | -- it from being included by get_template_invocation_name. | ||
if fragment then | if not fragment then | ||
title.fragment = "" | |||
end | end | ||
chunks[n + 1] = | chunks[n + 1] = get_template_invocation_name(title) | ||
return chunks, "template" | return chunks, "template" | ||
end | end | ||
| Line 532: | Line 502: | ||
local arg = self[i] | local arg = self[i] | ||
if class_else_type(arg) == "argument" then | if class_else_type(arg) == "argument" then | ||
template_args[ | template_args[scribunto_parameter_key(expand(arg[1], frame_args))] = php_trim((expand(arg[2], frame_args))) | ||
else | else | ||
implicit = implicit + 1 | implicit = implicit + 1 | ||
| Line 540: | Line 510: | ||
return template_args | return template_args | ||
end | end | ||
-- BIG TODO: manual template expansion. | -- BIG TODO: manual template expansion. | ||
function Template:expand() | function Template:expand(frame_args) | ||
local name, subclass, pf_arg1 = process_name(self, frame_args) | |||
if name == nil then | |||
local output = {} | |||
for i = 1, #self do | |||
output[i] = expand(self[i], frame_args) | |||
end | |||
return "{{" .. concat(output, "|") .. "}}" | |||
elseif subclass == "parser variable" then | |||
return (frame or get_frame()):preprocess("{{" .. name .. "}}") | |||
elseif subclass == "parser function" then | |||
local f = frame or get_frame() | |||
if frame_args ~= nil then | |||
local success, new_f = pcall(f.newChild, f, {args = frame_args}) | |||
if success then | |||
f = new_f | |||
end | |||
end | |||
return f:preprocess(tostring(self)) | |||
end | |||
local output = {} | |||
for i = 1, #self do | |||
output[i] = expand(self[i], frame_args) | |||
end | |||
return (frame or get_frame()):preprocess("{{" .. concat(output, "|") .. "}}") | |||
end | |||
end | end | ||
local Tag = Node:new_class("tag") | local Tag = Node:new_class("tag") | ||
function Tag:__tostring() | |||
local | local open_tag, attributes, n = {"<", self.name}, self:get_attributes(), 2 | ||
for attr, value in next, attributes do | |||
n = n + 1 | |||
open_tag[n] = " " .. php_htmlspecialchars(attr) .. "=\"" .. php_htmlspecialchars(value, "compat") .. "\"" | |||
end | end | ||
if self.self_closing then | |||
return concat(open_tag) .. "/>" | |||
return ( | |||
end | end | ||
return concat(open_tag) .. ">" .. concat(self) .. "</" .. self.name .. ">" | |||
end | |||
do | |||
local valid_attribute_name | local valid_attribute_name | ||
local function get_valid_attribute_name() | local function get_valid_attribute_name() | ||
valid_attribute_name, get_valid_attribute_name = (data or get_data()).valid_attribute_name, nil | valid_attribute_name, get_valid_attribute_name = (data or get_data()).valid_attribute_name, nil | ||
| Line 638: | Line 619: | ||
local success, str = pcall(concat, this) | local success, str = pcall(concat, this) | ||
if success then | if success then | ||
return | return new_node(self, { | ||
str, | str, | ||
level = this.level, | level = this.level, | ||
| Line 646: | Line 627: | ||
end | end | ||
end | end | ||
return | return new_node(self, this) | ||
end | end | ||
function Heading:__tostring() | do | ||
local node_tostring = Node.__tostring | |||
function Heading:__tostring() | |||
local eq = rep("=", self.level) | |||
return eq .. node_tostring(self) .. eq | |||
end | |||
end | end | ||
do | do | ||
local expand_node = Node.expand | local expand_node = Node.expand | ||
-- Expanded heading names can contain "\n" (e.g. inside nowiki tags), which | -- Expanded heading names can contain "\n" (e.g. inside nowiki tags), which | ||
-- causes any heading containing them to fail. However, in such cases, the | -- causes any heading containing them to fail. However, in such cases, the | ||
| Line 692: | Line 677: | ||
------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ||
local Parser = m_parser.string_parser() | |||
-- Template or parameter. | -- Template or parameter. | ||
| Line 740: | Line 688: | ||
local handle_name | local handle_name | ||
local handle_argument | local handle_argument | ||
local handle_value | |||
local function do_template_or_parameter(self, inner_node) | local function do_template_or_parameter(self, inner_node) | ||
| Line 752: | Line 701: | ||
end | end | ||
end | end | ||
local function pipe(self) | |||
self:emit(Wikitext:new(self:pop_sublayer())) | |||
self:push_sublayer(handle_argument) | |||
self:set_pattern("[\n<=[{|}]") | |||
end | |||
local function rbrace(self, this) | |||
if self:read(1) == "}" then | |||
self:emit(Wikitext:new(self:pop_sublayer())) | |||
return self:pop() | |||
end | |||
self:emit(this) | |||
end | |||
function handle_name(self, ...) | function handle_name(self, ...) | ||
handle_name = self:switch(handle_name, { | handle_name = self:switch(handle_name, { | ||
| Line 759: | Line 722: | ||
["["] = Parser.wikilink_block, | ["["] = Parser.wikilink_block, | ||
["{"] = Parser.braces, | ["{"] = Parser.braces, | ||
["|"] = pipe, | |||
["|"] = | ["}"] = rbrace, | ||
["}"] = | |||
[""] = Parser.fail_route, | [""] = Parser.fail_route, | ||
[false] = Parser.emit | [false] = Parser.emit | ||
| Line 779: | Line 729: | ||
return handle_name(self, ...) | return handle_name(self, ...) | ||
end | end | ||
function handle_argument(self, ...) | function handle_argument(self, ...) | ||
handle_argument = self:switch(handle_argument, { | handle_argument = self:switch(handle_argument, { | ||
["\n"] = function(self) | ["\n"] = function(self, this) | ||
return self:heading_block( | return self:heading_block(this, "==") | ||
end, | end, | ||
["<"] = Parser.tag, | ["<"] = Parser.tag, | ||
["="] = function(self) | ["="] = function(self) | ||
local key = | local key = self:pop_sublayer() | ||
self:push_sublayer(handle_value) | |||
self:push_sublayer( | |||
self:set_pattern("[\n<[{|}]") | self:set_pattern("[\n<[{|}]") | ||
self.current_layer._parse_data.key = key | |||
end, | end, | ||
["["] = Parser.wikilink_block, | |||
["{"] = Parser.braces, | |||
["|"] = pipe, | |||
["}"] = rbrace, | |||
[""] = Parser.fail_route, | |||
[false] = Parser.emit | |||
}) | |||
return handle_argument(self, ...) | |||
end | |||
function handle_value(self, ...) | |||
handle_value = self:switch(handle_value, { | |||
["\n"] = Parser.heading_block, | |||
["<"] = Parser.tag, | |||
["["] = Parser.wikilink_block, | ["["] = Parser.wikilink_block, | ||
["{"] = Parser.braces, | ["{"] = Parser.braces, | ||
["|"] = function(self) | ["|"] = function(self) | ||
self:emit(Argument:new(self:pop_sublayer())) | |||
self:push_sublayer(handle_argument) | self:push_sublayer(handle_argument) | ||
self:set_pattern("[\n<=[{|}]") | self:set_pattern("[\n<=[{|}]") | ||
end, | end, | ||
["}"] = function(self) | ["}"] = function(self, this) | ||
if self:read(1) == "}" then | if self:read(1) == "}" then | ||
self:emit(Argument:new(self:pop_sublayer())) | |||
return self:pop() | return self:pop() | ||
end | end | ||
self:emit( | self:emit(this) | ||
end, | end, | ||
[""] = Parser.fail_route, | [""] = Parser.fail_route, | ||
[false] = Parser.emit | [false] = Parser.emit | ||
}) | }) | ||
return | return handle_value(self, ...) | ||
end | end | ||
| Line 888: | Line 841: | ||
local function do_tag(self) | local function do_tag(self) | ||
local layer = self | local layer = self.current_layer | ||
layer.handler, layer.index = handle_start, self.head | layer._parse_data.handler, layer.index = handle_start, self.head | ||
self:set_pattern("[%s/>]") | self:set_pattern("[%s/>]") | ||
self:advance() | self:advance() | ||
| Line 933: | Line 886: | ||
return self:fail_route() | return self:fail_route() | ||
end | end | ||
local layer = self | local layer = self.current_layer | ||
local text, head = self.text, self.head + | local pdata = layer._parse_data | ||
local text, head = self.text, self.head + pdata.step | |||
if match(text, "^/[^>]", head) then | if match(text, "^/[^>]", head) then | ||
return self:fail_route() | return self:fail_route() | ||
| Line 947: | Line 901: | ||
layer.raw_name = raw_name | layer.raw_name = raw_name | ||
end | end | ||
layer.name, | layer.name, pdata.handler, pdata.end_tag_pattern = this, handle_tag, end_tag_pattern | ||
self:set_pattern(">") | self:set_pattern(">") | ||
end | end | ||
| Line 954: | Line 908: | ||
if this == "" then | if this == "" then | ||
return self:fail_route() | return self:fail_route() | ||
end | |||
local layer = self.current_layer | |||
if this ~= ">" then | |||
layer.attributes = this | |||
return | return | ||
elseif self:read(-1) == "/" then | elseif self:read(-1) == "/" then | ||
layer.self_closing = true | |||
return self:pop() | return self:pop() | ||
end | end | ||
local text, head | local text, head = self.text, self.head + 1 | ||
local loc1, loc2 = find(text, layer.end_tag_pattern, head) | local loc1, loc2 = find(text, layer._parse_data.end_tag_pattern, head) | ||
if loc1 then | if loc1 then | ||
if loc1 > head then | if loc1 > head then | ||
| Line 999: | Line 955: | ||
self:emit("<") | self:emit("<") | ||
elseif not tag.ignored then | elseif not tag.ignored then | ||
self:emit(Tag:new(tag)) | self:emit(Tag:new(tag)) | ||
end | end | ||
| Line 1,015: | Line 970: | ||
local function do_heading(self) | local function do_heading(self) | ||
local layer, head = self | local layer, head = self.current_layer, self.head | ||
layer.handler, layer.index = handle_start, head | layer._parse_data.handler, layer.index = handle_start, head | ||
self:set_pattern("[\t\n ]") | self:set_pattern("[\t\n ]") | ||
-- Comments/tags interrupt the equals count. | -- Comments/tags interrupt the equals count. | ||
| Line 1,025: | Line 980: | ||
local function do_heading_possible_end(self) | local function do_heading_possible_end(self) | ||
self.current_layer._parse_data.handler = handle_possible_end | |||
self:set_pattern("[\n<]") | self:set_pattern("[\n<]") | ||
end | end | ||
| Line 1,033: | Line 987: | ||
-- ===== is "=" as an L2; ======== is "==" as an L3 etc. | -- ===== is "=" as an L2; ======== is "==" as an L3 etc. | ||
local function newline(self) | local function newline(self) | ||
local layer = self | local layer = self.current_layer | ||
local eq = layer.level | local eq = layer.level | ||
if eq <= 2 then | if eq <= 2 then | ||
| Line 1,051: | Line 1,005: | ||
if success then | if success then | ||
self:emit(Wikitext:new(possible_end)) | self:emit(Wikitext:new(possible_end)) | ||
self.current_layer._parse_data.handler = handle_body | |||
self:set_pattern("[\n<=[{]") | self:set_pattern("[\n<=[{]") | ||
return self:consume() | return self:consume() | ||
| Line 1,067: | Line 1,020: | ||
[false] = function(self) | [false] = function(self) | ||
-- Emit any excess = signs once we know it's a conventional heading. Up till now, we couldn't know if the heading is just a string of = signs (e.g. ========), so it wasn't guaranteed that the heading text starts after the 6th. | -- Emit any excess = signs once we know it's a conventional heading. Up till now, we couldn't know if the heading is just a string of = signs (e.g. ========), so it wasn't guaranteed that the heading text starts after the 6th. | ||
local layer = self | local layer = self.current_layer | ||
local eq = layer.level | local eq = layer.level | ||
if eq > 6 then | if eq > 6 then | ||
| Line 1,073: | Line 1,026: | ||
layer.level = 6 | layer.level = 6 | ||
end | end | ||
layer.handler = handle_body | layer._parse_data.handler = handle_body | ||
self:set_pattern("[\n<=[{]") | self:set_pattern("[\n<=[{]") | ||
return self:consume() | return self:consume() | ||
| Line 1,097: | Line 1,050: | ||
return self:consume() | return self:consume() | ||
end | end | ||
local layer = self | local layer = self.current_layer | ||
local level = layer.level | local level = layer.level | ||
if eq_len > level then | if eq_len > level then | ||
| Line 1,111: | Line 1,064: | ||
["{"] = function(self, this) | ["{"] = function(self, this) | ||
return self:braces( | return self:braces(this, true) | ||
end, | end, | ||
| Line 1,187: | Line 1,140: | ||
local function do_language_conversion_block(self) | local function do_language_conversion_block(self) | ||
self.current_layer._parse_data.handler = handle_language_conversion_block | |||
self:set_pattern("[\n<[{}]") | self:set_pattern("[\n<[{}]") | ||
end | end | ||
| Line 1,199: | Line 1,151: | ||
["{"] = Parser.braces, | ["{"] = Parser.braces, | ||
["}"] = function(self) | ["}"] = function(self, this) | ||
if self:read(1) == "-" then | if self:read(1) == "-" then | ||
self:emit("}-") | self:emit("}-") | ||
| Line 1,205: | Line 1,157: | ||
return self:pop() | return self:pop() | ||
end | end | ||
self:emit( | self:emit(this) | ||
end, | end, | ||
| Line 1,251: | Line 1,203: | ||
local function do_heading_block(self) | local function do_heading_block(self) | ||
self.current_layer._parse_data.handler = handle_heading_block | |||
self:set_pattern("[\n<[{]") | self:set_pattern("[\n<[{]") | ||
end | end | ||
| Line 1,290: | Line 1,241: | ||
local function do_wikilink_block(self) | local function do_wikilink_block(self) | ||
self.current_layer._parse_data.handler = handle_wikilink_block | |||
self:set_pattern("[\n<[%]{]") | self:set_pattern("[\n<[%]{]") | ||
end | end | ||
| Line 1,301: | Line 1,251: | ||
["["] = Parser.wikilink_block, | ["["] = Parser.wikilink_block, | ||
["]"] = function(self) | ["]"] = function(self, this) | ||
if self:read(1) == "]" then | if self:read(1) == "]" then | ||
self:emit("]]") | self:emit("]]") | ||
| Line 1,307: | Line 1,257: | ||
return self:pop() | return self:pop() | ||
end | end | ||
self:emit( | self:emit(this) | ||
end, | end, | ||
| Line 1,370: | Line 1,320: | ||
-- blocks. | -- blocks. | ||
local function do_parse(self, transcluded) | local function do_parse(self, transcluded) | ||
self.current_layer._parse_data.handler = handle_start | |||
self:set_pattern(".") | self:set_pattern(".") | ||
self.section = 0 | self.section = 0 | ||
| Line 1,390: | Line 1,339: | ||
-- If the first character is "=", try parsing it as a heading. | -- If the first character is "=", try parsing it as a heading. | ||
function handle_start(self, this) | function handle_start(self, this) | ||
self.current_layer._parse_data.handler = main_handler | |||
self:set_pattern("[\n<{]") | self:set_pattern("[\n<{]") | ||
if this == "=" then | if this == "=" then | ||
| Line 1,411: | Line 1,359: | ||
["<"] = Parser.tag, | ["<"] = Parser.tag, | ||
["{"] = function(self) | ["{"] = function(self, this) | ||
if self:read(1) == "{" then | if self:read(1) == "{" then | ||
self:template_or_parameter() | self:template_or_parameter() | ||
return self:consume() | return self:consume() | ||
end | end | ||
self:emit( | self:emit(this) | ||
end, | end, | ||
| Line 1,438: | Line 1,386: | ||
end | end | ||
function export.find_templates(text, not_transcluded) | |||
return parse(text, not not_transcluded):iterate_nodes("template") | |||
end | end | ||
| Line 1,484: | Line 1,426: | ||
-- "Module:Template:foo", and "Module:foo" gives "Module:Module:foo"). | -- "Module:Template:foo", and "Module:foo" gives "Module:Module:foo"). | ||
-- However, this isn't possible with mainspace (namespace 0), so prefixes | -- However, this isn't possible with mainspace (namespace 0), so prefixes | ||
-- are respected. make_title handles all of this automatically. | -- are respected. make_title() handles all of this automatically. | ||
local function finalize_arg(pagename, namespace) | local function finalize_arg(pagename, namespace) | ||
if namespace == nil then | if namespace == nil then | ||
| Line 1,490: | Line 1,432: | ||
end | end | ||
local title = make_title(namespace, pagename) | local title = make_title(namespace, pagename) | ||
return title and | return title and not title.isExternal and link_page(title, pagename) or pagename | ||
end | end | ||
| Line 1,629: | Line 1,571: | ||
do | do | ||
function export.find_parameters(text, not_transcluded) | function export.find_parameters(text, not_transcluded) | ||
return parse(text, not not_transcluded): | return parse(text, not not_transcluded):iterate_nodes("parameter") | ||
end | end | ||
| Line 1,653: | Line 1,591: | ||
end | end | ||
return level | return level | ||
end | end | ||
| Line 1,678: | Line 1,612: | ||
local parsed = parse(text) | local parsed = parse(text) | ||
if i == nil and j == nil then | if i == nil and j == nil then | ||
return | return parse(text):iterate_nodes("heading") | ||
end | end | ||
i = i and check_level(i) or 1 | i = i and check_level(i) or 1 | ||