This commit is contained in:
Aaron Smith 2023-09-11 15:11:04 +01:00
parent 4f9db6ff41
commit 3ecec1f787
2 changed files with 65 additions and 21 deletions

View file

@ -5,18 +5,17 @@ local flags = require(script.Parent.flags)
export type StartNode<T> = {
cache: T,
children: { [Node<T>]: true } | false
children: { Node<T> } | false
}
export type Node<T> = {
cache: T,
children: { [Node<T>]: true } | false,
parents: { StartNode<T> },
children: { Node<T> } | false,
effect: (T) -> () | false,
cleanups: { () -> () } | false,
}
local active = {} :: { [Node<any>]: true }
local scopes = { n = 0 } :: { [number]: Node<any>, n: number }
local WEAK_VALUES = { __mode = "v" }
@ -59,11 +58,12 @@ end
local function add_child<T>(parent: StartNode<any>, child: Node<any>)
if parent.children then
parent.children[child] = true
table.insert(parent.children :: { Node<T> }, child)
else
parent.children = { [child] = true :: true }
setmetatable(parent.children :: any, WEAK_KEYS) -- todo:
parent.children = { child }
end
table.insert(child.parents, parent)
end
local function open_scope<T>(node: Node<T>)
@ -101,13 +101,25 @@ local function run_effect<T>(node: Node<T>)
end
end
local function unparent<T>(node: Node<T>)
for _, parent in node.parents do
local children = parent.children :: {}
local idx = table.find(children :: {}, node)
local n = #children
children[idx] = children[n]
children[n] = nil
end
table.clear(node.parents)
end
local function destroy<T>(node: Node<T>)
run_cleanups(node)
active[node] = nil
if node.children then
for child in node.children do
destroy(child)
end
unparent(node)
local children = node.children :: {}
if children then
while children[1] do destroy(children[1]) end
end
end
@ -117,12 +129,15 @@ local update_queue = {} :: { Node<any> }
local function rec<T>(node: StartNode<T>)
if not node.children then return end
for child in next, node.children do
table.insert(update_queue, child)
rec(child)
local children = node.children :: {}
while children[1] do
table.insert(update_queue, children[1])
rec(children[1])
unparent(children[1])
end
table.clear(node.children)
table.clear(children)
end
local function update<T>(node: StartNode<T>)
@ -156,11 +171,10 @@ local function create_node<T>(value: T): Node<T>
cache = value,
effect = false,
cleanups = false :: false,
parents = {},
children = false :: false
}
active[node] = true
return node
end
@ -169,7 +183,7 @@ local function get_children<T>(node: Node<T>): { Node<unknown> }
local children = {}
for child in node.children do
for _, child in node.children do
table.insert(children, child)
end

View file

@ -103,7 +103,7 @@ TEST("graph", function()
CHECK(count == 3)
end
do CASE "diamond problem"
do CASE "diamond graph"
local a, b, c, d = node(), node(), node(), node()
local b_cnt, c_cnt, d_cnt = 0, 0, 0
@ -119,7 +119,7 @@ TEST("graph", function()
CHECK(b_cnt == 1)
CHECK(c_cnt == 1)
CHECK(d_cnt == 2) -- confirm current behavior
CHECK(d_cnt == 1)
end
do CASE "duplicate child on rerun"
@ -387,6 +387,36 @@ TEST("derive()", wrap_root(function()
CHECK(count == 4)
end
do CASE "conditional derive"
local a = source(false)
local b = source(false)
local c = derive(function()
return
if a() then "a"
elseif b() then "b"
else "never"
end)
local count = 0
watch(function() count += 1 end)
b(true)
CHECK(c() == "b")
CHECK(count == 2)
a(true)
CHECK(c() == "a")
CHECK(count == 3)
b(false)
CHECK(count == 3)
b(true)
CHECK(count == 3)
a(false)
CHECK(c() == "b")
CHECK(count == 4)
end
do CASE "garbage collection"
-- check that `b` does not allow gc of `a`
local a = source(1)