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
|
-----
--- 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) =>
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
-- @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) =>
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
}
|