Module:parser: Difference between revisions

Jump to navigation Jump to search
No edit summary
Line 1: Line 1:
local export = {}
local export = {}


local metamethods_data_module = "Module:data/metamethods"
local scribunto_metamethods_module = "Module:Scribunto/metamethods"
local table_module = "Module:table"
local table_deep_copy_module = "Module:table/deepCopy"
local table_get_nested_module = "Module:table/getNested"
local table_set_nested_module = "Module:table/setNested"


local concat = table.concat
local concat = table.concat
local find = string.find
local getmetatable = getmetatable
local getmetatable = getmetatable
local insert = table.insert
local insert = table.insert
Line 12: Line 15:
local remove = table.remove
local remove = table.remove
local require = require
local require = require
local select = select
local setmetatable = setmetatable
local setmetatable = setmetatable
local sub = string.sub
local type = type
local type = type
local unpack = unpack
local unpack = unpack or table.unpack -- Lua 5.2 compatibility


local classes = {}
local node_classes = {}


--[==[
local function deep_copy(...)
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.]==]
deep_copy = require(table_deep_copy_module)
local function deep_copy(...)
return deep_copy(...)
deep_copy = require(table_module).deepCopy
end
return deep_copy(...)
 
end
local function get_nested(...)
get_nested = require(table_get_nested_module)
return get_nested(...)
end
 
local function set_nested(...)
set_nested = require(table_set_nested_module)
return set_nested(...)
end


--[==[
--[==[
Line 30: Line 43:
local function get_metamethods()
local function get_metamethods()
-- Use require, since lookup times are much slower with mw.loadData.
-- Use require, since lookup times are much slower with mw.loadData.
metamethods, get_metamethods = require(metamethods_data_module), nil
metamethods, get_metamethods = require(scribunto_metamethods_module), nil
return metamethods
return metamethods
end
end
Line 39: Line 52:
--
--
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
local function get_nested(t, k, ...)
if t == nil then
return nil
elseif ... == nil then
return t[k]
end
return get_nested(t[k], ...)
end
local function set_nested(t, k, v, ...)
if ... ~= nil then
local t_next = t[k]
if t_next == nil then
t_next = {}
t[k] = t_next
end
return set_nested(t_next, v, ...)
end
t[k] = v
end


local function inherit_metamethods(child, parent)
local function inherit_metamethods(child, parent)
Line 81: Line 73:
end
end
local mt = getmetatable(value)
local mt = getmetatable(value)
return not (mt == nil or classes[mt] == nil)
return not (mt == nil or node_classes[mt] == nil)
end
 
local function node_class(value)
if value == nil then
return nil
end
local mt = getmetatable(value)
if mt == nil then
return nil
end
return mt ~= nil and node_classes[mt] or nil
end
 
local function class_else_type(value)
if value == nil then
return type(value)
end
local mt = getmetatable(value)
if mt == nil then
return type(value)
end
local class = node_classes[mt]
return class == nil and type(value) or class
end
end


Line 97: Line 112:
return _tostring(value)
return _tostring(value)
end
end
end
local function class_else_type(value)
if value == nil then
return type(value)
end
local mt = getmetatable(value)
if mt == nil then
return type(value)
end
local class = classes[mt]
return class == nil and type(value) or class
end
end


Line 178: Line 181:
end
end


function Node:iterate_nodes()
function Node:iterate_nodes(...)
return self:iterate(is_node)
local args_n = select("#", ...)
if args_n == 0 then
return self:iterate(is_node)
elseif args_n == 1 then
local class = ...
return self:iterate(function(value)
return node_class(value) == class
end)
end
local classes = {}
for i = 1, args_n do
classes[select(i, ...)] = true
end
return self:iterate(function(value)
return classes[node_class(value)]
end)
end
end


Line 198: Line 216:
t.__index = t
t.__index = t
t = inherit_metamethods(t, self)
t = inherit_metamethods(t, self)
classes[t] = class
node_classes[t] = class
return setmetatable(t, self)
return setmetatable(t, self)
end
end
Node.keys_to_remove = {"fail", "handler", "head", "override", "route"}


function Node:new(t)
function Node:new(t)
setmetatable(t, nil)
rawset(t, "_parse_data", nil)
local keys_to_remove = self.keys_to_remove
for i = 1, #keys_to_remove do
t[keys_to_remove[i]] = nil
end
return setmetatable(t, self)
return setmetatable(t, self)
end
end
Line 261: Line 273:
}, Proxy)
}, Proxy)
end
end
end
function export.node()
return Node:new_class("node")
end
end


Line 272: Line 288:
Parser.__index = Parser
Parser.__index = Parser


function Parser:read(delta)
function Parser:get_layer(n)
local v = self.text[self.head + (delta or 0)]
return v == nil and "" or v
end
 
function Parser:advance(n)
self.head = self.head + (n == nil and 1 or n)
end
 
function Parser:layer(n)
if n ~= nil then
if n ~= nil then
return rawget(self, #self + n)
return rawget(self, #self + n)
end
end
return self[-1]
return self.current_layer
end
end


function Parser:emit(a, b)
function Parser:emit(a, b)
local layer = self[-1]
local layer = self.current_layer
if b ~= nil then
if b ~= nil then
insert(layer, signed_index(layer, a), b)
insert(layer, signed_index(layer, a), b)
Line 298: Line 305:


function Parser:emit_tokens(a, b)
function Parser:emit_tokens(a, b)
local layer = self[-1]
local layer = self.current_layer
if b ~= nil then
if b ~= nil then
a = signed_index(layer, a)
a = signed_index(layer, a)
Line 314: Line 321:


function Parser:remove(n)
function Parser:remove(n)
local layer = self[-1]
local layer = self.current_layer
if n ~= nil then
if n ~= nil then
return remove(layer, signed_index(layer, n))
return remove(layer, signed_index(layer, n))
Line 325: Line 332:


function Parser:replace(a, b)
function Parser:replace(a, b)
local layer = self[-1]
local layer = self.current_layer
layer[signed_index(layer, a)] = b
layer[signed_index(layer, a)] = b
end
end
Line 334: Line 341:
return self:concat(0, a, b)
return self:concat(0, a, b)
end
end
local layer, ret, n = self:layer(a), {}, 0
local layer, ret, n = self:get_layer(a), {}, 0
for i = b and signed_index(layer, b) or 1, c and signed_index(layer, c) or #layer do
for i = b and signed_index(layer, b) or 1, c and signed_index(layer, c) or #layer do
n = n + 1
n = n + 1
Line 346: Line 353:
delta = -1
delta = -1
end
end
local i = 0
local get_layer, i = self.get_layer, 0
while true do
while true do
local layer = self:layer(i)
local layer = get_layer(self, i)
if layer == nil then
if layer == nil then
return nil
return nil
Line 362: Line 369:


function Parser:push(route)
function Parser:push(route)
local layer = {
local layer = {_parse_data = {
head = self.head,
head = self.head,
route = route
route = route,
}
}}
self[#self + 1] = layer
self[#self + 1] = layer
self[-1] = layer
self.current_layer = layer
end
end


function Parser:push_sublayer(handler, inherit)
function Parser:push_sublayer(handler, inherit)
local pdata = {
handler = handler,
sublayer = true,
}
local sublayer = {
local sublayer = {
handler = handler,
_parse_data = pdata,
sublayer = true
}
}
if inherit then
if inherit then
local layer = self[-1]
local layer_parse_data = self.current_layer._parse_data
setmetatable(sublayer, inherit_metamethods({
setmetatable(pdata, inherit_metamethods({
__index = layer,
__index = layer_parse_data,
__newindex = layer
__newindex = layer_parse_data
}, getmetatable(layer)))
}, getmetatable(layer_parse_data)))
end
end
self[#self + 1] = sublayer
self[#self + 1] = sublayer
self[-1] = sublayer
self.current_layer = sublayer
end
end


Line 392: Line 402:
self[len] = nil
self[len] = nil
len = len - 1
len = len - 1
local new = self[len]
if len == 0 then
self[-1] = new == nil and self or new
self.current_layer = self
if layer.sublayer == nil then
break
elseif layer._parse_data.sublayer == nil then
self.current_layer = self[len]
break
break
end
end
self:emit_tokens(layer)
self:emit_tokens(layer)
end
end
return layer
return setmetatable(layer, nil)
end
end


function Parser:pop_sublayer()
function Parser:pop_sublayer()
local len, layer = #self, self[-1]
local len, layer = #self, self.current_layer
self[len] = nil
self[len] = nil
local new = self[len - 1]
self.current_layer = len == 1 and self or self[len - 1]
self[-1] = new == nil and self or new
return setmetatable(layer, nil)
setmetatable(layer, nil)
layer.sublayer = nil
return layer
end
end


Line 422: Line 431:


function Parser:try(route, ...)
function Parser:try(route, ...)
local failed_layer = get_nested(self.failed_routes, route, self.head)
local failed_routes = self.failed_routes
if failed_layer ~= nil then
if failed_routes ~= nil then
return false, failed_layer
local failed_layer = get_nested(failed_routes, route, self.head)
if failed_layer ~= nil then
return false, failed_layer
end
end
end
local layer = self:get(route, ...)
local layer = self:get(route, ...)
return not layer.fail, layer
return not layer._parse_data.fail, layer
end
 
function Parser:consume(this, ...)
local layer = self[-1]
if this == nil then
this = self:read()
end
return (layer.override or layer.handler)(self, this, ...)
end
end


function Parser:fail_route()
function Parser:fail_route()
local layer = self:pop()
local layer = self:pop()
layer.fail = true
local pdata = layer._parse_data
set_nested(self, "failed_routes", layer.route, layer.head, layer)
pdata.fail = true
self.head = layer.head
local layer_head = pdata.head
set_nested(self, layer, "failed_routes", pdata.route, layer_head)
self.head = layer_head
return layer
return layer
end
end


function Parser:traverse()
function Parser:traverse()
local consume, advance = self.consume, self.advance
while true do
while true do
local layer = self:consume()
local layer = consume(self)
if layer ~= nil then
if layer ~= nil then
return layer
return layer
end
end
self:advance()
advance(self)
end
end
end
end
Line 466: Line 473:
function Parser:switch(func, t)
function Parser:switch(func, t)
local layer = self[-1]
local pdata = self.current_layer._parse_data
-- Point handler to the new switch table if the calling function is the current handler.
-- Point handler to the new switch table if the calling function is the current handler.
if layer.handler == func then
if pdata.handler == func then
layer.handler = t
pdata.handler = t
end
end
return setmetatable(t, Switch)
return setmetatable(t, Switch)
Line 509: Line 516:
export.tostring = tostring
export.tostring = tostring


function export.new()
local ArrayParser = Parser:new_class()
return Parser:new_class(), Node:new_class("node")
 
function ArrayParser:read(delta)
local v = self.text[self.head + (delta or 0)]
return v == nil and "" or v
end
 
function ArrayParser:advance(n)
self.head = self.head + (n == nil and 1 or n)
end
 
function ArrayParser:jump(head)
self.head = head
end
 
function ArrayParser:consume(this, ...)
if this == nil then
this = self:read()
end
local pdata = self.current_layer._parse_data
return pdata.handler(self, this, ...)
end
 
function export.array_parser()
return ArrayParser:new_class()
end
 
local StringParser = Parser:new_class()
 
function StringParser:read(i, j)
local head, i = self.head, i or 0
return sub(self.text, head + i, head + (j or i))
end
 
function StringParser:advance(n)
self.head = self.head + (n or self.current_layer._parse_data.step or 1)
end
 
function StringParser:jump(head)
local pdata = self.current_layer._parse_data
self.head, pdata.next, pdata.next_len = head, nil, nil
end
 
-- If `ignore_nonmatch` is set, any non-match segment before the match will be ignored.
function StringParser:set_pattern(pattern, ignore_nonmatch)
local pdata = self.current_layer._parse_data
pdata.pattern, pdata.next, pdata.next_len = "(" .. pattern .. ")", nil, nil
if ignore_nonmatch then
pdata.ignore_nonmatch = true
end
end
 
function StringParser:consume()
local pdata = self.current_layer._parse_data
local this = pdata.next
-- Use `next` and `next_len` from the previous iteration, if available.
if this then
pdata.step, pdata.next, pdata.next_len = pdata.next_len, nil, nil
return pdata.handler(self, this)
end
local text, head, loc1, loc2 = self.text, self.head
loc1, loc2, this = find(text, pdata.pattern, head)
-- If there was no match, default to find(text, "$", head), with `this` as
-- the empty string.
if not loc1 then
this, loc1 = "", #text + 1
loc2 = loc1 - 1 -- zero-length matches cause loc2 to be less than loc1
end
-- If `this` is at `head`, consume it.
if loc1 == head then
pdata.step = loc2 - loc1 + 1
-- If `ignore_nonmatch` is set, ignore everything before `this`, then
-- consume it.
elseif pdata.ignore_nonmatch then
self.head, pdata.step = loc1, loc2 - loc1 + 1
-- Otherwise, consume everything before `this`, and memoize the match and
-- match length; the length is dealt with separately, as it could be 0 if
-- `next` is an index (e.g. if the pattern is the empty string).
else
this, pdata.step, pdata.next, pdata.next_len = sub(text, head, loc1 - 1), loc1 - head, this, loc2 - loc1 + 1
end
return pdata.handler(self, this)
end
 
function export.string_parser()
return StringParser:new_class()
end
end


return export
return export
Anonymous user

Navigation menu