git.s-ol.nu alive / fb248e9
rename alv.builtin to alv.builtins s-ol 1 year, 8 months ago
7 changed file(s) with 518 addition(s) and 518 deletion(s). Raw diff Collapse all Expand all
3030 @mkdir -p `dirname $@`
3131 docs/gen/module $@ alv-lib.$(subst /,.,$*) $(subst /,.,$*)
3232
33 docs/reference/index.html: alv/builtin.moon $(MODREFS) $(DEPS)
33 docs/reference/index.html: alv/builtins.moon $(MODREFS) $(DEPS)
3434 docs/gen/index $@ $(MODULES)
3535
3636 docs/ldoc.ltp: $(DEPS)
+0
-512
alv/builtin.moon less more
0 ----
1 -- Builtin `Builtin`s and `Op`s.
2 --
3 -- Please see the [reference](../../reference/index.html#builtins) for
4 -- documentation.
5 --
6 -- @module builtin
7 import Builtin, Op, PureOp, T, FnDef, Input, const, val, evt, Struct, Array
8 from require 'alv.base'
9 import Constant from require 'alv.result'
10 import Error from require 'alv.error'
11 import RTNode from require 'alv.rtnode'
12 import Cell from require 'alv.cell'
13 import Scope from require 'alv.scope'
14 import Tag from require 'alv.tag'
15 import op_invoke from require 'alv.invoke'
16 lfs = require 'lfs'
17
18 doc = Constant.meta
19 meta:
20 name: 'doc'
21 summary: "Print documentation in console."
22 examples: { '(doc sym)' }
23 description: "Print the documentation for `sym` to the console"
24
25 value: class extends Builtin
26 format_meta = =>
27 str = @summary
28 if @examples
29 for example in *@examples
30 str ..= '\n' .. example
31 if @description
32 str ..= '\n' .. @description\match '^\n*(.+)\n*$'
33 str
34
35 eval: (scope, tail) =>
36 assert #tail == 1, "'doc' takes exactly one parameter"
37
38 node = L\push tail[1]\eval, scope
39 with RTNode children: { def }
40 meta = node.result.meta
41 L\print "(doc #{tail[1]}):\n#{format_meta meta}\n"
42
43 def = Constant.meta
44 meta:
45 name: 'def'
46 summary: "Declare symbols in current scope."
47 examples: { '(def sym1 val-expr1 [sym2 val-expr2…])' }
48 description: "
49 Define the symbols `sym1`, `sym2`, … to resolve to the values of `val-expr1`,
50 `val-expr2`, …."
51
52 value: class extends Builtin
53 eval: (scope, tail) =>
54 L\trace "evaling #{@}"
55 assert #tail > 1, "'def' requires at least 2 arguments"
56 assert #tail % 2 == 0, "'def' requires an even number of arguments"
57
58 children = L\push ->
59 return for i=1,#tail,2
60 name, val_expr = tail[i], tail[i+1]
61 name = name\unwrap T.sym
62
63 with val_expr\eval scope
64 scope\set name, \make_ref!
65
66 RTNode :children
67
68 use = Constant.meta
69 meta:
70 name: 'use'
71 summary: "Merge scopes into current scope."
72 examples: { '(use scope1 [scope2…])' }
73 description: "
74 Copy all symbol definitions from `scope1`, `scope2`, … to the current scope.
75 All arguments have to be evaltime constant."
76
77 value: class extends Builtin
78 eval: (scope, tail) =>
79 L\trace "evaling #{@}"
80 for child in *tail
81 node = L\push child\eval, scope
82 value = node\const!
83 scope\use value\unwrap 'scope', "'use' only works on scopes"
84
85 RTNode!
86
87 require_ = Constant.meta
88 meta:
89 name: 'require'
90 summary: "Load a module."
91 examples: { '(require name)' }
92 description: "Load a module and return its scope."
93
94 value: class extends Builtin
95 eval: (scope, tail) =>
96 L\trace "evaling #{@}"
97 assert #tail == 1, "'require' takes exactly one parameter"
98
99 node = L\push tail[1]\eval, scope
100 name = node\const!\unwrap 'str'
101
102 L\trace @, "loading module #{name}"
103 COPILOT\require name
104
105 import_ = Constant.meta
106 meta:
107 name: 'import'
108 summary: "Require and define modules."
109 examples: { '(import sym1 [sym2…])' }
110 description: "
111 Requires modules `sym1`, `sym2`, … and define them as `sym1`, `sym2`, … in the
112 current scope."
113
114 value: class extends Builtin
115 eval: (scope, tail) =>
116 L\trace "evaling #{@}"
117 assert #tail > 0, "'import' requires at least one arguments"
118
119 children = for i, child in ipairs tail
120 name = child\unwrap T.sym
121 with COPILOT\require name
122 scope\set name, \make_ref!
123 RTNode :children
124
125 import_star = Constant.meta
126 meta:
127 name: 'import*'
128 summary: "Require and use modules."
129 examples: { '(import* sym1 [sym2…])' }
130 description: "
131 Requires modules `sym1`, `sym2`, … and merges them into the current scope."
132
133 value: class extends Builtin
134 eval: (scope, tail) =>
135 L\trace "evaling #{@}"
136 assert #tail > 0, "'import' requires at least one arguments"
137
138 children = for i, child in ipairs tail
139 with COPILOT\require child\unwrap T.sym
140 scope\use .result\unwrap T.scope
141 RTNode :children
142
143 export_ = Constant.meta
144 meta:
145 name: 'export'
146 summary: "Evaluate definitions in a new scope and return it."
147 examples: { '(export expr1 [expr2…])' }
148 description: "
149 Evaluate `expr1`, `expr2`, … in a new Scope and return scope."
150
151 value: class extends Builtin
152 eval: (scope, tail) =>
153 new_scope = Scope scope
154 children = [expr\eval new_scope for expr in *tail]
155 RTNode :children, result: Constant.wrap new_scope
156
157 export_star = Constant.meta
158 meta:
159 name: 'export*'
160 summary: "Export specific symbol definitions as a module/scope."
161 examples: { '(export* sym1 [sym2…])', '(export*)' }
162 description: "
163 Creates a scope containing the symbols `sym1`, `sym2`, … and returns it.
164
165 Copies the containing scope if no symbols are given."
166
167 value: class extends Builtin
168 eval: (scope, tail) =>
169 L\trace "evaling #{@}"
170 new_scope = Scope!
171
172 children = if #tail == 0
173 for k,node in pairs scope.values
174 new_scope\set k, node
175 node
176 else
177 for child in *tail
178 name = child\unwrap T.sym
179 with node = scope\get name
180 new_scope\set name, node
181
182 RTNode :children, result: Constant.wrap new_scope
183
184 fn = Constant.meta
185 meta:
186 name: 'fn'
187 summary: "Declare a function."
188 examples: { '(fn (p1 [p2…]) body-expr)' }
189 description: "
190 The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
191 function is invoked."
192
193 value: class extends Builtin
194 eval: (scope, tail) =>
195 L\trace "evaling #{@}"
196 assert #tail == 2, "'fn' takes exactly two arguments"
197 { params, body } = tail
198
199 assert params.__class == Cell, "'fn's first argument has to be an expression"
200 param_symbols = for param in *params.children
201 assert param.type == T.sym, "function parameter declaration has to be a symbol"
202 param
203
204 RTNode result: with Constant.wrap FnDef param_symbols, body, scope
205 .meta = {
206 summary: "(user defined function)"
207 examples: { "(??? #{table.concat [p! for p in *param_symbols], ' '})" }
208 }
209
210 defn = Constant.meta
211 meta:
212 name: 'defn'
213 summary: "Define a function."
214 examples: { '(defn name-sym (p1 [p2…]) body-expr)' }
215 description: "
216 Declare a function and define it as `name-sym` in the current scope.
217 The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
218 function is invoked."
219
220 value: class extends Builtin
221 eval: (scope, tail) =>
222 L\trace "evaling #{@}"
223 assert #tail == 3, "'defn' takes exactly three arguments"
224 { name, params, body } = tail
225
226 name = name\unwrap T.sym
227 assert params.__class == Cell, "'defn's second argument has to be an expression"
228 param_symbols = for param in *params.children
229 assert param.type == T.sym, "function parameter declaration has to be a symbol"
230 param
231
232 result = with Constant.wrap FnDef param_symbols, body, scope
233 .meta =
234 :name
235 summary: "(user defined function)"
236 examples: { "(#{name} #{table.concat [p! for p in *param_symbols], ' '})" }
237
238 scope\set name, RTNode :result
239 RTNode!
240
241 do_expr = Constant.meta
242 meta:
243 name: 'do'
244 summary: "Evaluate multiple expressions in a new scope."
245 examples: { '(do expr1 [expr2…])' }
246 description: "
247 Evaluate `expr1`, `expr2`, … and return the value of the last expression."
248
249 value: class extends Builtin
250 eval: (scope, tail) =>
251 scope = Scope scope
252 children = [expr\eval scope for expr in *tail]
253 last = children[#children]
254 RTNode :children, result: last and last.result
255
256 if_ = Constant.meta
257 meta:
258 name: 'if'
259 summary: "Make an evaltime const choice."
260 examples: { '(if bool then-expr [else-expr])' }
261 description: "
262 `bool` has to be an evaltime constant. If it is truthy, this expression is equivalent
263 to `then-expr`, otherwise it is equivalent to `else-xpr` if given, or nil otherwise."
264
265 value: class extends Builtin
266 eval: (scope, tail) =>
267 L\trace "evaling #{@}"
268 assert #tail >= 2, "'if' needs at least two parameters"
269 assert #tail <= 3, "'if' needs at most three parameters"
270
271 { xif, xthen, xelse } = tail
272
273 xif = L\push xif\eval, scope
274 xif = xif\const!\unwrap!
275
276 if xif
277 xthen\eval scope
278 elseif xelse
279 xelse\eval scope
280
281 trace_ = Constant.meta
282 meta:
283 name: 'trace='
284 summary: "Trace an expression's value at evaltime."
285 examples: { '(trace= expr)' }
286
287 value: class extends Builtin
288 eval: (scope, tail) =>
289 L\trace "evaling #{@}"
290 assert #tail == 1, "'trace!' takes exactly one parameter"
291
292 with node = L\push tail[1]\eval, scope
293 L\print "trace! #{tail[1]\stringify 2}: #{node.result}"
294
295 trace = Constant.meta
296 meta:
297 name: 'trace'
298 summary: "Trace an expression's values at runtime."
299 examples: { '(trace expr)' }
300
301 value: class extends Builtin
302 class traceOp extends Op
303 setup: (inputs) =>
304 super
305 prefix: Input.cold inputs[1]
306 value: Input.hot inputs[2]
307
308 tick: =>
309 L\print "trace #{@inputs.prefix!}: #{@inputs.value.result}"
310
311 eval: (scope, tail) =>
312 L\trace "evaling #{@}"
313 assert #tail == 1, "'trace!' takes exactly one parameter"
314
315 tag = @tag\clone Tag.parse '-1'
316 inner = Cell tag, {
317 Constant.literal T.opdef, traceOp, 'trace'
318 Constant.str tail[1]\stringify 2
319 tail[1]
320 }
321 inner\eval scope
322
323 print_ = Constant.meta
324 meta:
325 name: 'print'
326 summary: "Print string values."
327 examples: { '(print str)' }
328
329 value: class extends Op
330 setup: (inputs) =>
331 value = (val.str / evt.str)\match inputs
332 super value: Input.hot value
333
334 tick: =>
335 if @inputs.value\metatype! == 'event'
336 for msg in *@inputs.value!
337 L\print msg
338 else
339 L\print @inputs.value!
340
341 to_const = Constant.meta
342 meta:
343 name: '='
344 summary: "Assert expression is constant."
345 examples: { '(= val)' }
346 description: "Asserts that `val` is a constant expression and returns it."
347 value: class extends Op
348 setup: (inputs) =>
349 super {}
350 input = const!\match inputs
351 @out = input\type!\mk_const input.result!
352
353 to_sig = Constant.meta
354 meta:
355 name: '~'
356 summary: "Cast to ~-stream."
357 examples: { '(~ event initial)' }
358 description: "
359 Casts !-stream to ~-stream by always reproducing the last received value.
360 Since ~-streams cannot be emtpy, specifying an `initial` value is necessary."
361 value: class extends Op
362 setup: (inputs) =>
363 { event, initial } = (evt! + val!)\match inputs
364 assert event\type! == initial\type!,
365 Error 'argument', "~ arguments have to be of the same type"
366
367 super event: Input.hot event
368
369 if not @out or @out.type != input\type!
370 @out = event\type!\mk_sig initial.result!
371
372 tick: => @out\set @inputs.event!
373
374 to_evt = Constant.meta
375 meta:
376 name: '!'
377 summary: "Cast to !-stream."
378 examples: { '(! val)', '(! sig trig)' }
379 description: "Casts anything to a !-stream depending on arguments:
380
381 - if `val` is a ~-stream, emits events on change.s
382 - if `val` is a !-stream, emits a bang for each incoming even.t
383 - if `trig` is given, samples `sig` as a new event when `trig` arrives."
384 value: class extends Op
385 pattern = (val! + evt.bang) / (val! / evt!)\rep(1,1)
386 setup: (inputs) =>
387 { sig, trig } = pattern\match inputs
388 if trig
389 super
390 trig: Input.hot trig
391 sig: Input.cold sig
392 elseif sig\metatype! == '!'
393 super
394 trig: Input.hot sig
395 sig: Input.cold Constant.bang true
396 else
397 super sig: Input.hot sig
398 @out = @inputs.sig\type!\mk_evt!
399
400 tick: =>
401 @out\set @inputs.sig!
402
403 array = Constant.meta
404 meta:
405 name: 'array'
406 summary: "Construct an array."
407 examples: { '(array a b c…)' }
408 description: "Produces an array of values."
409
410 value: do
411 any = val! / evt!
412
413 class extends PureOp
414 pattern: any!*0
415 type: (args) => Array #args, args[1]\type!
416
417 tick: =>
418 args = @unwrap_all!
419 @out\set args
420
421 struct = Constant.meta
422 meta:
423 name: 'struct'
424 summary: "Construct an struct."
425 examples: { '(struct key1 val1 [key2 val2…])' }
426 description: "Produces an struct of values."
427
428 value: do
429 key = const.str / const.sym
430 val = val! / evt!
431 pair = (key + val)\named 'key', 'val'
432
433 class extends PureOp
434 pattern: pair*0
435 type: (pairs) =>
436 Struct {key.result!, val\type! for {:key, :val} in *pairs}
437
438 tick: =>
439 pairs = @unwrap_all!
440 @out\set {key, val for {:key, :val} in *pairs}
441
442 get = Constant.meta
443 meta:
444 name: 'get'
445 summary: "Index into Arrays and Structs."
446 examples: { '(get val key [key2…])' }
447
448 value: class extends Op
449 pattern = (val! / evt!) + (const.str / const.sym / const.num)*0
450 setup: (inputs) =>
451 { val, keys } = pattern\match inputs
452 super val: Input.hot val
453
454 @state = [key.result! for key in *keys]
455
456 type = val\type!
457 for key in *@state
458 type = type\get key
459
460 if val\metatype == '!'
461 @out = type\mk_evt!
462 else
463 @out = type\mk_sig!
464
465 tick: =>
466 val = @inputs.val!
467 for key in *@state
468 if type(key) == 'number'
469 key = key + 1
470 val = val[key]
471 @out\set val
472
473 Scope.from_table {
474 :doc
475 :trace, 'trace=': trace_, print: print_
476
477 :def, :use
478 require: require_
479 import: import_
480 'import*': import_star
481 export: export_
482 'export*': export_star
483
484 '=': to_const
485 '~': to_sig
486 '!': to_evt
487
488 :array, :struct, :get
489
490 true: Constant.meta
491 meta:
492 name: 'true'
493 summary: "The boolean constant `true`."
494 value: Constant.bool true
495
496 false: Constant.meta
497 meta:
498 name: 'false'
499 summary: "The boolean constant `false`."
500 value: Constant.bool false
501
502 bang: Constant.meta
503 meta:
504 name: 'bang'
505 summary: "A `bang` value-constant."
506 value: Constant T.bang, true
507
508 :fn, :defn
509 'do': do_expr
510 if: if_
511 }
0 ----
1 -- Builtin `Builtin`s and `Op`s.
2 --
3 -- Please see the [reference](../../reference/index.html#builtins) for
4 -- documentation.
5 --
6 -- @module builtins
7 import Builtin, Op, PureOp, T, FnDef, Input, const, val, evt, Struct, Array
8 from require 'alv.base'
9 import Constant from require 'alv.result'
10 import Error from require 'alv.error'
11 import RTNode from require 'alv.rtnode'
12 import Cell from require 'alv.cell'
13 import Scope from require 'alv.scope'
14 import Tag from require 'alv.tag'
15 import op_invoke from require 'alv.invoke'
16 lfs = require 'lfs'
17
18 doc = Constant.meta
19 meta:
20 name: 'doc'
21 summary: "Print documentation in console."
22 examples: { '(doc sym)' }
23 description: "Print the documentation for `sym` to the console"
24
25 value: class extends Builtin
26 format_meta = =>
27 str = @summary
28 if @examples
29 for example in *@examples
30 str ..= '\n' .. example
31 if @description
32 str ..= '\n' .. @description\match '^\n*(.+)\n*$'
33 str
34
35 eval: (scope, tail) =>
36 assert #tail == 1, "'doc' takes exactly one parameter"
37
38 node = L\push tail[1]\eval, scope
39 with RTNode children: { def }
40 meta = node.result.meta
41 L\print "(doc #{tail[1]}):\n#{format_meta meta}\n"
42
43 def = Constant.meta
44 meta:
45 name: 'def'
46 summary: "Declare symbols in current scope."
47 examples: { '(def sym1 val-expr1 [sym2 val-expr2…])' }
48 description: "
49 Define the symbols `sym1`, `sym2`, … to resolve to the values of `val-expr1`,
50 `val-expr2`, …."
51
52 value: class extends Builtin
53 eval: (scope, tail) =>
54 L\trace "evaling #{@}"
55 assert #tail > 1, "'def' requires at least 2 arguments"
56 assert #tail % 2 == 0, "'def' requires an even number of arguments"
57
58 children = L\push ->
59 return for i=1,#tail,2
60 name, val_expr = tail[i], tail[i+1]
61 name = name\unwrap T.sym
62
63 with val_expr\eval scope
64 scope\set name, \make_ref!
65
66 RTNode :children
67
68 use = Constant.meta
69 meta:
70 name: 'use'
71 summary: "Merge scopes into current scope."
72 examples: { '(use scope1 [scope2…])' }
73 description: "
74 Copy all symbol definitions from `scope1`, `scope2`, … to the current scope.
75 All arguments have to be evaltime constant."
76
77 value: class extends Builtin
78 eval: (scope, tail) =>
79 L\trace "evaling #{@}"
80 for child in *tail
81 node = L\push child\eval, scope
82 value = node\const!
83 scope\use value\unwrap 'scope', "'use' only works on scopes"
84
85 RTNode!
86
87 require_ = Constant.meta
88 meta:
89 name: 'require'
90 summary: "Load a module."
91 examples: { '(require name)' }
92 description: "Load a module and return its scope."
93
94 value: class extends Builtin
95 eval: (scope, tail) =>
96 L\trace "evaling #{@}"
97 assert #tail == 1, "'require' takes exactly one parameter"
98
99 node = L\push tail[1]\eval, scope
100 name = node\const!\unwrap 'str'
101
102 L\trace @, "loading module #{name}"
103 COPILOT\require name
104
105 import_ = Constant.meta
106 meta:
107 name: 'import'
108 summary: "Require and define modules."
109 examples: { '(import sym1 [sym2…])' }
110 description: "
111 Requires modules `sym1`, `sym2`, … and define them as `sym1`, `sym2`, … in the
112 current scope."
113
114 value: class extends Builtin
115 eval: (scope, tail) =>
116 L\trace "evaling #{@}"
117 assert #tail > 0, "'import' requires at least one arguments"
118
119 children = for i, child in ipairs tail
120 name = child\unwrap T.sym
121 with COPILOT\require name
122 scope\set name, \make_ref!
123 RTNode :children
124
125 import_star = Constant.meta
126 meta:
127 name: 'import*'
128 summary: "Require and use modules."
129 examples: { '(import* sym1 [sym2…])' }
130 description: "
131 Requires modules `sym1`, `sym2`, … and merges them into the current scope."
132
133 value: class extends Builtin
134 eval: (scope, tail) =>
135 L\trace "evaling #{@}"
136 assert #tail > 0, "'import' requires at least one arguments"
137
138 children = for i, child in ipairs tail
139 with COPILOT\require child\unwrap T.sym
140 scope\use .result\unwrap T.scope
141 RTNode :children
142
143 export_ = Constant.meta
144 meta:
145 name: 'export'
146 summary: "Evaluate definitions in a new scope and return it."
147 examples: { '(export expr1 [expr2…])' }
148 description: "
149 Evaluate `expr1`, `expr2`, … in a new Scope and return scope."
150
151 value: class extends Builtin
152 eval: (scope, tail) =>
153 new_scope = Scope scope
154 children = [expr\eval new_scope for expr in *tail]
155 RTNode :children, result: Constant.wrap new_scope
156
157 export_star = Constant.meta
158 meta:
159 name: 'export*'
160 summary: "Export specific symbol definitions as a module/scope."
161 examples: { '(export* sym1 [sym2…])', '(export*)' }
162 description: "
163 Creates a scope containing the symbols `sym1`, `sym2`, … and returns it.
164
165 Copies the containing scope if no symbols are given."
166
167 value: class extends Builtin
168 eval: (scope, tail) =>
169 L\trace "evaling #{@}"
170 new_scope = Scope!
171
172 children = if #tail == 0
173 for k,node in pairs scope.values
174 new_scope\set k, node
175 node
176 else
177 for child in *tail
178 name = child\unwrap T.sym
179 with node = scope\get name
180 new_scope\set name, node
181
182 RTNode :children, result: Constant.wrap new_scope
183
184 fn = Constant.meta
185 meta:
186 name: 'fn'
187 summary: "Declare a function."
188 examples: { '(fn (p1 [p2…]) body-expr)' }
189 description: "
190 The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
191 function is invoked."
192
193 value: class extends Builtin
194 eval: (scope, tail) =>
195 L\trace "evaling #{@}"
196 assert #tail == 2, "'fn' takes exactly two arguments"
197 { params, body } = tail
198
199 assert params.__class == Cell, "'fn's first argument has to be an expression"
200 param_symbols = for param in *params.children
201 assert param.type == T.sym, "function parameter declaration has to be a symbol"
202 param
203
204 RTNode result: with Constant.wrap FnDef param_symbols, body, scope
205 .meta = {
206 summary: "(user defined function)"
207 examples: { "(??? #{table.concat [p! for p in *param_symbols], ' '})" }
208 }
209
210 defn = Constant.meta
211 meta:
212 name: 'defn'
213 summary: "Define a function."
214 examples: { '(defn name-sym (p1 [p2…]) body-expr)' }
215 description: "
216 Declare a function and define it as `name-sym` in the current scope.
217 The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
218 function is invoked."
219
220 value: class extends Builtin
221 eval: (scope, tail) =>
222 L\trace "evaling #{@}"
223 assert #tail == 3, "'defn' takes exactly three arguments"
224 { name, params, body } = tail
225
226 name = name\unwrap T.sym
227 assert params.__class == Cell, "'defn's second argument has to be an expression"
228 param_symbols = for param in *params.children
229 assert param.type == T.sym, "function parameter declaration has to be a symbol"
230 param
231
232 result = with Constant.wrap FnDef param_symbols, body, scope
233 .meta =
234 :name
235 summary: "(user defined function)"
236 examples: { "(#{name} #{table.concat [p! for p in *param_symbols], ' '})" }
237
238 scope\set name, RTNode :result
239 RTNode!
240
241 do_expr = Constant.meta
242 meta:
243 name: 'do'
244 summary: "Evaluate multiple expressions in a new scope."
245 examples: { '(do expr1 [expr2…])' }
246 description: "
247 Evaluate `expr1`, `expr2`, … and return the value of the last expression."
248
249 value: class extends Builtin
250 eval: (scope, tail) =>
251 scope = Scope scope
252 children = [expr\eval scope for expr in *tail]
253 last = children[#children]
254 RTNode :children, result: last and last.result
255
256 if_ = Constant.meta
257 meta:
258 name: 'if'
259 summary: "Make an evaltime const choice."
260 examples: { '(if bool then-expr [else-expr])' }
261 description: "
262 `bool` has to be an evaltime constant. If it is truthy, this expression is equivalent
263 to `then-expr`, otherwise it is equivalent to `else-xpr` if given, or nil otherwise."
264
265 value: class extends Builtin
266 eval: (scope, tail) =>
267 L\trace "evaling #{@}"
268 assert #tail >= 2, "'if' needs at least two parameters"
269 assert #tail <= 3, "'if' needs at most three parameters"
270
271 { xif, xthen, xelse } = tail
272
273 xif = L\push xif\eval, scope
274 xif = xif\const!\unwrap!
275
276 if xif
277 xthen\eval scope
278 elseif xelse
279 xelse\eval scope
280
281 trace_ = Constant.meta
282 meta:
283 name: 'trace='
284 summary: "Trace an expression's value at evaltime."
285 examples: { '(trace= expr)' }
286
287 value: class extends Builtin
288 eval: (scope, tail) =>
289 L\trace "evaling #{@}"
290 assert #tail == 1, "'trace!' takes exactly one parameter"
291
292 with node = L\push tail[1]\eval, scope
293 L\print "trace! #{tail[1]\stringify 2}: #{node.result}"
294
295 trace = Constant.meta
296 meta:
297 name: 'trace'
298 summary: "Trace an expression's values at runtime."
299 examples: { '(trace expr)' }
300
301 value: class extends Builtin
302 class traceOp extends Op
303 setup: (inputs) =>
304 super
305 prefix: Input.cold inputs[1]
306 value: Input.hot inputs[2]
307
308 tick: =>
309 L\print "trace #{@inputs.prefix!}: #{@inputs.value.result}"
310
311 eval: (scope, tail) =>
312 L\trace "evaling #{@}"
313 assert #tail == 1, "'trace!' takes exactly one parameter"
314
315 tag = @tag\clone Tag.parse '-1'
316 inner = Cell tag, {
317 Constant.literal T.opdef, traceOp, 'trace'
318 Constant.str tail[1]\stringify 2
319 tail[1]
320 }
321 inner\eval scope
322
323 print_ = Constant.meta
324 meta:
325 name: 'print'
326 summary: "Print string values."
327 examples: { '(print str)' }
328
329 value: class extends Op
330 setup: (inputs) =>
331 value = (val.str / evt.str)\match inputs
332 super value: Input.hot value
333
334 tick: =>
335 if @inputs.value\metatype! == 'event'
336 for msg in *@inputs.value!
337 L\print msg
338 else
339 L\print @inputs.value!
340
341 to_const = Constant.meta
342 meta:
343 name: '='
344 summary: "Assert expression is constant."
345 examples: { '(= val)' }
346 description: "Asserts that `val` is a constant expression and returns it."
347 value: class extends Op
348 setup: (inputs) =>
349 super {}
350 input = const!\match inputs
351 @out = input\type!\mk_const input.result!
352
353 to_sig = Constant.meta
354 meta:
355 name: '~'
356 summary: "Cast to ~-stream."
357 examples: { '(~ event initial)' }
358 description: "
359 Casts !-stream to ~-stream by always reproducing the last received value.
360 Since ~-streams cannot be emtpy, specifying an `initial` value is necessary."
361 value: class extends Op
362 setup: (inputs) =>
363 { event, initial } = (evt! + val!)\match inputs
364 assert event\type! == initial\type!,
365 Error 'argument', "~ arguments have to be of the same type"
366
367 super event: Input.hot event
368
369 if not @out or @out.type != input\type!
370 @out = event\type!\mk_sig initial.result!
371
372 tick: => @out\set @inputs.event!
373
374 to_evt = Constant.meta
375 meta:
376 name: '!'
377 summary: "Cast to !-stream."
378 examples: { '(! val)', '(! sig trig)' }
379 description: "Casts anything to a !-stream depending on arguments:
380
381 - if `val` is a ~-stream, emits events on change.s
382 - if `val` is a !-stream, emits a bang for each incoming even.t
383 - if `trig` is given, samples `sig` as a new event when `trig` arrives."
384 value: class extends Op
385 pattern = (val! + evt.bang) / (val! / evt!)\rep(1,1)
386 setup: (inputs) =>
387 { sig, trig } = pattern\match inputs
388 if trig
389 super
390 trig: Input.hot trig
391 sig: Input.cold sig
392 elseif sig\metatype! == '!'
393 super
394 trig: Input.hot sig
395 sig: Input.cold Constant.bang true
396 else
397 super sig: Input.hot sig
398 @out = @inputs.sig\type!\mk_evt!
399
400 tick: =>
401 @out\set @inputs.sig!
402
403 array = Constant.meta
404 meta:
405 name: 'array'
406 summary: "Construct an array."
407 examples: { '(array a b c…)' }
408 description: "Produces an array of values."
409
410 value: do
411 any = val! / evt!
412
413 class extends PureOp
414 pattern: any!*0
415 type: (args) => Array #args, args[1]\type!
416
417 tick: =>
418 args = @unwrap_all!
419 @out\set args
420
421 struct = Constant.meta
422 meta:
423 name: 'struct'
424 summary: "Construct an struct."
425 examples: { '(struct key1 val1 [key2 val2…])' }
426 description: "Produces an struct of values."
427
428 value: do
429 key = const.str / const.sym
430 val = val! / evt!
431 pair = (key + val)\named 'key', 'val'
432
433 class extends PureOp
434 pattern: pair*0
435 type: (pairs) =>
436 Struct {key.result!, val\type! for {:key, :val} in *pairs}
437
438 tick: =>
439 pairs = @unwrap_all!
440 @out\set {key, val for {:key, :val} in *pairs}
441
442 get = Constant.meta
443 meta:
444 name: 'get'
445 summary: "Index into Arrays and Structs."
446 examples: { '(get val key [key2…])' }
447
448 value: class extends Op
449 pattern = (val! / evt!) + (const.str / const.sym / const.num)*0
450 setup: (inputs) =>
451 { val, keys } = pattern\match inputs
452 super val: Input.hot val
453
454 @state = [key.result! for key in *keys]
455
456 type = val\type!
457 for key in *@state
458 type = type\get key
459
460 if val\metatype == '!'
461 @out = type\mk_evt!
462 else
463 @out = type\mk_sig!
464
465 tick: =>
466 val = @inputs.val!
467 for key in *@state
468 if type(key) == 'number'
469 key = key + 1
470 val = val[key]
471 @out\set val
472
473 Scope.from_table {
474 :doc
475 :trace, 'trace=': trace_, print: print_
476
477 :def, :use
478 require: require_
479 import: import_
480 'import*': import_star
481 export: export_
482 'export*': export_star
483
484 '=': to_const
485 '~': to_sig
486 '!': to_evt
487
488 :array, :struct, :get
489
490 true: Constant.meta
491 meta:
492 name: 'true'
493 summary: "The boolean constant `true`."
494 value: Constant.bool true
495
496 false: Constant.meta
497 meta:
498 name: 'false'
499 summary: "The boolean constant `false`."
500 value: Constant.bool false
501
502 bang: Constant.meta
503 meta:
504 name: 'bang'
505 summary: "A `bang` value-constant."
506 value: Constant T.bang, true
507
508 :fn, :defn
509 'do': do_expr
510 if: if_
511 }
2626
2727 cycle\resolve!
2828
29 globals = require 'alv.builtin'
29 globals = require 'alv.builtins'
3030
3131 cycle\resolve!
3232
55 import Error from require 'alv.error'
66 import Scope from require 'alv.scope'
77 import program from require 'alv.parsing'
8 builtin = require 'alv.builtin'
8 builtins = require 'alv.builtins'
99
1010 slurp = (file) ->
1111 file = assert (io.open file, 'r'), Error 'io', "couldn't open '#{file}'"
4545 @ast = Error.wrap "parsing '#{@file}'", -> program\match slurp @file
4646 assert @ast, Error 'syntax', "failed to parse"
4747
48 scope = Scope builtin
48 scope = Scope builtins
4949 @root = Error.wrap "evaluating '#{@file}'", @ast\eval, scope, @registry
5050
5151 --- rollback the last evaluation cycle.
4242 h2 a "builtins", href: '#builtins'
4343 p "These definitions are automatically loaded into the global Scope of
4444 every alive session."
45 ul for key, val in opairs (require 'alv.builtin').values
45 ul for key, val in opairs (require 'alv.builtins').values
4646 li render key, val.result
4747 }
4848 }
176176 There is less of a concrete guideline for implementing Builtins because there
177177 are a lot more options, and it really depends a lot on what the Builtin should
178178 achieve. Nevertheless, a good starting point is to read the `Builtin` class
179 documentation, take a look at `Builtin`s in `alv/builtin.moon` and get
179 documentation, take a look at `Builtin`s in `alv/builtins.moon` and get
180180 familiar with the relevant internal interfaces (especially `AST`, `Result`, and
181181 `Scope`).
182182