aboutsummaryrefslogtreecommitdiffstats
path: root/alv/base/input.moon
blob: 87e00de24c65ae233a35e229fbb93c4c6346570a (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
----
-- Update scheduling policy for `Op` arguments.
--
-- @classmod Input
import Constant, SigStream, EvtStream from require 'alv.result'
import RTNode from require 'alv.rtnode'

inherits = (klass, frm) ->
  assert klass, "cant find the ancestor of nil"
  return true if klass == frm
  while klass.__parent
    return true if klass.__parent == frm
    klass = klass.__parent
  false

match_parent = (inst, map) ->
  klass = assert inst and inst.__class, "not an instance"
  if key = map[klass]
    return key

  while klass.__parent
    if key = map[klass.__parent]
      return key

    klass = klass.__parent

local ColdInput, ValueInput, IOInput, mapping

class Input
--- Input interface.
--
-- Methods that have to be implemented by `Input` implementations.
-- @section interface

  --- create a new Input.
  --
  -- @classmethod
  -- @tparam Result result
  new: (@result) =>
    assert @result, "nil passed to Input: #{value}"

  --- copy state from old instance (optional).
  --
  -- called by `Op:setup` with another `Input` instance or `nil` once this instance is
  -- registered. Must prepare this instance for `dirty`.
  --
  -- May enter a 'setup state' that is exited using `finish_setup`.
  --
  -- @tparam ?Input prev previous `Input` intance or nil
  setup: (prev) =>

  --- whether this input requires processing (optional).
  --
  -- must return a boolean indicating whether `Op`s that refer to this instance
  -- should be notified (via `Op:tick`). If not overwritten, delegates to
  -- `result`:@{SigStream:dirty|dirty}.
  --
  -- @treturn bool whether processing is necessary
  dirty: => @result\dirty!

  --- leave setup state (optional).
  --
  -- called after the `Op` has completed (or skipped) its first `Op:tick` after
  -- `Op:setup`. Must prepare this instance for dataflow operation.
  finish_setup: =>

  --- unwrap to Lua value (optional).
  --
  -- @treturn any the raw Lua value
  unwrap: => @result\unwrap!

  --- return the type name of this `Input` (optional).
  type: => @result.type

  --- return the metatype name of this `Input` (optional).
  metatype: => @result.metatype

  --- the current value
  -- @tfield Result result

  --- the Input mode
  -- @tfield string mode `'hot'`, `'cold'` or `'io'`.
  mode: 'hot'

--- members
-- @section members

  --- alias for `unwrap`.
  __call: => @result\unwrap!

  __tostring: => "#{@@__name}:#{@result}"
  __inherited: (cls) =>
    cls.__base.__call = @__call
    cls.__base.__tostring = @__tostring

--- static functions
-- @section static

  --- Create a `cold` `Input`.
  --
  -- Never marked dirty. Use this for input results that are only read when
  -- another `Input` is dirty.
  --
  -- @tparam Result|RTNode value
  @cold: (value) ->
    if value.__class == RTNode
      value = assert value.result, "Input from node without result!"
    ColdInput value

  --- Create a `hot` `Input`.
  --
  -- Behaviour depends on what kind of `Result` `value` is:
  --
  -- - `Constant`: treated like `cold`.
  -- - `SigStream`: Marked dirty only if old and new `result` differ.
  -- - `EvtStream`: Marked dirty only if the current `result` is dirty.
  --
  -- This is the most common `Input` strategy.
  --
  -- @tparam Result|RTNode value
  @hot: (value) ->
    if value.__class == RTNode
      value = assert value.result, "Input from node without result!"

    InputType = match_parent value, mapping
    assert InputType, "Input from unknown value: #{value}"
    InputType value

class ColdInput extends Input
  mode: 'cold'
  dirty: => false

class ValueInput extends Input
  new: (result) =>
    super result
    @mode = if @result.metatype == '=' then 'cold' else 'hot'

  setup: (old) =>
    @dirty_setup = not old or @result != old.result

  finish_setup: => @dirty_setup = nil

  dirty: =>
    return @dirty_setup if @dirty_setup != nil
    @result\dirty!

class IOInput extends Input
  mode: 'io'

mapping = {
  [Constant]: ValueInput
  [SigStream]: ValueInput
  [EvtStream]: Input
}

{
  :Input
}