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 end
local function walk_inner(tree, container_elems) local function walk_inner(tree, container_elems)
local parents = {} -- Use two tables to store values so that a new table doesn't have to be
local i = 1 -- created every time a container is entered
local parent_trees = {}
local parent_indexes = {}
local parent_idx = 0
local i = 0
return function() return function()
local res = tree[i] -- If the previously yielded element has children
while not res do if i > 0 and container_elems[tree[i].type] then
local n = table.remove(parents) -- Save the parent element and the next index
if not n then parent_idx = parent_idx + 1
return parent_trees[parent_idx] = tree
end parent_indexes[parent_idx] = i + 1
tree, i = n[1], n[2]
res = tree[i] -- Set the new tree
tree = tree[i]
-- Reset I to initial value (zero)
i = 0
end end
-- Point index to next child
i = i + 1 i = i + 1
if container_elems[res.type] then -- Get child at index
table.insert(parents, {tree, i}) local elem = tree[i]
tree = res while not elem do -- current child is invalid
i = 1 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 end
return res
return elem, tree, i
end end
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;name;a,b,c;1;false]"))
assert.is_nil(formspec_ast.parse("tabheader[1,2;3,4;name;a,b,c;1]")) assert.is_nil(formspec_ast.parse("tabheader[1,2;3,4;name;a,b,c;1]"))
end) 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)