This commit is contained in:
Aaron Smith 2023-08-10 13:18:16 +01:00
parent 2050c2585f
commit cc10c80a90
10 changed files with 174 additions and 220 deletions

View file

@ -1,5 +1,6 @@
if not game then script = require "test/wrap-require" end
local throw = require(script.Parent.throw)
local flags = require(script.Parent.flags)
export type Node<T> = {
@ -13,28 +14,30 @@ local reff = false
local refs = {} :: { Node<unknown> }
local WEAK_VALUES_RESIZABLE = { __mode = "vs" }
local EVALUATION_ERR = "error while evaluating node:\n\n"
local EVALUATION_ERR = "error while evaluating source:\n\n"
setmetatable(refs :: any, WEAK_VALUES_RESIZABLE)
local check_for_yield do
local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
local t = { __mode = "kv" }
setmetatable(t, t)
check_for_yield = function<T..., U...>(fn: (T...) -> (), ...: any)
check_for_yield = function(fn, ...: any)
local args = { ... }
t.__unm = function()
t.__unm = function(_)
fn(unpack(args))
end
local ok, err = pcall(function()
return -t :: any
local _ = -t
end)
if not ok then
if err == "attempt to yield across metamethod/C-call boundary" or err == "thread is not yieldable" then
error(EVALUATION_ERR .. "cannot yield when deriving node in watcher", 3)
throw(EVALUATION_ERR .. "cannot yield when deriving node in watcher")
else
error(EVALUATION_ERR..err, 3)
throw(EVALUATION_ERR .. err)
end
end
end
@ -45,10 +48,16 @@ local function set_effect<T>(node: Node<unknown>, fn: (T) -> (), key: T)
end
local function run_effects(node: Node<unknown>)
for effect, key in next, node.effects do
if flags.strict then effect(key) end
effect(key)
end
if flags.strict then
for effect, key in next, node.effects do
effect(key)
effect(key)
end
else
for effect, key in next, node.effects do
effect(key)
end
end
end
-- retrieves a node's cached value
@ -71,8 +80,10 @@ end
local function update(node: Node<unknown>)
run_effects(node)
if node.children then
local strict = flags.strict
for _, child in node.children do
if flags.strict then check_for_yield(child.derive) end
if strict then check_for_yield(child.derive) end
child.cache = child.derive()
update(child)
end
@ -108,7 +119,7 @@ local function capture<T, U>(fn: (U?) -> T, arg: U?): ({ Node<unknown> }, T)
reff = false
if not ok then error("error while detecting watcher: " .. result :: string, 0) end
if not ok then throw(EVALUATION_ERR .. result :: string) end
return refs, result :: T
end