rename array/struct modules to avoid name conflict
s-ol
1 year, 14 days ago
0 | import Array, Op, PureOp, Builtin, Constant, Error, const, sig, evt, T from require 'alv.base' | |
1 | import Cell from require 'alv.cell' | |
2 | import Tag from require 'alv.tag' | |
3 | builtins = require 'alv.builtins' | |
4 | ||
5 | unpack or= table.unpack | |
6 | any = sig! / evt! | |
7 | ||
8 | get = Constant.meta | |
9 | meta: | |
10 | name: 'get' | |
11 | summary: "Index into an array." | |
12 | examples: { '(get array i)' } | |
13 | description: "Get the value at index `i` (starting at 0). | |
14 | ||
15 | `i` has to be a constant expression." | |
16 | ||
17 | value: class extends PureOp | |
18 | pattern: any + const.num | |
19 | type: (inputs) => | |
20 | { array, i } = inputs | |
21 | array\type!\get i.result! | |
22 | ||
23 | tick: => | |
24 | { array, i } = @unwrap_all! | |
25 | @out\set array[i + 1] | |
26 | ||
27 | set = Constant.meta | |
28 | meta: | |
29 | name: 'set' | |
30 | summary: "Update a value in an array." | |
31 | examples: { '(set array i val)' } | |
32 | description: "Set the value for `i` to `val`. | |
33 | ||
34 | `i` has to be a constant expression. This is a pure op, so at most one of | |
35 | `array` and `val` may be a !-stream." | |
36 | ||
37 | value: class extends PureOp | |
38 | pattern: any + const.num + any | |
39 | type: (inputs) => | |
40 | { array, i, val } = inputs | |
41 | type = array\type! | |
42 | expected = type\get i.result! | |
43 | ||
44 | if expected ~= val\type! | |
45 | msg = string.format "expected value of type %s, not %s", | |
46 | expected, val\type! | |
47 | error Error 'argument', msg | |
48 | ||
49 | type | |
50 | ||
51 | tick: => | |
52 | { array, key, val } = @unwrap_all! | |
53 | ||
54 | array = [v for v in *array] | |
55 | array[key + 1] = val | |
56 | ||
57 | @out\set array | |
58 | ||
59 | head = Constant.meta | |
60 | meta: | |
61 | name: 'head' | |
62 | summary: "Get the first element from an array." | |
63 | examples: { '(head array)' } | |
64 | ||
65 | value: class extends PureOp | |
66 | pattern: any*1 | |
67 | type: (inputs) => | |
68 | type = inputs[1]\type! | |
69 | ||
70 | assert type.__class == Array, Error 'argument', "expected an Array" | |
71 | assert type.size > 0, Error 'argument', "cannot get head of empty Array" | |
72 | ||
73 | type.type | |
74 | ||
75 | tick: => | |
76 | { array } = @unwrap_all! | |
77 | @out\set array[1] | |
78 | ||
79 | tail = Constant.meta | |
80 | meta: | |
81 | name: 'tail' | |
82 | summary: "Get everything except the first element from an array." | |
83 | examples: { '(tail array)' } | |
84 | ||
85 | value: class extends PureOp | |
86 | pattern: any*1 | |
87 | type: (inputs) => | |
88 | type = inputs[1]\type! | |
89 | ||
90 | assert type.__class == Array, Error 'argument', "expected an Array" | |
91 | assert type.size > 0, Error 'argument', "cannot get tail of empty Array" | |
92 | ||
93 | Array type.size - 1, type.type | |
94 | ||
95 | tick: => | |
96 | { array } = @unwrap_all! | |
97 | @out\set [v for v in *array[2,]] | |
98 | ||
99 | prepend = Constant.meta | |
100 | meta: | |
101 | name: 'prepend' | |
102 | summary: "Prepend a new value at the start of an array." | |
103 | examples: { '(prepend array val)' } | |
104 | description: "Prepend `val` to `array` at index `0`, moving other values back. | |
105 | ||
106 | This is a pure op, so at most one of `array` and `val` may be a !-stream." | |
107 | ||
108 | value: class extends PureOp | |
109 | pattern: any + any | |
110 | type: (inputs) => | |
111 | { array, val } = inputs | |
112 | type = array\type! | |
113 | ||
114 | if val\type! ~= type.type | |
115 | msg = string.format "expected value of type %s, not %s", | |
116 | type.type, val\type! | |
117 | error Error 'argument', msg | |
118 | ||
119 | Array type.size + 1, type.type | |
120 | ||
121 | tick: => | |
122 | { array, val } = @unwrap_all! | |
123 | ||
124 | array = [v for v in *array] | |
125 | table.insert array, 1, val | |
126 | ||
127 | @out\set array | |
128 | ||
129 | insert = Constant.meta | |
130 | meta: | |
131 | name: 'insert' | |
132 | summary: "Insert a new value into an array." | |
133 | examples: { '(insert array i val)' } | |
134 | description: "Insert `val` into `array` at `i`, moving other values back if | |
135 | necessary. | |
136 | ||
137 | `i` has to be a constant expression. This is a pure op, so at most one of | |
138 | `array` and `val` may be a !-stream." | |
139 | ||
140 | value: class extends PureOp | |
141 | pattern: any + const.num + any | |
142 | type: (inputs) => | |
143 | { array, i, val } = inputs | |
144 | type = array\type! | |
145 | i = i.result! | |
146 | ||
147 | if i > type.size or i < 0 | |
148 | error Error 'argument', "index '#{i}' out of range!" | |
149 | if val\type! ~= type.type | |
150 | msg = string.format "expected value of type %s, not %s", | |
151 | type.type, val\type! | |
152 | error Error 'argument', msg | |
153 | ||
154 | Array type.size + 1, type.type | |
155 | ||
156 | tick: => | |
157 | { array, i, val } = @unwrap_all! | |
158 | ||
159 | array = [v for v in *array] | |
160 | table.insert array, i + 1, val | |
161 | ||
162 | @out\set array | |
163 | ||
164 | remove = Constant.meta | |
165 | meta: | |
166 | name: 'remove' | |
167 | summary: "Remove a value from an Array." | |
168 | examples: { '(remove array i)' } | |
169 | description: "Removes the value at index `i` from `array`. | |
170 | ||
171 | `i` has to be a constant expression." | |
172 | ||
173 | value: class extends PureOp | |
174 | pattern: any + const.num | |
175 | type: (inputs) => | |
176 | { array, i } = inputs | |
177 | type = array\type! | |
178 | ||
179 | -- check index range | |
180 | type\get i.result! | |
181 | ||
182 | Array type.size - 1, type.type | |
183 | ||
184 | tick: => | |
185 | { array, i, val } = @unwrap_all! | |
186 | ||
187 | array = [v for v in *array] | |
188 | table.remove array, i + 1 | |
189 | ||
190 | @out\set array | |
191 | ||
192 | size = Constant.meta | |
193 | meta: | |
194 | name: 'size' | |
195 | summary: "Get the size of an array." | |
196 | examples: { '(size array)' } | |
197 | ||
198 | value: class extends Op | |
199 | setup: (inputs) => | |
200 | super {} | |
201 | ||
202 | assert #inputs == 1, Error 'argument', "expected exactly one argument" | |
203 | type = inputs[1]\type! | |
204 | assert type.__class == Array, Error 'argument', "expected an Array" | |
205 | ||
206 | @out = Constant.num type.size | |
207 | ||
208 | concat = Constant.meta | |
209 | meta: | |
210 | name: 'concat' | |
211 | summary: "Concatenate Arrays." | |
212 | examples: { '(concat arr1 arr2 [arr3…])' } | |
213 | ||
214 | value: class extends PureOp | |
215 | pattern: any\rep 2 | |
216 | type: (inputs) => | |
217 | size = 0 | |
218 | type = inputs[1]\type!.type | |
219 | ||
220 | for input in *inputs | |
221 | array = input\type! | |
222 | ||
223 | if array.type ~= type | |
224 | msg = string.format "Cannot concatenate different arrays %s, %s", | |
225 | inputs[1]\type!, array | |
226 | error Error 'argument', msg | |
227 | ||
228 | size += array.size | |
229 | ||
230 | Array size, type | |
231 | ||
232 | tick: => | |
233 | arrays = @unwrap_all! | |
234 | out = {} | |
235 | ||
236 | for array in *arrays | |
237 | for val in *array | |
238 | table.insert out, val | |
239 | ||
240 | @out\set out | |
241 | ||
242 | array_constr = builtins!\get('array').result | |
243 | map = Constant.meta | |
244 | meta: | |
245 | name: 'map' | |
246 | summary: "Apply an function to each value in an array." | |
247 | examples: { '(map array fn)' } | |
248 | description: " | |
249 | Invokes `fn` once for each element in `array` and returns an array of the results. | |
250 | `fn` must take one argument and return the same type consistently." | |
251 | ||
252 | value: class extends Builtin | |
253 | eval: (scope, tail) => | |
254 | L\trace "evaling #{@}" | |
255 | assert #tail == 2, "'map' takes exactly two arguments" | |
256 | tail = [L\push t\eval, scope for t in *tail] | |
257 | { array, fn } = tail | |
258 | ||
259 | fndef = fn.result | |
260 | assert fn\type! == T.fndef, "fn has to be a fndef" | |
261 | array_type = array\type! | |
262 | assert array_type.__class == Array, Error 'argument', "expected an Array" | |
263 | ||
264 | invocations = for i=1, array_type.size | |
265 | tag_o = @tag\clone Tag.parse tostring i | |
266 | tag_i = @tag\clone tag_o | |
267 | Cell tag_o, { | |
268 | with Constant.literal T.fndef, fndef!, 'fn' | |
269 | .meta = fndef.meta | |
270 | Cell tag_i, { | |
271 | Constant.literal T.opdef, get!, 'get' | |
272 | Constant.literal array_type, array.result!, 'array' | |
273 | Constant.num i-1 | |
274 | } | |
275 | } | |
276 | ||
277 | tag = @tag\clone Tag.parse '-1' | |
278 | inner = Cell tag, { | |
279 | Constant.literal T.opdef, array_constr, 'array' | |
280 | unpack invocations | |
281 | } | |
282 | super inner\eval scope | |
283 | ||
284 | ||
285 | Constant.meta | |
286 | meta: | |
287 | name: 'array' | |
288 | summary: "Utilities for dealing with arrays." | |
289 | ||
290 | value: | |
291 | :get, :set | |
292 | :head, :tail, :prepend | |
293 | :insert, :remove | |
294 | :map | |
295 | ||
296 | :size, :concat |
0 | import Array, Op, PureOp, Builtin, Constant, Error, const, sig, evt, T from require 'alv.base' | |
1 | import Cell from require 'alv.cell' | |
2 | import Tag from require 'alv.tag' | |
3 | builtins = require 'alv.builtins' | |
4 | ||
5 | unpack or= table.unpack | |
6 | any = sig! / evt! | |
7 | ||
8 | get = Constant.meta | |
9 | meta: | |
10 | name: 'get' | |
11 | summary: "Index into an array." | |
12 | examples: { '(get array i)' } | |
13 | description: "Get the value at index `i` (starting at 0). | |
14 | ||
15 | `i` has to be a constant expression." | |
16 | ||
17 | value: class extends PureOp | |
18 | pattern: any + const.num | |
19 | type: (inputs) => | |
20 | { array, i } = inputs | |
21 | array\type!\get i.result! | |
22 | ||
23 | tick: => | |
24 | { array, i } = @unwrap_all! | |
25 | @out\set array[i + 1] | |
26 | ||
27 | set = Constant.meta | |
28 | meta: | |
29 | name: 'set' | |
30 | summary: "Update a value in an array." | |
31 | examples: { '(set array i val)' } | |
32 | description: "Set the value for `i` to `val`. | |
33 | ||
34 | `i` has to be a constant expression. This is a pure op, so at most one of | |
35 | `array` and `val` may be a !-stream." | |
36 | ||
37 | value: class extends PureOp | |
38 | pattern: any + const.num + any | |
39 | type: (inputs) => | |
40 | { array, i, val } = inputs | |
41 | type = array\type! | |
42 | expected = type\get i.result! | |
43 | ||
44 | if expected ~= val\type! | |
45 | msg = string.format "expected value of type %s, not %s", | |
46 | expected, val\type! | |
47 | error Error 'argument', msg | |
48 | ||
49 | type | |
50 | ||
51 | tick: => | |
52 | { array, key, val } = @unwrap_all! | |
53 | ||
54 | array = [v for v in *array] | |
55 | array[key + 1] = val | |
56 | ||
57 | @out\set array | |
58 | ||
59 | head = Constant.meta | |
60 | meta: | |
61 | name: 'head' | |
62 | summary: "Get the first element from an array." | |
63 | examples: { '(head array)' } | |
64 | ||
65 | value: class extends PureOp | |
66 | pattern: any*1 | |
67 | type: (inputs) => | |
68 | type = inputs[1]\type! | |
69 | ||
70 | assert type.__class == Array, Error 'argument', "expected an Array" | |
71 | assert type.size > 0, Error 'argument', "cannot get head of empty Array" | |
72 | ||
73 | type.type | |
74 | ||
75 | tick: => | |
76 | { array } = @unwrap_all! | |
77 | @out\set array[1] | |
78 | ||
79 | tail = Constant.meta | |
80 | meta: | |
81 | name: 'tail' | |
82 | summary: "Get everything except the first element from an array." | |
83 | examples: { '(tail array)' } | |
84 | ||
85 | value: class extends PureOp | |
86 | pattern: any*1 | |
87 | type: (inputs) => | |
88 | type = inputs[1]\type! | |
89 | ||
90 | assert type.__class == Array, Error 'argument', "expected an Array" | |
91 | assert type.size > 0, Error 'argument', "cannot get tail of empty Array" | |
92 | ||
93 | Array type.size - 1, type.type | |
94 | ||
95 | tick: => | |
96 | { array } = @unwrap_all! | |
97 | @out\set [v for v in *array[2,]] | |
98 | ||
99 | prepend = Constant.meta | |
100 | meta: | |
101 | name: 'prepend' | |
102 | summary: "Prepend a new value at the start of an array." | |
103 | examples: { '(prepend array val)' } | |
104 | description: "Prepend `val` to `array` at index `0`, moving other values back. | |
105 | ||
106 | This is a pure op, so at most one of `array` and `val` may be a !-stream." | |
107 | ||
108 | value: class extends PureOp | |
109 | pattern: any + any | |
110 | type: (inputs) => | |
111 | { array, val } = inputs | |
112 | type = array\type! | |
113 | ||
114 | if val\type! ~= type.type | |
115 | msg = string.format "expected value of type %s, not %s", | |
116 | type.type, val\type! | |
117 | error Error 'argument', msg | |
118 | ||
119 | Array type.size + 1, type.type | |
120 | ||
121 | tick: => | |
122 | { array, val } = @unwrap_all! | |
123 | ||
124 | array = [v for v in *array] | |
125 | table.insert array, 1, val | |
126 | ||
127 | @out\set array | |
128 | ||
129 | insert = Constant.meta | |
130 | meta: | |
131 | name: 'insert' | |
132 | summary: "Insert a new value into an array." | |
133 | examples: { '(insert array i val)' } | |
134 | description: "Insert `val` into `array` at `i`, moving other values back if | |
135 | necessary. | |
136 | ||
137 | `i` has to be a constant expression. This is a pure op, so at most one of | |
138 | `array` and `val` may be a !-stream." | |
139 | ||
140 | value: class extends PureOp | |
141 | pattern: any + const.num + any | |
142 | type: (inputs) => | |
143 | { array, i, val } = inputs | |
144 | type = array\type! | |
145 | i = i.result! | |
146 | ||
147 | if i > type.size or i < 0 | |
148 | error Error 'argument', "index '#{i}' out of range!" | |
149 | if val\type! ~= type.type | |
150 | msg = string.format "expected value of type %s, not %s", | |
151 | type.type, val\type! | |
152 | error Error 'argument', msg | |
153 | ||
154 | Array type.size + 1, type.type | |
155 | ||
156 | tick: => | |
157 | { array, i, val } = @unwrap_all! | |
158 | ||
159 | array = [v for v in *array] | |
160 | table.insert array, i + 1, val | |
161 | ||
162 | @out\set array | |
163 | ||
164 | remove = Constant.meta | |
165 | meta: | |
166 | name: 'remove' | |
167 | summary: "Remove a value from an Array." | |
168 | examples: { '(remove array i)' } | |
169 | description: "Removes the value at index `i` from `array`. | |
170 | ||
171 | `i` has to be a constant expression." | |
172 | ||
173 | value: class extends PureOp | |
174 | pattern: any + const.num | |
175 | type: (inputs) => | |
176 | { array, i } = inputs | |
177 | type = array\type! | |
178 | ||
179 | -- check index range | |
180 | type\get i.result! | |
181 | ||
182 | Array type.size - 1, type.type | |
183 | ||
184 | tick: => | |
185 | { array, i, val } = @unwrap_all! | |
186 | ||
187 | array = [v for v in *array] | |
188 | table.remove array, i + 1 | |
189 | ||
190 | @out\set array | |
191 | ||
192 | size = Constant.meta | |
193 | meta: | |
194 | name: 'size' | |
195 | summary: "Get the size of an array." | |
196 | examples: { '(size array)' } | |
197 | ||
198 | value: class extends Op | |
199 | setup: (inputs) => | |
200 | super {} | |
201 | ||
202 | assert #inputs == 1, Error 'argument', "expected exactly one argument" | |
203 | type = inputs[1]\type! | |
204 | assert type.__class == Array, Error 'argument', "expected an Array" | |
205 | ||
206 | @out = Constant.num type.size | |
207 | ||
208 | concat = Constant.meta | |
209 | meta: | |
210 | name: 'concat' | |
211 | summary: "Concatenate Arrays." | |
212 | examples: { '(concat arr1 arr2 [arr3…])' } | |
213 | ||
214 | value: class extends PureOp | |
215 | pattern: any\rep 2 | |
216 | type: (inputs) => | |
217 | size = 0 | |
218 | type = inputs[1]\type!.type | |
219 | ||
220 | for input in *inputs | |
221 | array = input\type! | |
222 | ||
223 | if array.type ~= type | |
224 | msg = string.format "Cannot concatenate different arrays %s, %s", | |
225 | inputs[1]\type!, array | |
226 | error Error 'argument', msg | |
227 | ||
228 | size += array.size | |
229 | ||
230 | Array size, type | |
231 | ||
232 | tick: => | |
233 | arrays = @unwrap_all! | |
234 | out = {} | |
235 | ||
236 | for array in *arrays | |
237 | for val in *array | |
238 | table.insert out, val | |
239 | ||
240 | @out\set out | |
241 | ||
242 | array_constr = builtins!\get('array').result | |
243 | map = Constant.meta | |
244 | meta: | |
245 | name: 'map' | |
246 | summary: "Apply an function to each value in an array." | |
247 | examples: { '(map array fn)' } | |
248 | description: " | |
249 | Invokes `fn` once for each element in `array` and returns an array of the results. | |
250 | `fn` must take one argument and return the same type consistently." | |
251 | ||
252 | value: class extends Builtin | |
253 | eval: (scope, tail) => | |
254 | L\trace "evaling #{@}" | |
255 | assert #tail == 2, "'map' takes exactly two arguments" | |
256 | tail = [L\push t\eval, scope for t in *tail] | |
257 | { array, fn } = tail | |
258 | ||
259 | fndef = fn.result | |
260 | assert fn\type! == T.fndef, "fn has to be a fndef" | |
261 | array_type = array\type! | |
262 | assert array_type.__class == Array, Error 'argument', "expected an Array" | |
263 | ||
264 | invocations = for i=1, array_type.size | |
265 | tag_o = @tag\clone Tag.parse tostring i | |
266 | tag_i = @tag\clone tag_o | |
267 | Cell tag_o, { | |
268 | with Constant.literal T.fndef, fndef!, 'fn' | |
269 | .meta = fndef.meta | |
270 | Cell tag_i, { | |
271 | Constant.literal T.opdef, get!, 'get' | |
272 | Constant.literal array_type, array.result!, 'array' | |
273 | Constant.num i-1 | |
274 | } | |
275 | } | |
276 | ||
277 | tag = @tag\clone Tag.parse '-1' | |
278 | inner = Cell tag, { | |
279 | Constant.literal T.opdef, array_constr, 'array' | |
280 | unpack invocations | |
281 | } | |
282 | super inner\eval scope | |
283 | ||
284 | ||
285 | Constant.meta | |
286 | meta: | |
287 | name: 'array' | |
288 | summary: "Utilities for dealing with arrays." | |
289 | ||
290 | value: | |
291 | :get, :set | |
292 | :head, :tail, :prepend | |
293 | :insert, :remove | |
294 | :map | |
295 | ||
296 | :size, :concat |
0 | import Struct, Op, PureOp, Constant, Error, const, sig, evt from require 'alv.base' | |
1 | ||
2 | any = sig! / evt! | |
3 | key_type = const.str / const.sym | |
4 | ||
5 | get = Constant.meta | |
6 | meta: | |
7 | name: 'get' | |
8 | summary: "Index into a struct." | |
9 | examples: { '(get struct key)' } | |
10 | description: "Get the value at `key`. | |
11 | ||
12 | `key` has to be a constant expression." | |
13 | ||
14 | value: class extends PureOp | |
15 | pattern: any + key_type | |
16 | type: (inputs) => | |
17 | { struct, key } = inputs | |
18 | struct\type!\get key.result! | |
19 | ||
20 | tick: => | |
21 | { struct, key } = @unwrap_all! | |
22 | @out\set struct[key] | |
23 | ||
24 | set = Constant.meta | |
25 | meta: | |
26 | name: 'set' | |
27 | summary: "Update values in a struct." | |
28 | examples: { '(set struct key val)' } | |
29 | description: "Set the value for `key` to `val`. | |
30 | ||
31 | `key` has to be a constant expression. This is a pure op, so at most one of | |
32 | `struct` and `val` may be a !-stream." | |
33 | ||
34 | value: class extends PureOp | |
35 | pattern: any + key_type + any | |
36 | type: (inputs) => | |
37 | { struct, key, val } = inputs | |
38 | type = struct\type! | |
39 | expected = type\get key.result! | |
40 | ||
41 | if expected ~= val\type! | |
42 | msg = string.format "expected value for key '%s' to be %s, not %s", | |
43 | key.result!, expected, val\type! | |
44 | error Error 'argument', msg | |
45 | ||
46 | type | |
47 | ||
48 | tick: => | |
49 | { struct, key, val } = @unwrap_all! | |
50 | ||
51 | struct = {k,v for k,v in pairs struct} | |
52 | struct[key] = val | |
53 | ||
54 | @out\set struct | |
55 | ||
56 | insert = Constant.meta | |
57 | meta: | |
58 | name: 'insert' | |
59 | summary: "Insert a new value into a struct." | |
60 | examples: { '(insert struct key val)' } | |
61 | description: "Insert `val` into `struct` at `key`. | |
62 | ||
63 | `key` has to be a constant expression. This is a pure op, so at most one of | |
64 | `struct` and `val` may be a !-stream." | |
65 | ||
66 | value: class extends PureOp | |
67 | pattern: any + key_type + any | |
68 | type: (inputs) => | |
69 | { struct, key, val } = inputs | |
70 | type = struct\type! | |
71 | key = key.result! | |
72 | ||
73 | if type.types[key] | |
74 | msg = string.format "key '%s' already exists in value of type %s", | |
75 | key, type | |
76 | error Error 'argument', msg | |
77 | ||
78 | types = {k,v for k,v in pairs type.types} | |
79 | types[key] = val\type! | |
80 | Struct types | |
81 | ||
82 | tick: => | |
83 | { struct, key, val } = @unwrap_all! | |
84 | ||
85 | struct = {k,v for k,v in pairs struct} | |
86 | struct[key] = val | |
87 | ||
88 | @out\set struct | |
89 | ||
90 | remove = Constant.meta | |
91 | meta: | |
92 | name: 'remove' | |
93 | summary: "Remove values from a struct." | |
94 | examples: { '(remove struct key)' } | |
95 | description: "Removes the value at index `key` from `struct`. | |
96 | ||
97 | `key` has to be a constant expression." | |
98 | ||
99 | value: class extends PureOp | |
100 | pattern: any + key_type | |
101 | type: (inputs) => | |
102 | { struct, key } = inputs | |
103 | type = struct\type! | |
104 | key = key.result! | |
105 | ||
106 | -- check key exists | |
107 | type\get key | |
108 | ||
109 | types = {k,v for k,v in pairs type.types} | |
110 | types[key] = nil | |
111 | Struct types | |
112 | ||
113 | tick: => | |
114 | { struct, key, val } = @unwrap_all! | |
115 | ||
116 | struct = {k,v for k,v in pairs struct} | |
117 | struct[key] = nil | |
118 | ||
119 | @out\set struct | |
120 | ||
121 | Constant.meta | |
122 | meta: | |
123 | name: 'struct' | |
124 | summary: "Utilities for dealing with structs." | |
125 | ||
126 | value: | |
127 | :get, :set | |
128 | :insert, :remove |
0 | import Struct, Op, PureOp, Constant, Error, const, sig, evt from require 'alv.base' | |
1 | ||
2 | any = sig! / evt! | |
3 | key_type = const.str / const.sym | |
4 | ||
5 | get = Constant.meta | |
6 | meta: | |
7 | name: 'get' | |
8 | summary: "Index into a struct." | |
9 | examples: { '(get struct key)' } | |
10 | description: "Get the value at `key`. | |
11 | ||
12 | `key` has to be a constant expression." | |
13 | ||
14 | value: class extends PureOp | |
15 | pattern: any + key_type | |
16 | type: (inputs) => | |
17 | { struct, key } = inputs | |
18 | struct\type!\get key.result! | |
19 | ||
20 | tick: => | |
21 | { struct, key } = @unwrap_all! | |
22 | @out\set struct[key] | |
23 | ||
24 | set = Constant.meta | |
25 | meta: | |
26 | name: 'set' | |
27 | summary: "Update values in a struct." | |
28 | examples: { '(set struct key val)' } | |
29 | description: "Set the value for `key` to `val`. | |
30 | ||
31 | `key` has to be a constant expression. This is a pure op, so at most one of | |
32 | `struct` and `val` may be a !-stream." | |
33 | ||
34 | value: class extends PureOp | |
35 | pattern: any + key_type + any | |
36 | type: (inputs) => | |
37 | { struct, key, val } = inputs | |
38 | type = struct\type! | |
39 | expected = type\get key.result! | |
40 | ||
41 | if expected ~= val\type! | |
42 | msg = string.format "expected value for key '%s' to be %s, not %s", | |
43 | key.result!, expected, val\type! | |
44 | error Error 'argument', msg | |
45 | ||
46 | type | |
47 | ||
48 | tick: => | |
49 | { struct, key, val } = @unwrap_all! | |
50 | ||
51 | struct = {k,v for k,v in pairs struct} | |
52 | struct[key] = val | |
53 | ||
54 | @out\set struct | |
55 | ||
56 | insert = Constant.meta | |
57 | meta: | |
58 | name: 'insert' | |
59 | summary: "Insert a new value into a struct." | |
60 | examples: { '(insert struct key val)' } | |
61 | description: "Insert `val` into `struct` at `key`. | |
62 | ||
63 | `key` has to be a constant expression. This is a pure op, so at most one of | |
64 | `struct` and `val` may be a !-stream." | |
65 | ||
66 | value: class extends PureOp | |
67 | pattern: any + key_type + any | |
68 | type: (inputs) => | |
69 | { struct, key, val } = inputs | |
70 | type = struct\type! | |
71 | key = key.result! | |
72 | ||
73 | if type.types[key] | |
74 | msg = string.format "key '%s' already exists in value of type %s", | |
75 | key, type | |
76 | error Error 'argument', msg | |
77 | ||
78 | types = {k,v for k,v in pairs type.types} | |
79 | types[key] = val\type! | |
80 | Struct types | |
81 | ||
82 | tick: => | |
83 | { struct, key, val } = @unwrap_all! | |
84 | ||
85 | struct = {k,v for k,v in pairs struct} | |
86 | struct[key] = val | |
87 | ||
88 | @out\set struct | |
89 | ||
90 | remove = Constant.meta | |
91 | meta: | |
92 | name: 'remove' | |
93 | summary: "Remove values from a struct." | |
94 | examples: { '(remove struct key)' } | |
95 | description: "Removes the value at index `key` from `struct`. | |
96 | ||
97 | `key` has to be a constant expression." | |
98 | ||
99 | value: class extends PureOp | |
100 | pattern: any + key_type | |
101 | type: (inputs) => | |
102 | { struct, key } = inputs | |
103 | type = struct\type! | |
104 | key = key.result! | |
105 | ||
106 | -- check key exists | |
107 | type\get key | |
108 | ||
109 | types = {k,v for k,v in pairs type.types} | |
110 | types[key] = nil | |
111 | Struct types | |
112 | ||
113 | tick: => | |
114 | { struct, key, val } = @unwrap_all! | |
115 | ||
116 | struct = {k,v for k,v in pairs struct} | |
117 | struct[key] = nil | |
118 | ||
119 | @out\set struct | |
120 | ||
121 | Constant.meta | |
122 | meta: | |
123 | name: 'struct' | |
124 | summary: "Utilities for dealing with structs." | |
125 | ||
126 | value: | |
127 | :get, :set | |
128 | :insert, :remove |