aboutsummaryrefslogtreecommitdiffstats
path: root/core/builtin.moon
blob: babe2e4626205b8aa05195ceb0393eb0e4680974 (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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
----
-- Builtin `Action`s and `Op`s.
--
-- Please see the [reference](../../reference/index.html#builtins) for
-- documentation.
--
-- @module builtin
import Action, Op, FnDef, Input, val from require 'core.base'
import ValueStream, LiteralValue from require 'core.stream.value'
import Result from require 'core.result'
import Cell from require 'core.cell'
import Scope from require 'core.scope'
import Tag from require 'core.tag'
import op_invoke from require 'core.invoke'

doc = ValueStream.meta
  meta:
    name: 'doc'
    summary: "Print documentation in console."
    examples: { '(doc sym)' }
    description: "Print the documentation for `sym` to the console"

  value: class extends Action
    format_meta = =>
      str = @summary
      if @examples
        for example in *@examples
          str ..= '\n' .. example
      if @description
        str ..= '\n' .. @description\match '^\n*(.+)\n*$'
      str

    eval: (scope, tail) =>
      assert #tail == 1, "'doc' takes exactly one parameter"

      result = L\push tail[1]\eval, scope
      with Result children: { def }
        meta = result.value.meta
        L\print "(doc #{tail[1]}):\n#{format_meta meta}\n"

def = ValueStream.meta
  meta:
    name: 'def'
    summary: "Declare symbols in current scope."
    examples: { '(def sym1 val-expr1 [sym2 val-expr2…])' }
    description: "
Define the symbols `sym1`, `sym2`, … to resolve to the values of `val-expr1`,
`val-expr2`, …."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail > 1, "'def' requires at least 2 arguments"
      assert #tail % 2 == 0, "'def' requires an even number of arguments"

      children = L\push ->
        return for i=1,#tail,2
          name, val_expr = tail[i], tail[i+1]
          name = (name\quote scope)\unwrap 'sym'

          with val_expr\eval scope
            scope\set name, \make_ref!

      Result :children

use = ValueStream.meta
  meta:
    name: 'use'
    summary: "Merge scopes into current scope."
    examples: { '(use scope1 [scope2…])' }
    description: "
Copy all symbol definitions from `scope1`, `scope2`, … to the current scope.
All arguments have to be evaltime constant."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      for child in *tail
        result = L\push child\eval, scope
        value = result\const!
        scope\use value\unwrap 'scope', "'use' only works on scopes"

      Result!

require_ = ValueStream.meta
  meta:
    name: 'require'
    summary: "Load a module."
    examples: { '(require name)' }
    description: "Load a module and return its scope."

  value: class extends Action
    eval: (scope,  tail) =>
      L\trace "evaling #{@}"
      assert #tail == 1, "'require' takes exactly one parameter"

      result = L\push tail[1]\eval, scope
      name = result\const!

      L\trace @, "loading module #{name}"
      scope = ValueStream.wrap require "lib.#{name\unwrap 'str'}"
      Result :value

import_ = ValueStream.meta
  meta:
    name: 'import'
    summary: "Require and define modules."
    examples: { '(import sym1 [sym2…])' }
    description: "
Requires modules `sym1`, `sym2`, … and define them as `sym1`, `sym2`, … in the
current scope."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail > 0, "'import' requires at least one arguments"

      for child in *tail
        name = (child\quote scope)\unwrap 'sym'
        value = ValueStream.wrap require "lib.#{name}"
        scope\set name, Result :value -- (require "lib.#{name})\unwrap 'scope'
      Result!

import_star = ValueStream.meta
  meta:
    name: 'import*'
    summary: "Require and use modules."
    examples: { '(import* sym1 [sym2…])' }
    description: "
Requires modules `sym1`, `sym2`, … and merges them into the current scope."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail > 0, "'import' requires at least one arguments"


      for child in *tail
        name = (child\quote scope)\unwrap 'sym'
        value = ValueStream.wrap require "lib.#{name}"
        scope\use value\unwrap 'scope' -- (require "lib.#{name}")\unwrap 'scope'

      Result!

fn = ValueStream.meta
  meta:
    name: 'fn'
    summary: "Declare a function."
    examples: { '(fn (p1 [p2…]) body-expr)' }
    description: "
The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
function is invoked."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail == 2, "'fn' takes exactly two arguments"
      { params, body } = tail

      assert params.__class == Cell, "'fn's first argument has to be an expression"
      param_symbols = for param in *params.children
        assert param.type == 'sym', "function parameter declaration has to be a symbol"
        param\quote scope

      body = body\quote scope
      Result value: with ValueStream.wrap FnDef param_symbols, body, scope
        .meta = {
          summary: "(user defined function)"
          examples: { "(??? #{table.concat [p! for p in *param_symbols], ' '})" }
        }

defn = ValueStream.meta
  meta:
    name: 'defn'
    summary: "Define a function."
    examples: { '(defn name-sym (p1 [p2…]) body-expr)' }
    description: "
Declare a function and define it as `name-sym` in the current scope.
The symbols `p1`, `p2`, ... will resolve to the arguments passed when the
function is invoked."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail == 3, "'defn' takes exactly three arguments"
      { name, params, body } = tail

      name = (name\quote scope)\unwrap 'sym'
      assert params.__class == Cell, "'defn's second argument has to be an expression"
      param_symbols = for param in *params.children
        assert param.type == 'sym', "function parameter declaration has to be a symbol"
        param\quote scope

      body = body\quote scope

      value = with ValueStream.wrap FnDef param_symbols, body, scope
        .meta =
          :name
          summary: "(user defined function)"
          examples: { "(#{name} #{table.concat [p! for p in *param_symbols], ' '})" }

      scope\set name, Result :value
      Result!

do_expr = ValueStream.meta
  meta:
    name: 'do_expr'
    summary: "Evaluate multiple expressions in a new scope."
    examples: { '(do expr1 [expr2…])' }
    description: "
Evaluate `expr1`, `expr2`, … and return the value of the last expression."

  value: class  extends Action
    eval: (scope, tail) =>
      scope = Scope scope
      Result children: [expr\eval scope for expr in *tail]

if_ = ValueStream.meta
  meta:
    name: 'if'
    summary: "Make an evaltime const choice."
    examples: { '(if bool then-expr [else-expr])' }
    description: "
`bool` has to be an evaltime constant. If it is truthy, this expression is equivalent
to `then-expr`, otherwise it is equivalent to `else-xpr` if given, or nil otherwise."

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail >= 2, "'if' needs at least two parameters"
      assert #tail <= 3, "'if' needs at most three parameters"

      { xif, xthen, xelse } = tail

      xif = L\push xif\eval, scope
      xif = xif\const!\unwrap!

      if xif
        xthen\eval scope
      elseif xelse
        xelse\eval scope

trace_ = ValueStream.meta
  meta:
    name: 'trace!'
    summary: "Trace an expression's value at evaltime."
    examples: { '(trace! expr)' }

  value: class extends Action
    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail == 1, "'trace!' takes exactly one parameter"

      with result = L\push tail[1]\eval, scope
        L\print "trace! #{tail[1]\stringify!}: #{result.value}"

trace = ValueStream.meta
  meta:
    name: 'trace'
    summary: "Trace an expression's values at runtime."
    examples: { '(trace expr)' }

  value: class extends Action
    class traceOp extends Op
      setup: (inputs) =>
        super
          prefix: Input.cold inputs[1]
          value: Input.hot inputs[2]

      tick: =>
        L\print "trace #{@inputs.prefix!}: #{@inputs.value.stream}"

    eval: (scope, tail) =>
      L\trace "evaling #{@}"
      assert #tail == 1, "'trace!' takes exactly one parameter"

      tag = @tag\clone Tag.parse '-1'
      inner = Cell tag, {
        LiteralValue 'opdef', traceOp, 'trace'
        ValueStream.str tostring tail[1]
        tail[1]
      }
      inner\eval scope

print = ValueStream.meta
  meta:
    name: 'print'
    summary: "Print string values."
    examples: { '(print str)' }

  value: class extends Op
    setup: (inputs) =>
      value = val.str\match inputs
      super value: Input.hot value

    tick: => print @inputs.value!

{
  :doc
  :trace, 'trace!': trace_, :print

  :def, :use
  require: require_
  import: import_
  'import*': import_star

  true: ValueStream.meta
    meta:
      name: 'true'
      summary: "The boolean constant `true`."
    value: ValueStream.bool true

  false: ValueStream.meta
    meta:
      name: 'false'
      summary: "The boolean constant `false`."
    value: ValueStream.bool false

  bang: ValueStream.meta
    meta:
      name: 'bang'
      summary: "A `bang` value-constant."
    value: ValueStream 'bang', true

  :fn, :defn
  'do': do_expr
  if: if_
}