aboutsummaryrefslogtreecommitdiffstats
path: root/alv/type.moon
blob: d3af642ba8fe3c47218532d801b0adb8b8c1ea5d (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
-----
--- Type definitions (`type.Type` and implementations).
--
-- @module type
import opairs from require 'alv.util'
import result from require 'alv.cycle'
import Error from require 'alv.error'

shared_shape = (a, b) ->
  for key in pairs a
    return false unless b[key]

  for key in pairs b
    return false unless a[key]

  true

same = (a, b) ->
  return unless shared_shape a, b
  for key, val in pairs a
    return false unless val == b[key]

  true

local Primitive

--- Magic table containing all `Primitive` types.
--
-- When indexed with a string returns a (cached) instance of that type.
--
-- @table T
T = setmetatable {}, __index: (key) =>
  return key if key.__class

  with type = Primitive key
    rawset @, key, type

--- Base class for types.
-- @type Type
class Type
  --- pretty-print a value of this type.
  -- @function pp
  -- @tparam any value
  -- @tparam[opt] bool raw whether to print "raw" (strings without quotes)
  -- @treturn string

  --- check two values of this type for equality.
  -- @function eq
  -- @tparam any a
  -- @tparam any b
  -- @treturn bool

  --- index into this type or throw a error.
  -- @function get
  -- @tparam any key
  -- @treturn Type

  --- create a `SigStream` of this type.
  -- @tparam ?any init initial value
  -- @treturn SigStream
  mk_sig: (init) =>
    result.SigStream @, init

  --- create a `EvtStream` of this type.
  -- @treturn EvtStream
  mk_evt: =>
    result.EvtStream @

  --- create a `Constant` of this type.
  -- @tparam any val value
  -- @treturn Constant
  mk_const: (val) =>
    result.Constant @, val

--- Primitive type.
--
-- Extends `Type`.
--
-- @type Primitive
class Primitive extends Type
  pp: (value, raw) =>
    return tostring value if raw

    switch @name
      when 'str'
        string.format '%q', value
      else
        tostring value

  eq: (a, b) => a == b

  get: (key) => error "cannot index into Primitive type '#{@}'"

  __eq: (other) => @name == other.name
  __tostring: => @name

  --- instantiate a Primitive type.
  -- @classmethod
  -- @tparam string name the typename
  new: (@name) =>
    assert (type @name) == 'string', "Typename has to be a string: '#{@name}'"

  --- the type's unique name.
  -- @tfield string name

--- Struct/Hashmap type.
--
-- Extends `Type`.
--
-- @type Struct
class Struct extends Type
  pp: (value) =>
    inner = table.concat ["#{k}: #{@types[k]\pp v}" for k, v in opairs value], ' '
    "{#{inner}}"

  eq: (a, b) =>
    return false unless (type a) == (type b)
    return true if a == b
    for key, type in pairs @types
      if not type\eq a[key], b[key]
        return false
    true

  get: (key) =>
    assert @types[key], Error 'index', "#{@} has no '#{key}' key"

  --- iterate over contained keys and types
  -- each iteration, returns a key and associated type
  -- @treturn string key
  -- @treturn Type associated type
  iter_keys: => next, @types, nil

  --- create a new struct type with a subset of keys.
  project: (keys) =>
    types = {}
    for key in *keys
      types[key] = @types[key]
    @@ types

  __eq: (other) => other.__class == Struct and same @types, other.types
  __tostring: =>
    inner = table.concat ["#{k}: #{v}" for k, v in opairs @types], ' '
    "{#{inner}}"

  --- instantiate a Primitive type.
  -- @classmethod
  -- @tparam {string=Type} types
  new: (@types) =>

  --- the shape and field types of the struct.
  -- @tfield {string=Type} types

--- Array type.
--
-- Extends `Type`.
--
-- @type Array
class Array extends Type
  pp: (value) =>
    inner = table.concat [@type\pp v for v in *value], ' '
    "[#{inner}]"

  eq: (a, b) =>
    return false unless (type a) == (type b)
    for i=1, @size
      if not @type\eq a[i], b[i]
        return false
    true

  get: (key) =>
    assert (type key) == 'number'
    assert key >= 0 and key < @size, Error 'index', "index '#{key}' out of range!"
    @type

  array_next = (i) => if i < @size then i + 1, @type
  --- iterate over contained types
  -- each iteration, returns a key and associated type
  -- @treturn number key
  -- @treturn Type associated type
  iter_keys: => array_next, @, 0

  __eq: (other) => other.__class == Array and @size == other.size and @type == other.type
  __tostring: => "#{@type}[#{@size}]"

  --- instantiate an Array type.
  -- @classmethod
  -- @tparam number size
  -- @tparam Type type
  new: (@size, @type) =>

  --- the number of elements in this array.
  -- @tfield number size

  --- the element type of this array.
  -- @tfield Type type

{
  :Type
  :T
  :Primitive
  :Array
  :Struct
}