aboutsummaryrefslogtreecommitdiffstats
path: root/alv/rtnode.moon
blob: 0639e3eecfc614ba975dd8fb9093cbe8a57e0789 (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
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
}