feat(walk_inner): provide parent info

This commit is contained in:
Lazerbeak12345 2023-09-03 01:26:02 +00:00 committed by luk3yx
parent 4836cb2fb1
commit 61e91d483d
No known key found for this signature in database
2 changed files with 192 additions and 15 deletions

View File

@ -46,26 +46,47 @@ function formspec_ast.interpret(spec, custom_handlers)
end
local function walk_inner(tree, container_elems)
local parents = {}
local i = 1
-- Use two tables to store values so that a new table doesn't have to be
-- created every time a container is entered
local parent_trees = {}
local parent_indexes = {}
local parent_idx = 0
local i = 0
return function()
local res = tree[i]
while not res do
local n = table.remove(parents)
if not n then
return
end
tree, i = n[1], n[2]
res = tree[i]
-- If the previously yielded element has children
if i > 0 and container_elems[tree[i].type] then
-- Save the parent element and the next index
parent_idx = parent_idx + 1
parent_trees[parent_idx] = tree
parent_indexes[parent_idx] = i + 1
-- Set the new tree
tree = tree[i]
-- Reset I to initial value (zero)
i = 0
end
-- Point index to next child
i = i + 1
if container_elems[res.type] then
table.insert(parents, {tree, i})
tree = res
i = 1
-- Get child at index
local elem = tree[i]
while not elem do -- current child is invalid
if parent_idx < 1 then
return
end
-- Restore parent's relative index
tree, i = parent_trees[parent_idx], parent_indexes[parent_idx]
parent_idx = parent_idx - 1
-- Get child at index
elem = tree[i]
end
return res
return elem, tree, i
end
end

156
test.lua
View File

@ -620,3 +620,159 @@ it("does not parse invalid tabheader elements", function()
assert.is_nil(formspec_ast.parse("tabheader[1,2;name;a,b,c;1;false]"))
assert.is_nil(formspec_ast.parse("tabheader[1,2;3,4;name;a,b,c;1]"))
end)
describe("helpers", function ()
describe("walk", function ()
it("walks over every element", function ()
local tree = {
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "container",
{ type = "label", label = "the text" }
}
}
for node in formspec_ast.walk(tree) do
node.visited = true
end
assert.same({
{ type = "box", color = "green", visited = true },
{ type = "label", label = "the text", visited = true },
{
type = "container",
visited = true,
{ type = "label", label = "the text", visited = true }
}
}, tree)
end)
it("can be stopped", function ()
local tree = {
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "container",
{ type = "label", label = "the text" }
},
{ type = "label", label = "the text" }
}
local count = 0
for node in formspec_ast.walk(tree) do
count = count + 1
node.visited = true
if count == 3 then break end
end
assert.same({
{ type = "box", color = "green", visited = true },
{ type = "label", label = "the text", visited = true },
{
type = "container",
visited = true,
{ type = "label", label = "the text" }
},
{ type = "label", label = "the text" }
}, tree)
end)
it("can accept custom element list", function ()
local tree = {
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "nonsensewordhere",
{ type = "label", label = "the text" }
},
{
type = "container",
{ type = "label", label = "the text" }
},
{ type = "label", label = "the text" }
}
for node in formspec_ast.walk(tree, {nonsensewordhere = true}) do
node.visited = true
end
assert.same({
{ type = "box", color = "green", visited = true },
{ type = "label", label = "the text", visited = true },
{
type = "nonsensewordhere",
visited = true,
{ type = "label", label = "the text", visited = true }
},
{
type = "container",
visited = true,
{ type = "label", label = "the text" }
},
{ type = "label", label = "the text", visited = true }
}, tree)
end)
it("can provide parent info when walking", function ()
local tree = {
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "container",
{ type = "label", label = "the text" }
},
{ type = "label", label = "the text" }
}
local logged_indexes = {}
for node, parent, index in formspec_ast.walk(tree) do
node.visited = true
parent.parent_of = (parent.parent_of or 0) + 1
logged_indexes[#logged_indexes+1] = index
end
assert.same({
parent_of = 4,
{ type = "box", color = "green", visited = true },
{ type = "label", label = "the text", visited = true },
{
type = "container",
visited = true,
parent_of = 1,
{ type = "label", label = "the text", visited = true }
},
{ type = "label", label = "the text", visited = true },
}, tree)
-- NOTE: this is, in effect, asserting the order of the crawl
assert.same({ 1, 2, 3, 1, 4}, logged_indexes)
end)
it("parent info can be modified without failure", function ()
-- INFO: This test is a regression test.
local tree = {
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "container",
{ type = "label", label = "the text" }
}
}
local found_child = false
for node, parent in formspec_ast.walk(tree) do
if node.type == "container" then
node[#node+1] = { type = "label", label = "the new text" }
elseif parent.type == "container" then
node.is_child_thingy = true
if not found_child then
found_child = true
node.type = "container"
node[1] = { type = "box", color = "red" }
node.label = nil
end
end
end
assert.same({
{ type = "box", color = "green" },
{ type = "label", label = "the text" },
{
type = "container",
{
type = "container",
is_child_thingy = true,
{ type = "box", color = "red", is_child_thingy = true }
},
{ type = "label", label = "the new text", is_child_thingy = true }
}
}, tree)
end)
end)
end)