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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
----
-- Result of evaluating an expression.
--
-- `Result`s form a tree that controls execution order and message passing
-- between `Op`s.
--
-- @classmod Result
class Result
--- members
-- @section members
--- return whether this Result's value is const.
is_const: => not next @side_inputs
--- assert value-constness and returns the value.
-- @tparam[opt] string msg the error message to throw
const: (msg) =>
assert not (next @side_inputs), msg or "eval-time const expected"
@value
--- assert this result has a value, returns its type.
type: =>
assert @value, "Result with value expected"
@value.type
--- create a copy of this result with value-copy semantics.
-- the copy has the same @value and @side_inputs, but will not update
-- anything on \tick.
make_ref: =>
with Result value: @value
.side_inputs = @side_inputs
--- tick all IO instances that are effecting this (sub)tree.
-- should be called once per frame on the root, right before tick.
tick_io: =>
for stream, input in pairs @side_inputs
if input.__class.__name == "IOInput"
io = input!
io\tick!
--- in depth-first order, tick all Ops which have dirty Inputs.
--
-- short-circuits if there are no dirty Inputs in the entire subtree
tick: =>
any_dirty = false
for stream, input in pairs @side_inputs
if input\dirty!
any_dirty = true
break
-- early-out if no Inputs are dirty in this whole subtree
return unless any_dirty
for child in *@children
child\tick!
if @op
-- we have to check self_dirty here, because Inputs from children may
-- have become dirty due to \tick
self_dirty = false
for stream in @op\all_inputs!
if stream\dirty!
self_dirty = true
return unless self_dirty
@op\tick!
__tostring: =>
buf = "<result=#{@value}"
buf ..= " #{@op}" if @op
buf ..= " (#{#@children} children)" if #@children > 0
buf ..= ">"
buf
--- the `Value` result
--
-- @tfield ?Value value
--- an Op
--
-- @tfield ?Op op
--- list of child `Result`s from subexpressions
--
-- @tfield {}|{Result,...} children
--- cached mapping of all `Value`/`Input` pairs affecting this Result.
--
-- This is the union of all `children`s `side_inputs` and all `Input`s from
-- `op` that are not the `value` of any child.
--
-- @tfield {[Value]=Input,...} side_inputs
--- static functions
-- @section static
--- create a new Result.
-- @classmethod
-- @param params table with optional keys op, value, children. default: {}
new: (params={}) =>
@value = params.value
@op = params.op
@children = params.children or {}
@side_inputs, is_child = {}, {}
for child in *@children
for s, d in pairs child.side_inputs
@side_inputs[s] = d
if child.value
is_child[child.value] = true
if @op
for input in @op\all_inputs!
if input.impure or not is_child[input.stream]
@side_inputs[input.stream] = input
{
:Result
}
|