<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AParser</id>
	<title>Module:Parser - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AParser"/>
	<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:Parser&amp;action=history"/>
	<updated>2026-04-07T20:09:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://mbwiki.stairwaygames.work/w/index.php?title=Module:Parser&amp;diff=5585&amp;oldid=prev</id>
		<title>Admin coral island: Created page with &quot;local p = {} local CHAR_EQUAL = string.byte(&quot;=&quot;) local CHAR_OPEN_BRACE = string.byte(&quot;{&quot;) local CHAR_PIPE = string.byte(&quot;|&quot;) local CHAR_CLOSE_BRACE = string.byte(&quot;}&quot;) local CHAR_OPEN_BRACKET = string.byte(&quot;[&quot;) local CHAR_CLOSE_BRACKET = string.byte(&quot;]&quot;) local FormatParser = {} -- methods local FormatParserMetatable = { 	__index = FormatParser, }  function p.getTemplateArgs(page, options) 	options = options or {} 	local rawContent = &#039;&#039; 	if options.custom then 		rawContent...&quot;</title>
		<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:Parser&amp;diff=5585&amp;oldid=prev"/>
		<updated>2023-08-03T06:52:53Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;local p = {} local CHAR_EQUAL = string.byte(&amp;quot;=&amp;quot;) local CHAR_OPEN_BRACE = string.byte(&amp;quot;{&amp;quot;) local CHAR_PIPE = string.byte(&amp;quot;|&amp;quot;) local CHAR_CLOSE_BRACE = string.byte(&amp;quot;}&amp;quot;) local CHAR_OPEN_BRACKET = string.byte(&amp;quot;[&amp;quot;) local CHAR_CLOSE_BRACKET = string.byte(&amp;quot;]&amp;quot;) local FormatParser = {} -- methods local FormatParserMetatable = { 	__index = FormatParser, }  function p.getTemplateArgs(page, options) 	options = options or {} 	local rawContent = &amp;#039;&amp;#039; 	if options.custom then 		rawContent...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
local CHAR_EQUAL = string.byte(&amp;quot;=&amp;quot;)&lt;br /&gt;
local CHAR_OPEN_BRACE = string.byte(&amp;quot;{&amp;quot;)&lt;br /&gt;
local CHAR_PIPE = string.byte(&amp;quot;|&amp;quot;)&lt;br /&gt;
local CHAR_CLOSE_BRACE = string.byte(&amp;quot;}&amp;quot;)&lt;br /&gt;
local CHAR_OPEN_BRACKET = string.byte(&amp;quot;[&amp;quot;)&lt;br /&gt;
local CHAR_CLOSE_BRACKET = string.byte(&amp;quot;]&amp;quot;)&lt;br /&gt;
local FormatParser = {} -- methods&lt;br /&gt;
local FormatParserMetatable = {&lt;br /&gt;
	__index = FormatParser,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function p.getTemplateArgs(page, options)&lt;br /&gt;
	options = options or {}&lt;br /&gt;
	local rawContent = &amp;#039;&amp;#039;&lt;br /&gt;
	if options.custom then&lt;br /&gt;
		rawContent = page&lt;br /&gt;
	else&lt;br /&gt;
		local title = mw.title.makeTitle(options.namespace or &amp;#039;&amp;#039;, page)&lt;br /&gt;
		if title == nil then&lt;br /&gt;
			return {}&lt;br /&gt;
		end&lt;br /&gt;
		rawContent = title:getContent()&lt;br /&gt;
		if rawContent == nil or rawContent == &amp;#039;&amp;#039; then&lt;br /&gt;
			return {}&lt;br /&gt;
		else&lt;br /&gt;
			rawContent = (rawContent:gsub(&amp;#039;&amp;lt;!%-%-.-%-%-&amp;gt;&amp;#039;, &amp;#039;&amp;#039;)) .. &amp;#039;&amp;lt;wbr&amp;gt;&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if options.unstrip then&lt;br /&gt;
		rawContent = mw.text.unstrip(rawContent)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local templates = p._parseFormat(rawContent, options.only)&lt;br /&gt;
	&lt;br /&gt;
	if options.only and type(options.only) == &amp;#039;string&amp;#039; then&lt;br /&gt;
		templates = templates[options.only] or {}&lt;br /&gt;
	elseif options.only and type(options.only) == &amp;#039;table&amp;#039; then&lt;br /&gt;
		local ONLY = {}&lt;br /&gt;
		for _, only in pairs(options.only) do&lt;br /&gt;
			if templates[only] then&lt;br /&gt;
				ONLY[only] = templates[only]&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		templates = ONLY&lt;br /&gt;
	end&lt;br /&gt;
	return templates&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._parseFormat(str, only)&lt;br /&gt;
	if not str then return nil end&lt;br /&gt;
&lt;br /&gt;
	 -- TODO consider supporting DPL-style escape characters&lt;br /&gt;
&lt;br /&gt;
	local parser = {&lt;br /&gt;
		str = str,&lt;br /&gt;
		strLen = #str,&lt;br /&gt;
		i = 1,&lt;br /&gt;
		tokenStart = 1,&lt;br /&gt;
		template = nil&lt;br /&gt;
	}&lt;br /&gt;
	setmetatable(parser, FormatParserMetatable)&lt;br /&gt;
	return parser:parse(only)&lt;br /&gt;
end&lt;br /&gt;
function FormatParser:peekByte(offset)&lt;br /&gt;
	return self.str:byte(self.i + (offset or 0))&lt;br /&gt;
end&lt;br /&gt;
function FormatParser:parse(only)&lt;br /&gt;
	local output = {}&lt;br /&gt;
	local currPage&lt;br /&gt;
	while self.i &amp;lt;= self.strLen do&lt;br /&gt;
		if self:peekByte() == CHAR_OPEN_BRACE and self:peekByte(1) == CHAR_OPEN_BRACE then&lt;br /&gt;
			currPage = self:parseTransclude()&lt;br /&gt;
		elseif self.template ~= nil then&lt;br /&gt;
			if (only ~= nil and self.template == only) or only == nil then&lt;br /&gt;
				if output[self.template] == nil then&lt;br /&gt;
					output[self.template] = currPage&lt;br /&gt;
				elseif output[self.template][1] ~= nil and type(output[self.template][1]) == &amp;#039;table&amp;#039; then&lt;br /&gt;
					table.insert(output[self.template], currPage)&lt;br /&gt;
				else&lt;br /&gt;
					local temp = output[self.template]&lt;br /&gt;
					output[self.template] = {}&lt;br /&gt;
					table.insert(output[self.template], temp)&lt;br /&gt;
					table.insert(output[self.template], currPage)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			currPage = nil&lt;br /&gt;
			self.template = nil&lt;br /&gt;
		end&lt;br /&gt;
		self.i = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return output&lt;br /&gt;
end&lt;br /&gt;
-- note: assumes all parser functions accept named args in all positions&lt;br /&gt;
-- (but in the actual wikitext parser, some (e.g., &amp;quot;#if&amp;quot;) don&amp;#039;t accept any,&lt;br /&gt;
-- and some don&amp;#039;t accept them in certain positions (e.g., first arg of &amp;quot;#invoke&amp;quot;))&lt;br /&gt;
function FormatParser:parseTransclude()&lt;br /&gt;
	local start = self.i&lt;br /&gt;
	self.i = self.i + 2 -- advance past {{&lt;br /&gt;
	self.tokenStart = self.i&lt;br /&gt;
	local currPart = {value = {}}&lt;br /&gt;
	local output = {}&lt;br /&gt;
	-- note: always adds to currPart.value. when = is reached, moves currPart.value to currPart.name.&lt;br /&gt;
	while self.i &amp;lt;= self.strLen do&lt;br /&gt;
		if not self:tryParsingOpen(currPart.value) then&lt;br /&gt;
			local currChar = self:peekByte()&lt;br /&gt;
			if currChar == CHAR_PIPE then&lt;br /&gt;
				if self.template == nil then&lt;br /&gt;
					local name = self:parseString()&lt;br /&gt;
					if name ~= nil and name ~= &amp;#039;&amp;#039; then&lt;br /&gt;
						self.template = mw.text.trim(name)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				self:parseStringInto(currPart.value)&lt;br /&gt;
				self.tokenStart = self.i+1&lt;br /&gt;
&lt;br /&gt;
				--include param to data if valid&lt;br /&gt;
				if currPart.name and #currPart.name&amp;gt;0 and currPart.value and #currPart.value&amp;gt;0 then&lt;br /&gt;
					currPart.name = mw.text.trim(table.concat(currPart.name))&lt;br /&gt;
					currPart.value = mw.text.unstrip(mw.text.trim(table.concat(currPart.value)))&lt;br /&gt;
					output[currPart.name] = currPart.value&lt;br /&gt;
				elseif currPart.value and #currPart.value&amp;gt;0 then&lt;br /&gt;
					currPart.value = mw.text.unstrip(mw.text.trim(table.concat(currPart.value)))&lt;br /&gt;
					if currPart.value ~= self.template then&lt;br /&gt;
						table.insert(output, currPart.value)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
				currPart = {value = {}}&lt;br /&gt;
			elseif currChar == CHAR_EQUAL then&lt;br /&gt;
				if not currPart.name then&lt;br /&gt;
					self:parseStringInto(currPart.value)&lt;br /&gt;
					self.tokenStart = self.i+1&lt;br /&gt;
					currPart.name = currPart.value&lt;br /&gt;
					currPart.value = {}&lt;br /&gt;
				end&lt;br /&gt;
			elseif currChar == CHAR_CLOSE_BRACE then&lt;br /&gt;
				if self:peekByte(1) == CHAR_CLOSE_BRACE then&lt;br /&gt;
					if self.template == nil then&lt;br /&gt;
						local name = self:parseString()&lt;br /&gt;
						if name ~= nil and name ~= &amp;#039;&amp;#039; then&lt;br /&gt;
							self.template = mw.text.trim(name)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					self:parseStringInto(currPart.value)&lt;br /&gt;
					self.i = self.i+1&lt;br /&gt;
					&lt;br /&gt;
					--include param to data if valid&lt;br /&gt;
					if currPart.name and #currPart.name&amp;gt;0 and currPart.value and #currPart.value&amp;gt;0 then&lt;br /&gt;
						currPart.name = mw.text.trim(table.concat(currPart.name))&lt;br /&gt;
						currPart.value = mw.text.unstrip(mw.text.trim(table.concat(currPart.value)))&lt;br /&gt;
						output[currPart.name] = currPart.value&lt;br /&gt;
					elseif currPart.value and #currPart.value&amp;gt;0 then&lt;br /&gt;
						currPart.value = mw.text.unstrip(mw.text.trim(table.concat(currPart.value)))&lt;br /&gt;
						if currPart.value ~= self.template then&lt;br /&gt;
							table.insert(output, currPart.value)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					&lt;br /&gt;
					&lt;br /&gt;
					return output&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		self.i = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	local issue = self:parseString()&lt;br /&gt;
	mw.logObject(issue, &amp;#039;issue in: &amp;#039;)&lt;br /&gt;
	mw.logObject(output, &amp;#039;up to now: &amp;#039;)&lt;br /&gt;
	error(&amp;quot;Unmatched {{ at position &amp;quot; .. start)&lt;br /&gt;
end&lt;br /&gt;
--- Add a string that ends on the character before the current one.&lt;br /&gt;
function FormatParser:parseString(offset)&lt;br /&gt;
	if self.tokenStart ~= self.i then&lt;br /&gt;
		return self.str:sub(self.tokenStart, self.i + (offset or -1))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
function FormatParser:parseStringInto(node, offset)&lt;br /&gt;
	local v = self:parseString(offset)&lt;br /&gt;
	if v then&lt;br /&gt;
		table.insert(node, v)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
-- close tokens and | = are handled in transclude/replace modes&lt;br /&gt;
function FormatParser:tryParsingOpen(node)&lt;br /&gt;
	local parseMode&lt;br /&gt;
	local currChar = self:peekByte()&lt;br /&gt;
	local nextChar = self:peekByte(1)&lt;br /&gt;
	if currChar == CHAR_OPEN_BRACE then&lt;br /&gt;
		if nextChar == CHAR_PIPE then&lt;br /&gt;
			parseMode = self.parseTable&lt;br /&gt;
		elseif nextChar == CHAR_OPEN_BRACE and self.template ~= nil and self.template ~= &amp;#039;&amp;#039; then&lt;br /&gt;
			parseMode = self.parseTranscludeBasic&lt;br /&gt;
		end&lt;br /&gt;
	elseif currChar == CHAR_OPEN_BRACKET then&lt;br /&gt;
		if nextChar == CHAR_OPEN_BRACKET then&lt;br /&gt;
			parseMode = self.parseLink&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if parseMode then&lt;br /&gt;
		self:parseStringInto(node)&lt;br /&gt;
		parseMode(self, node)&lt;br /&gt;
		self.tokenStart = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return parseMode ~= nil&lt;br /&gt;
end&lt;br /&gt;
function FormatParser:parseTranscludeBasic(output)&lt;br /&gt;
	local start = self.i&lt;br /&gt;
	self.tokenStart = self.i&lt;br /&gt;
&lt;br /&gt;
	self.i = self.i + 2&lt;br /&gt;
&lt;br /&gt;
	while self.i &amp;lt;= self.strLen do&lt;br /&gt;
		if not self:tryParsingOpen(output) then&lt;br /&gt;
			if self:peekByte() == CHAR_CLOSE_BRACE and self:peekByte(1) == CHAR_CLOSE_BRACE then&lt;br /&gt;
				self.i = self.i + 1&lt;br /&gt;
				self:parseStringInto(output, 0)&lt;br /&gt;
				return output&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		self.i = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	local issue = self:parseString()&lt;br /&gt;
	mw.logObject(issue, &amp;#039;issue in: &amp;#039;)&lt;br /&gt;
	mw.logObject(self.str, &amp;#039;entire page: &amp;#039;)&lt;br /&gt;
	error(&amp;quot;Unmatched {{ at position &amp;quot; .. start)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FormatParser:parseLink(output)&lt;br /&gt;
	local start = self.i&lt;br /&gt;
	self.tokenStart = self.i&lt;br /&gt;
&lt;br /&gt;
	self.i = self.i + 2&lt;br /&gt;
&lt;br /&gt;
	while self.i &amp;lt;= self.strLen do&lt;br /&gt;
		if not self:tryParsingOpen(output) then&lt;br /&gt;
			if self:peekByte() == CHAR_CLOSE_BRACKET and self:peekByte(1) == CHAR_CLOSE_BRACKET then&lt;br /&gt;
				self.i = self.i + 1&lt;br /&gt;
				self:parseStringInto(output, 0)&lt;br /&gt;
				return output&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		self.i = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	local issue = self:parseString()&lt;br /&gt;
	mw.logObject(issue, &amp;#039;issue in: &amp;#039;)&lt;br /&gt;
	mw.logObject(self.str, &amp;#039;entire page: &amp;#039;)&lt;br /&gt;
	error(&amp;quot;Unmatched [[ at position &amp;quot; .. start)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function FormatParser:parseTable(output)&lt;br /&gt;
	local start = self.i&lt;br /&gt;
	self.tokenStart = self.i&lt;br /&gt;
&lt;br /&gt;
	self.i = self.i + 2&lt;br /&gt;
&lt;br /&gt;
	while self.i &amp;lt;= self.strLen do&lt;br /&gt;
		if not self:tryParsingOpen(output) then&lt;br /&gt;
			if self:peekByte() == CHAR_PIPE and self:peekByte(1) == CHAR_CLOSE_BRACE then&lt;br /&gt;
				self.i = self.i + 1&lt;br /&gt;
				self:parseStringInto(output, 0)&lt;br /&gt;
				&lt;br /&gt;
				return output&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		self.i = self.i + 1&lt;br /&gt;
	end&lt;br /&gt;
	local issue = self:parseString()&lt;br /&gt;
	mw.logObject(issue, &amp;#039;issue in: &amp;#039;)&lt;br /&gt;
	mw.logObject(self.str, &amp;#039;entire page: &amp;#039;)&lt;br /&gt;
	error(&amp;quot;Unmatched {| at position &amp;quot; .. start)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.removeBlank(tab)&lt;br /&gt;
	if type(tab) ~= &amp;#039;table&amp;#039; then return false end&lt;br /&gt;
	for _ in pairs(tab) do&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Admin coral island</name></author>
	</entry>
</feed>