લખાણ પર જાઓ

વિભાગ:parser

વિકિકોશમાંથી

Documentation for this module may be created at વિભાગ:parser/doc

local concat = table.concat
local insert = table.insert
local ipairs = ipairs
local next = next
local rawget = rawget
local rawset = rawset
local remove = table.remove
local setmetatable = setmetatable
local tostring = tostring
local type = type

------------------------------------------------------------------------------------
--
-- Nodes
--
------------------------------------------------------------------------------------

local Node = {}
Node.__index = Node

function Node:next()
	self.i = self.i + 1
	return self[self.i], self, self.i
end

function Node:next_node()
	repeat
		self.i = self.i + 1
	until not self[self.i] or type(self[self.i]) == "table"
	return self[self.i], self, self.i
end

function Node:iterate()
	local value, parent, key, node = self._next(self._child or self)
	if not value then
		self = self._parent
		if self then
			self._next, self._child = self[self._next_func] or self.next
			return self:iterate(self)
		end
	elseif not node and type(value) == "table" then
		self._next, self._child = value:__pairs(self._next_func, self)
		return value, parent, key, true
	else
		return value, parent, key, node or false
	end
end

function Node:new_iter(next_func, parent)
	self.i = 0
	self._parent = parent
	self._child = nil
	self._next_func = next_func or "next"
	self._next = self[next_func] or self.next
end

function Node:__pairs(next_func, parent)
	self:new_iter(next_func, parent)
	return self.iterate, self
end

function Node:rawpairs()
	return next, self
end

-- Note: recursively calling tostring() adds to the C stack (limit: 200), whereas calling __tostring metamethods directly does not. Occasionally relevant when dealing with very deep nesting.
function Node:__tostring()
	local output = {}
	for _, v in ipairs(self) do
		insert(output, type(v) == "table" and v:__tostring() or tostring(v))
	end
	return concat(output)
end

local function new(self, t)
	rawset(t, "handler", nil)
	rawset(t, "override", nil)
	rawset(t, "context", nil)
	return setmetatable(t, self)
end

function Node:new(type)
	local t = {
		__concat = self.__concat,
		__pairs = self.__pairs,
		__tostring = self.__tostring,
		type = type,
		new = new
	}
	t.__index = t
	return setmetatable(t, self)
end

local Wikitext = Node:new("wikitext")

function Wikitext:new(t, force_wrapper)
	if not force_wrapper and t.len == 1 and type(t[1]) == "table" then
		return t[1]
	end
	return new(self, t)
end

do
	local deepcopy
	
	local function insert_clone(output, n)
		if n.type == "wikitext" then
			for _, v in ipairs(n) do
				insert(output, deepcopy(v))
			end
		else
			insert(output, deepcopy(n))
		end
	end
	
	function Node:__concat(a)
		deepcopy = deepcopy or require("Module:table").deepcopy
		local output = Wikitext:new{}
		insert_clone(output, self)
		insert_clone(output, a)
		return output
	end
end

------------------------------------------------------------------------------------
--
-- Parser
--
------------------------------------------------------------------------------------

local function signed_index(t, n)
	return n and n <= 0 and t.len + 1 + n or n
end

local Parser = {}
Parser.__index = Parser

function Parser:read(delta)
	return self.text[self.head + (delta or 0)] or ""
end

function Parser:advance(n)
	self.head = self.head + (n or 1)
end

function Parser:layer(n)
	if n then
		return rawget(self, self.len + n)
	end
	return self.n
end

function Parser:emit(a, b)
	local layer = self.n
	if b then
		a = signed_index(layer, a)
		insert(layer, a, b)
		layer.len = layer.len + 1
	else
		local len = layer.len + 1
		rawset(layer, len, a)
		layer.len = len
	end
end

function Parser:emit_tokens(a, b)
	local layer = self.n
	local len = layer.len
	if b then
		a = signed_index(layer, a)
		for i = 1, b.len do
			len = len + 1
			insert(layer, a + i - 1, b[i])
		end
	else
		for i = 1, a.len do
			len = len + 1
			rawset(layer, len, a[i])
		end
	end
	layer.len = len
end

function Parser:remove(n)
	local layer = self.n
	local len, token = layer.len
	if n then
		n = signed_index(layer, n)
		token = remove(layer, n)
		layer.len = layer.len - 1
	else
		token = layer[len]
		layer[len] = nil
		len = len - 1
		layer.len = len
	end
	return token
end

function Parser:replace(a, b)
	local layer = self.n
	a = signed_index(layer, a)
	layer[a] = b
end

-- Unlike default table.concat, this respects __tostring metamethods.
function Parser:concat(a, b, c)
	if not a or a > 0 then
		return self:concat(0, a, b)
	end
	local layer = self:layer(a)
	b = signed_index(layer, b) or 1
	c = signed_index(layer, c) or #layer
	local ret = {}
	for i = b, c do
		insert(ret, tostring(layer[i]))
	end
	return concat(ret)
end

function Parser:emitted(delta)
	delta = delta or -1
	local i, layer = 0, self.n
	while layer and -delta > layer.len do
		delta = delta + layer.len
		i = i - 1
		layer = self:layer(i)
	end
	return layer and rawget(layer, layer.len + delta + 1)
end

function Parser:push(handler)
	local layer = {
		handler = handler,
		head = self.head,
		context = handler,
		len = 0
	}
	local len = self.len + 1
	self[len] = layer
	self.n = layer
	self.len = len
end

function Parser:push_sublayer(handler)
	local layer = self.n
	rawset(layer, "__index", layer)
	rawset(layer, "__newindex", layer)
	local sublayer = setmetatable({
		handler = handler,
		sublayer = true,
		len = 0
	}, layer)
	local len = self.len + 1
	self[len] = sublayer
	self.n = sublayer
	self.len = len
end

function Parser:pop()
	local len, layer = self.len
	while self.n.sublayer do
		layer = self[len]
		self[len] = nil
		len = len - 1
		self.n = self[len]
		self:emit_tokens(layer)
	end
	layer = self[len]
	self[len] = nil
	len = len - 1
	self.n = self[len] or self
	self.len = len
	rawset(layer, "__concat", nil)
	rawset(layer, "__index", nil)
	rawset(layer, "__newindex", nil)
	rawset(layer, "__pairs", nil)
	rawset(layer, "__tostring", nil)
	return layer
end

function Parser:pop_sublayer()
	local len = self.len
	local layer = self[len]
	self[len] = nil
	len = len - 1
	self.n = self[len] or self
	self.len = len
	rawset(layer, "__concat", nil)
	rawset(layer, "__index", nil)
	rawset(layer, "__newindex", nil)
	rawset(layer, "__pairs", nil)
	rawset(layer, "__tostring", nil)
	return layer
end

function Parser:get(handler, first, ...)
	local head = self.head
	if (
		self.bad_routes and
		self.bad_routes[head]
	) then
		local bad_route = self.bad_routes[head][handler]
		if bad_route then
			self.n.bad_route = bad_route
			return bad_route
		end
	end
	self:push(handler)
	local layer
	if first then
		layer = first(self, ...)
	end
	if not layer then
		layer = self:traverse()
	end
	if layer == self.n.bad_route then
		self.head = head
	end
	return layer
end

function Parser:consume(this, ...)
	return (self.n.override or self.n.handler)(self, this or self:read(), ...)
end

function Parser:fail_route()
	local bad_route = self:pop()
	local head = bad_route.head
	if not self.bad_routes then
		self.bad_routes = {}
	end
	self.bad_routes[head] = self.bad_routes[head] or {}
	self.bad_routes[head][bad_route.context] = bad_route
	self.n.bad_route = bad_route
	return bad_route
end

function Parser:traverse()
	local layer
	while true do
		layer = self:consume()
		if layer then
			return layer
		end
		self:advance()
	end
end

function Parser:new(text)
	return setmetatable({
		text = text,
		head = 1,
		len = 0
	}, Parser)
end

do
	local function default_handler(self, this)
		if this == "" then
			return self:pop()
		end
		self:emit(this)
	end
	
	function Parser:parse(text, init_handler, first, ...)
		local parser = Parser:new(text)
		local tokens = parser:get(init_handler or default_handler, first, ...)
		if tokens == parser.bad_route then
			error("Parser exited with bad route.")
		elseif parser.len > 0 then
			error("Parser exited with non-empty stack.")
		end
		return Wikitext:new(tokens)
	end
end

return {
	Node = Node,
	Wikitext = Wikitext,
	Parser = Parser
}