Module:User:Theknightwho/parser/builder

local getmetatable = getmetatable local pcall = pcall local select = select local tostring = tostring local type = type local xpcall = xpcall

local table = table local concat = table.concat local insert = table.insert local remove = table.remove

--Temporary dummy objects. local Wikitext = {} Wikitext.__index = Wikitext

function Wikitext:new(t, type) return setmetatable(t, self) end

local Text = Wikitext:new{} Text.__index = Text

local Argument = Wikitext:new{} Argument.__index = Argument

local Template = Wikitext:new{} Template.__index = Template

local MissedCloseToken = {} setmetatable(MissedCloseToken, {	__call = function(t, handler, title)		MissedCloseToken.handler = handler		MissedCloseToken.title = title		error(MissedCloseToken)	end })

local function reverse_table(t) local new_t = {} local t_len = #t local base = t_len + 1 for i = t_len, 1, -1 do		new_t[base-i] = t[i] end return new_t end

local Builder = { nodes = { Argument = Argument, Template = Template, Text = Text, Wikitext = Wikitext },	stack = {} }

function Builder:push(n) insert(self.stack, {}) if n and n > 1 then self:push(n - 1) end end

function Builder:pop(node) local stack = remove(self.stack) if node ~= false then while type(stack) == "table" do			if node then stack = self.nodes[node]:new(stack, self.transcluded and node) break elseif getmetatable(stack) then break elseif #stack == 1 then stack = stack[1] else local ok, ret = pcall(concat, stack) stack = ok and ret or stack if not ok then stack = Wikitext:new(stack, self.transcluded and "Wikitext") break end end end end return stack end

function Builder:read return self.stack[#self.stack] end

function Builder:write(...) insert(self.stack[#self.stack], ...) end

function Builder:handle_other_token(token) self:write(self:handle_token(token)) end

function Builder:handle_text(token) self:push self:write(token[1]) return self:pop("Text") end

function Builder:handle_template_name self:push(2) while #self.tokens > 0 do		local token = remove(self.tokens) local token_type = token:get_type if (			token_type == "TemplateParamSeparator" or			token_type == "TemplateClose"		) then insert(self.tokens, token) return self:write(self:pop) else self:handle_other_token(token) end end MissedCloseToken("handle_template_name", self.title) end

function Builder:handle_parameter(default) self:push(2) while #self.tokens > 0 do		local token = remove(self.tokens) local token_type = token:get_type if token_type == "TemplateParamEquals" then self:write(self:pop) self:push elseif (			token_type == "TemplateParamSeparator" or			token_type == "TemplateClose"		) then insert(self.tokens, token) self:write(self:pop) if #self:read == 1 then self:write(1, tostring(default)) default = default + 1 else self:write(true) end self:write(self:pop(false)) return default else self:handle_other_token(token) end end MissedCloseToken("handle_parameter", self.title) end

function Builder:handle_template local default = 1 self:handle_template_name self:push while #self.tokens > 0 do		local token = remove(self.tokens) local token_type = token:get_type if token_type == "TemplateParamSeparator" then default = self:handle_parameter(default) elseif token_type == "TemplateClose" then self:write(self:pop(false)) return self:pop("Template") else self:handle_other_token(token) end end MissedCloseToken("handle_template", self.title) end

function Builder:handle_argument self:push(2) while #self.tokens > 0 do		local token = remove(self.tokens) local token_type = token:get_type if token_type == "ArgumentSeparator" then self:write(self:pop) self:push elseif token_type == "ArgumentClose" then self:write(self:pop) return self:pop("Argument") else self:handle_other_token(token) end end MissedCloseToken("handle_argument", self.title) end

Builder.handlers = { Text = Builder.handle_text, TemplateOpen = Builder.handle_template, ArgumentOpen = Builder.handle_argument }

function Builder:handle_token(token) return select(2, xpcall( function local token_type = token:get_type if not self.handlers[token_type] then error("Builder:handle_token got unexpected " .. token:get_type .. ".") else return self.handlers[token_type](self, token) end end, function(err) if err == MissedCloseToken then --error(debug.traceback("Builder:" .. MissedCloseToken.handler .. " missed a close token building " .. MissedCloseToken.title.fullText .. "."))				error(debug.traceback(tostring(err))) else --error("Error building " .. self.title.fullText .. ": " .. debug.traceback(err), 2) error(debug.traceback(tostring(err))) end end )) end

function Builder:build(tokens, transcluded, t)	self.tokens = reverse_table(tokens) self.title = t	self.transcluded = transcluded self:push while #self.tokens > 0 do		local node = self:handle_token(			remove(self.tokens)		) self:write(node) end return self:pop end

return Builder