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
|
----
-- 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 update
-- anything on \tick.
make_ref: =>
RTNode result: @result, side_inputs: @side_inputs
--- poll all IOStream instances that are effecting this (sub)tree.
-- should be called once per frame on the root, right before tick.
poll_io: =>
for result, input in pairs @side_inputs
result\poll! if input.mode == 'io'
--- 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 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 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 {}
if params.side_inputs
@side_inputs = params.side_inputs
return
@side_inputs, is_child = {}, {}
for child in *@children
for result, input in pairs child.side_inputs
@side_inputs[result] = input
if child.result
is_child[child.result] = true
if @op
for i in @op\all_inputs!
if i.mode == 'io' or (i.mode == 'hot' and not is_child[i.result])
@side_inputs[i.result] = i
if @result
if next @side_inputs
assert @result.metatype != '=', "Const result has side_inputs"
elseif @result.metatype == '~'
@result = @result.type\mk_const @result\unwrap!
{
:RTNode
}
|