Skip to content

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
centau committed Jul 30, 2023
1 parent 1aa7431 commit 676bbbc
Show file tree
Hide file tree
Showing 15 changed files with 661 additions and 869 deletions.
41 changes: 0 additions & 41 deletions src/Event.lua

This file was deleted.

53 changes: 49 additions & 4 deletions src/apply.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,57 @@

if not game then script = (require :: any) "test/wrap-require" end

local applyProperties = require(script.Parent.applyProperties)
local graph = require(script.Parent.graph)
type Node<T> = graph.Node<T>

local function apply<T>(instance: T & Instance)
return function(properties: { [any]: unknown }): T
return applyProperties(instance, properties)
local throw = require(script.Parent.throw)
local bind = require(script.Parent.bind)

local function recurse(instance: Instance, properties: { [unknown]: unknown }, event_buffer)
for property, value in properties do
if type(value) == "table" then
recurse(instance, value :: {}, event_buffer)
elseif type(property) == "string" then
if type(value) == "function" then
if typeof((instance :: any)[property] == "RBXScriptSignal") then
event_buffer[property] = value
else
bind.property(instance, property, value :: () -> ())
end
else
(instance :: any)[property] = value
end
elseif type(property) == "number" then
if type(value) == "function" then
bind.children(instance, value :: () -> { Instance })
else
(value :: Instance).Parent = instance
end
end
end
end

local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
local parent: unknown = properties.Parent
if parent then properties.Parent = nil end

local event_buffer: { [string]: () -> () } = {} -- connect events after setting properties

recurse(instance, properties, event_buffer)

for event, fn in next, event_buffer do
(instance :: any)[event]:Connect(fn)
end

if parent then
if type(parent) == "function" then
error("cannot set parent to state")
else
instance.Parent = parent :: Instance
end
end

return instance
end

return apply
66 changes: 0 additions & 66 deletions src/applyProperties.lua

This file was deleted.

75 changes: 34 additions & 41 deletions src/bind.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ end


local graph = require(script.Parent.graph)
type State<T> = graph.State<T>
type Node<T> = graph.Node<T>
local get = graph.get
local setEffect = graph.setEffect
local set_effect = graph.set_effect
local capture = graph.capture

local throw = require(script.Parent.throw)
local flags = require(script.Parent.flags)
Expand All @@ -22,15 +23,18 @@ local hold: { Instance? } = {}
local weak: { Instance? } = setmetatable({}, { __mode = "v" }) :: any
local bindcount = 0





local srcs do
local src1 = debug.info(1, "s")
local srctrunc = string.sub(src1, 1, #src1-4)

srcs = {
src1,
srctrunc .. "applyProperties",
srctrunc .. "apply",
srctrunc .. "create",
srctrunc .. "apply"
}
end

Expand All @@ -43,26 +47,30 @@ local function traceback() -- ensures trace begins outside of any vide library f
return debug.traceback("", s)
end

function setup(state: State<any>, instance: Instance, updateInstance: (Instance) -> ())
function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) -> ())
if flags.strict then
local fn = updateInstance
local fn = setter
local trace = traceback()
updateInstance = function(instance)
setter = function(instance)
local ok, err: string? = pcall(fn, instance)
if not ok then warn(`error occured updating state binding:\n{err}\nset from:{trace}`) end
end
end

updateInstance(instance)
setEffect(state, updateInstance, instance)
local nodes = table.clone((capture(setter :: (Instance) -> unknown, instance)))

for _, node in next, nodes do
set_effect(node, setter, instance)
end


bindcount += 1
local key = bindcount

weak[key] = instance

local function ref()
local _ = state -- prevent gc of state while instance exists
local _ = nodes -- prevent gc of state while instance exists
local instance = weak[key] :: Instance
hold[key] = instance.Parent and instance or nil -- prevent gc of instance while parented
end
Expand All @@ -71,28 +79,28 @@ function setup(state: State<any>, instance: Instance, updateInstance: (Instance)
instance:GetPropertyChangedSignal("Parent"):Connect(ref)
end

local function bindProperty(state: State<unknown>, instance_STRONG: Instance, property: string)
setup(state, instance_STRONG, function(instance)
(instance :: any)[property] = get(state)
local function bind_property(instance: Instance, property: string, fn: () -> unknown)
setup(instance, fn, function(instance_weak: any)
instance_weak[property] = fn()
end)
end

local function bindParent(state: State<Instance?>, instance_STRONG)
instance_STRONG.Destroying:Connect(function()
instance_STRONG = nil :: any -- allow gc when destroyed
local function bind_parent(instance: Instance, fn: () -> Instance?)
instance.Destroying:Connect(function()
instance= nil :: any -- allow gc when destroyed
end)
setup(state, instance_STRONG, function(instance)
local _ = instance_STRONG -- state will strongly reference instance when parent is bound
instance.Parent = get(state)
setup(instance, fn, function(instance)
local _ = instance -- state will strongly reference instance when parent is bound
instance.Parent = fn()
end)
end

local function bindChildren(state: State<{ Instance }?>, parent_STRONG: Instance)
local function bind_children(parent: Instance, fn: () -> { Instance })
local currentChildrenSet: { [Instance]: true } = {} -- cache of all children parented before update
local newChildrenSet: { [Instance]: true } = {} -- cache of all children parented after update

setup(state, parent_STRONG, function(parent)
local newChildren = get(state) -- all (and only) children that should be parented after this update
setup(parent, fn, function(parent_weak)
local newChildren = fn() -- all (and only) children that should be parented after this update
if newChildren and type(newChildren) ~= "table" then
throw(`Cannot parent instance of type { type(newChildren) } `)
end
Expand All @@ -101,7 +109,7 @@ local function bindChildren(state: State<{ Instance }?>, parent_STRONG: Instance
for _, child in next, newChildren do
newChildrenSet[child] = true -- record child set from this update
if not currentChildrenSet[child] then
child.Parent = parent -- if child wasn't already parented then parent it
child.Parent = parent_weak -- if child wasn't already parented then parent it
else
currentChildrenSet[child] = nil -- remove child from cache if it was already in cache
end
Expand All @@ -117,23 +125,8 @@ local function bindChildren(state: State<{ Instance }?>, parent_STRONG: Instance
end)
end

local function bindEvent(state: State<() -> ()?>, instance_STRONG: Instance, event: RBXScriptSignal)
local current: RBXScriptConnection? = nil
setup(state, instance_STRONG, function(instance)
if current then
current:Disconnect()
current = nil
end
local listener = get(state)
if listener then
current = event:Connect(listener)
end
end)
end

return {
property = bindProperty,
parent = bindParent,
children = bindChildren,
event = bindEvent
property = bind_property,
parent = bind_parent,
children = bind_children,
}
6 changes: 3 additions & 3 deletions src/create.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ end

local throw = require(script.Parent.throw)
local defaults = require(script.Parent.defaults)
local applyProperties = require(script.Parent.applyProperties)
local apply = require(script.Parent.apply)
local memoize = require(script.Parent.memoize)

local function createInstance(className: string)
Expand All @@ -24,15 +24,15 @@ local function createInstance(className: string)
end

return function(properties: { [any]: unknown }): Instance
return applyProperties(instance:Clone(), properties)
return apply(instance:Clone(), properties)
end
end; createInstance = memoize(createInstance)

local function cloneInstance(instance: Instance)
return function(properties: { [any]: unknown }): Instance
local clone = instance:Clone()
if not clone then error("Attempt to clone a non-archivable instance", 3) end
return applyProperties(clone, properties)
return apply(clone, properties)
end
end

Expand Down
20 changes: 10 additions & 10 deletions src/derive.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@
if not game then script = (require :: any) "test/wrap-require" end

local graph = require(script.Parent.graph)
type State<T> = graph.State<T>
type Unwrapper = graph.Unwrapper
local create = graph.create
local captureAndLink = graph.captureAndLink
local get = graph.get
local capture_and_link = graph.capture_and_link

local function derive<T>(deriveValue: (Unwrapper) -> T, cleanup: (T) -> ()?): State<T>
local function derive<T>(fn: () -> T, cleanup: (T) -> ()?): () -> T
local node = create((nil :: any) :: T)

if cleanup then
local fn = deriveValue
local f = fn
local last: T? = nil
deriveValue = function(from)
fn = function()
if last ~= nil then cleanup(last) end
last = fn(from)
last = f()
return last :: T
end
end

local value: T = captureAndLink(node, deriveValue)
rawset(node, "__cache", value)
node.cache = capture_and_link(node, fn)

return node :: State<T>
return function()
return get(node)
end
end

return derive
Loading

0 comments on commit 676bbbc

Please sign in to comment.