aboutsummaryrefslogtreecommitdiffstats
path: root/core/result.moon
blob: 7fda8e5aea4f6fccfe97a24a9e783c6cb9abc8c9 (plain)
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
}