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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
----
-- RTNode of evaluating an expression.
--
-- `RTNode`s form a tree that controls execution order and message
-- between `Op`s.
--
-- @classmod RTNode
import Error from require 'alv.error'
class RTNode
--- members
-- @section members
--- return whether this RTNode's result is const.
is_const: => not next @side_inputs
--- assert result-constness and return the result.
-- @tparam[opt] string msg the error message to throw
-- @treturn any
const: (msg) =>
assert not (next @side_inputs), msg or "eval-time const expected"
@result
--- assert this result has a result, return its type.
-- @treturn string
type: =>
assert @result, "RTNode with result expected"
@result.type
--- assert this result has a result, returns its metatype.
-- @treturn string `"result"` or `"event"`
metatype: =>
assert @result, "RTNode with result expected"
@result.metatype
--- create a copy of this result with result-copy semantics.
--
-- the copy has the same @result and @side_inputs, but will not (doubly)update
-- anything on `tick` or `poll`.
make_ref: =>
RTNode result: @result, side_inputs: @side_inputs
--- poll all IO Ops effecting this (sub)tree for changes.
--
-- should be called once per frame on the root, right before tick.
--
-- @treturn ?boolean whether any IO Op marked itself dirty.
poll_io: =>
dirty = false
for op in *@io_ops
dirty = op\poll! or dirty
dirty
--- in depth-first order, tick all Ops which have dirty Inputs.
--
-- short-circuits if there are no dirty Inputs in the entire subtree
tick: =>
if #@io_ops == 0
any_dirty = false
for result, 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 input in @op\all_inputs!
if input\dirty!
self_dirty = true
return unless self_dirty
Error.wrap "ticking #{@op}", @op\tick
__tostring: =>
buf = "<RT=#{@result}"
buf ..= " #{@op}" if @op
buf ..= " (#{#@children} children)" if #@children > 0
buf ..= ">"
buf
--- the `Result` result.
-- @tfield ?Result result
--- an Op.
-- @tfield ?Op op
--- list of child `RTNode`s from subexpressions.
-- @tfield {RTNode,...} children
--- cached list of all IO Ops inside this RTNode and children.
-- @tfield {Op,...} io_ops
--- cached mapping of all `Result`/`Input` pairs affecting this RTNode.
--
-- This is the union of all `children`s `side_inputs` and all `Input`s from
-- `op` that are not the `result` of any child.
--
-- @tfield {[Result]=Input,...} side_inputs
--- static functions
-- @section static
--- create a new RTNode.
-- @classmethod
-- @param params table with optional keys op, result, children. default: {}
new: (params={}) =>
@result = params.result
@op = params.op
@children = params.children or {}
@io_ops = params.io_ops or {}
if params.side_inputs
@side_inputs = params.side_inputs
return
@side_inputs = {}
is_child = {}
for child in *@children
-- collect child side_inputs
for result, input in pairs child.side_inputs
@side_inputs[result] = input
-- collect child io_ops
for op in *child.io_ops
table.insert @io_ops, op
if child.result
is_child[child.result] = true
if @op
if @op.poll
table.insert @io_ops, @op
-- find inputs outside this tree
for i in @op\all_inputs!
if i.mode == 'hot' and not is_child[i.result]
@side_inputs[i.result] = i
-- "freeze" ~-streams if there are no IO Ops around
return unless @result and @result.metatype == '~'
return if #@io_ops > 0
all_const = true
for result, input in pairs @side_inputs
if result.metatype != '='
all_const = false
break
return unless all_const
@result = @result.type\mk_const @result\unwrap!
{
:RTNode
}
|