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