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
|
import base from require 'core.cycle'
-- Result of evaluating an expression
-- carries (all optional):
-- - a Value
-- - an Op (to update)
-- - children (results of subexpressions that were evaluated)
-- - cached list of all Dispatchers affecting all Ops in the subtree
--
-- Results form a tree that controls execution order and message passing
-- between Ops.
class Result
-- params: table with optional keys op, value, children
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
is_const: => not next @side_inputs
-- asserts value-constness and returns the value
const: (msg) =>
assert not (next @side_inputs), msg or "eval-time const expected"
@value
-- asserts a value exists and returns its type
type: =>
assert @value, "Result with value expected"
@value.type
-- create a value-copy of this result that has the same impulses but without
-- affecting the original's update logic
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 == base.IOInput
io = input!
io\tick!
-- in depth-first order, tick all Ops who have dirty Stream inputs or impulses
--
-- short-circuits if there are no dirty Streams 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 streams 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 streams from child
-- expressions might have changed
self_dirty = false
for stream in @op\all_inputs!
if stream\dirty!
self_dirty = true
break
L\trace "#{@op} is #{if self_dirty then 'dirty' else 'clean'}"
return unless self_dirty
@op\tick!
-- static
__tostring: =>
buf = "<result=#{@value}"
buf ..= " #{@op}" if @op
buf ..= " (#{#@children} children)" if #@children > 0
buf ..= ">"
buf
{
:Result
}
|