Module:parser: Difference between revisions
Use Module:table/getNested and Module:table/setNested.
No edit summary |
(Use Module:table/getNested and Module:table/setNested.) |
||
| Line 1: | Line 1: | ||
local export = {} | local export = {} | ||
local | local scribunto_metamethods_module = "Module:Scribunto/metamethods" | ||
local | 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 | local node_classes = {} | ||
local function deep_copy(...) | |||
deep_copy = require(table_deep_copy_module) | |||
local function | 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, get_metamethods = require(scribunto_metamethods_module), nil | ||
return metamethods | return metamethods | ||
end | end | ||
| Line 39: | Line 52: | ||
-- | -- | ||
------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ||
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 | 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 | 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) | ||
node_classes[t] = class | |||
return setmetatable(t, self) | return setmetatable(t, self) | ||
end | end | ||
function Node:new(t) | function Node:new(t) | ||
rawset(t, "_parse_data", nil) | |||
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: | function Parser:get_layer(n) | ||
if n ~= nil then | if n ~= nil then | ||
return rawget(self, #self + n) | return rawget(self, #self + n) | ||
end | end | ||
return self | return self.current_layer | ||
end | end | ||
function Parser:emit(a, b) | function Parser:emit(a, b) | ||
local layer = self | 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 | 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 | 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 | 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: | 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 | 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 | 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 = { | ||
_parse_data = pdata, | |||
} | } | ||
if inherit then | if inherit then | ||
local | local layer_parse_data = self.current_layer._parse_data | ||
setmetatable( | setmetatable(pdata, inherit_metamethods({ | ||
__index = | __index = layer_parse_data, | ||
__newindex = | __newindex = layer_parse_data | ||
}, getmetatable( | }, getmetatable(layer_parse_data))) | ||
end | end | ||
self[#self + 1] = sublayer | self[#self + 1] = sublayer | ||
self | self.current_layer = sublayer | ||
end | end | ||
| Line 392: | Line 402: | ||
self[len] = nil | self[len] = nil | ||
len = len - 1 | len = len - 1 | ||
if len == 0 then | |||
self.current_layer = self | |||
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 | local len, layer = #self, self.current_layer | ||
self[len] = nil | self[len] = nil | ||
self.current_layer = len == 1 and self or self[len - 1] | |||
return setmetatable(layer, nil) | |||
setmetatable(layer, nil) | |||
end | end | ||
| Line 422: | Line 431: | ||
function Parser:try(route, ...) | function Parser:try(route, ...) | ||
local failed_layer = get_nested( | local failed_routes = self.failed_routes | ||
if failed_routes ~= nil then | |||
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 | 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", | pdata.fail = true | ||
self.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 = | local layer = consume(self) | ||
if layer ~= nil then | if layer ~= nil then | ||
return layer | return layer | ||
end | end | ||
advance(self) | |||
end | end | ||
end | end | ||
| Line 466: | Line 473: | ||
function Parser:switch(func, t) | function Parser:switch(func, t) | ||
local | 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 | if pdata.handler == func then | ||
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. | local ArrayParser = Parser:new_class() | ||
return Parser:new_class(), | |||
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 | ||