-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathformula.luau
70 lines (63 loc) · 1.69 KB
/
formula.luau
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
local FENV_KEY = "74709086-5669-43dc-a7d1-195d71ae0464"
local function callFormulaBody(cellData, body, recompute)
cellData.seen = {}
getfenv(0)[FENV_KEY] = cellData
cellData.value = body()
getfenv(0)[FENV_KEY] = nil
cellData.seen = nil
end
local function recompute(d)
for _, unsubscribe in d.unsubscribes do
unsubscribe()
end
table.clear(d.dependents)
table.clear(d.roots)
table.clear(d.unsubscribes)
callFormulaBody(d, d.body, recompute)
end
local function metaIndex(self, key)
if key == "value" then
local ourData = getmetatable(self)._data
local d = getfenv(0)[FENV_KEY]
if d and d.seen and not d.seen[self] then
d.seen[self] = true
ourData.dependents[d] = true
table.insert(d.unsubscribes, function()
ourData.dependents[d] = nil
end)
for root in ourData.roots do
d.roots[root] = true
end
end
return ourData.value
else
error(string.format('Cannot get key %q from a cell, only valid field is "value"', key), 2)
end
end
local function metaNewIndex(self, key, value)
error("Cannot set the value of a derived cell", 2)
end
local function metaToString(self)
local ourData = getmetatable(self)._data
return tostring(ourData.value)
end
local function formula<T>(body: () -> T): { value: T }
local cell = newproxy(true)
local mt = getmetatable(cell)
mt.__index = metaIndex
mt.__newindex = metaNewIndex
mt.__tostring = metaToString
local data = {
proxy = cell, -- Store a reference to the proxy so it doesn't get garbage collected.
body = body,
recompute = recompute,
seen = {},
unsubscribes = {},
dependents = setmetatable({}, { __mode = "k" }),
roots = {},
}
mt._data = data
callFormulaBody(data, body, recompute)
return cell
end
return formula