વિભાગ: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
}