fez-lisp 1.6.27 → 1.6.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -4
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/debugger.js +1191 -0
- package/src/keywords.js +0 -2
- package/src/parser.js +9 -0
- package/src/types.js +0 -18
- package/src/utils.js +13 -7
package/src/debugger.js
ADDED
@@ -0,0 +1,1191 @@
|
|
1
|
+
import stdT from '../lib/baked/std-T.js'
|
2
|
+
import std from '../lib/baked/std.js'
|
3
|
+
import { typeCheck } from './check.js'
|
4
|
+
import { enhance } from './enhance.js'
|
5
|
+
import {
|
6
|
+
APPLY,
|
7
|
+
ATOM,
|
8
|
+
KEYWORDS,
|
9
|
+
TYPE,
|
10
|
+
VALUE,
|
11
|
+
WORD,
|
12
|
+
TRUE,
|
13
|
+
FALSE,
|
14
|
+
STATIC_TYPES,
|
15
|
+
DEBUG,
|
16
|
+
SPECIAL_FORMS_SET
|
17
|
+
} from './keywords.js'
|
18
|
+
import { isLeaf, LISP } from './parser.js'
|
19
|
+
import { definedTypes, filteredDefinedTypes, withCtxTypes } from './types.js'
|
20
|
+
import { isForbiddenVariableName } from './utils.js'
|
21
|
+
const keywords = {
|
22
|
+
[KEYWORDS.LOOP]: (args, env) => {
|
23
|
+
if (args.length != 2)
|
24
|
+
throw new RangeError(`Wrong number of args to ${KEYWORDS.LOOP}`)
|
25
|
+
while (evaluate(args[0], env) === TRUE) evaluate(args[1], env)
|
26
|
+
return -1
|
27
|
+
},
|
28
|
+
[KEYWORDS.ADDITION]: (args, env) => {
|
29
|
+
if (args.length !== 2)
|
30
|
+
throw new RangeError(
|
31
|
+
`Invalid number of arguments for (${
|
32
|
+
KEYWORDS.ADDITION
|
33
|
+
}), expected (= 2) but got ${args.length}\n\n(${
|
34
|
+
KEYWORDS.ADDITION
|
35
|
+
} ${stringifyArgs(args)})`
|
36
|
+
)
|
37
|
+
const a = evaluate(args[0], env)
|
38
|
+
if (typeof a !== 'number')
|
39
|
+
throw new TypeError(
|
40
|
+
`First arguments of (${KEYWORDS.ADDITION}) must be a (${
|
41
|
+
RUNTIME_TYPES.NUMBER
|
42
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
43
|
+
KEYWORDS.ADDITION
|
44
|
+
} ${stringifyArgs(args)})`
|
45
|
+
)
|
46
|
+
const b = evaluate(args[1], env)
|
47
|
+
if (typeof b !== 'number')
|
48
|
+
throw new TypeError(
|
49
|
+
`Second arguments of (${KEYWORDS.ADDITION}) must be a (${
|
50
|
+
RUNTIME_TYPES.NUMBER
|
51
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
52
|
+
KEYWORDS.ADDITION
|
53
|
+
} ${stringifyArgs(args)})`
|
54
|
+
)
|
55
|
+
return a + b
|
56
|
+
},
|
57
|
+
[KEYWORDS.MULTIPLICATION]: (args, env) => {
|
58
|
+
if (args.length !== 2)
|
59
|
+
throw new RangeError(
|
60
|
+
`Invalid number of arguments for (${
|
61
|
+
KEYWORDS.MULTIPLICATION
|
62
|
+
}), expected (= 2) but got ${args.length}\n\n(${
|
63
|
+
KEYWORDS.MULTIPLICATION
|
64
|
+
} ${stringifyArgs(args)})`
|
65
|
+
)
|
66
|
+
const a = evaluate(args[0], env)
|
67
|
+
if (typeof a !== 'number')
|
68
|
+
throw new TypeError(
|
69
|
+
`First arguments of (${KEYWORDS.MULTIPLICATION}) must be a (${
|
70
|
+
RUNTIME_TYPES.NUMBER
|
71
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
72
|
+
KEYWORDS.MULTIPLICATION
|
73
|
+
} ${stringifyArgs(args)})`
|
74
|
+
)
|
75
|
+
const b = evaluate(args[1], env)
|
76
|
+
if (typeof b !== 'number')
|
77
|
+
throw new TypeError(
|
78
|
+
`Second arguments of (${KEYWORDS.MULTIPLICATION}) must be a (${
|
79
|
+
RUNTIME_TYPES.NUMBER
|
80
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
81
|
+
KEYWORDS.MULTIPLICATION
|
82
|
+
} ${stringifyArgs(args)})`
|
83
|
+
)
|
84
|
+
return a * b
|
85
|
+
},
|
86
|
+
[KEYWORDS.SUBTRACTION]: (args, env) => {
|
87
|
+
if (args.length !== 2)
|
88
|
+
throw new RangeError(
|
89
|
+
`Invalid number of arguments for (${
|
90
|
+
KEYWORDS.SUBTRACTION
|
91
|
+
}), expected (= 2) but got ${args.length}\n\n(${
|
92
|
+
KEYWORDS.SUBTRACTION
|
93
|
+
} ${stringifyArgs(args)})`
|
94
|
+
)
|
95
|
+
const a = evaluate(args[0], env)
|
96
|
+
if (typeof a !== 'number')
|
97
|
+
throw new TypeError(
|
98
|
+
`First argument of (${KEYWORDS.SUBTRACTION}) must be a (${
|
99
|
+
RUNTIME_TYPES.NUMBER
|
100
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
101
|
+
KEYWORDS.SUBTRACTION
|
102
|
+
} ${stringifyArgs(args)})`
|
103
|
+
)
|
104
|
+
const b = evaluate(args[1], env)
|
105
|
+
if (typeof b !== 'number')
|
106
|
+
throw new TypeError(
|
107
|
+
`Second argument of (${KEYWORDS.SUBTRACTION}) must be a (${
|
108
|
+
RUNTIME_TYPES.NUMBER
|
109
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
110
|
+
KEYWORDS.SUBTRACTION
|
111
|
+
} ${stringifyArgs(args)})`
|
112
|
+
)
|
113
|
+
return a - b
|
114
|
+
},
|
115
|
+
[KEYWORDS.DIVISION]: (args, env) => {
|
116
|
+
if (args.length !== 2)
|
117
|
+
throw new RangeError(
|
118
|
+
`Invalid number of arguments for (${
|
119
|
+
KEYWORDS.DIVISION
|
120
|
+
}), expected (= 2) but got ${args.length}\n\n(${
|
121
|
+
KEYWORDS.DIVISION
|
122
|
+
} ${stringifyArgs(args)})`
|
123
|
+
)
|
124
|
+
const a = evaluate(args[0], env)
|
125
|
+
if (typeof a !== 'number')
|
126
|
+
throw new TypeError(
|
127
|
+
`First argument of (${KEYWORDS.DIVISION}) is not (${
|
128
|
+
RUNTIME_TYPES.NUMBER
|
129
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
130
|
+
KEYWORDS.DIVISION
|
131
|
+
} ${stringifyArgs(args)})`
|
132
|
+
)
|
133
|
+
const b = evaluate(args[1], env)
|
134
|
+
if (typeof b !== 'number')
|
135
|
+
throw new TypeError(
|
136
|
+
`Second argument of (${KEYWORDS.DIVISION}) is not (${
|
137
|
+
RUNTIME_TYPES.NUMBER
|
138
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
139
|
+
KEYWORDS.DIVISION
|
140
|
+
} ${stringifyArgs(args)})`
|
141
|
+
)
|
142
|
+
if (b === 0)
|
143
|
+
throw new TypeError(
|
144
|
+
`Second Argument of (${
|
145
|
+
KEYWORDS.DIVISION
|
146
|
+
}) can't be a 0 (division by 0 is not allowed) but ${LISP.source(
|
147
|
+
args[1]
|
148
|
+
)} is 0\n\n(${KEYWORDS.DIVISION} ${stringifyArgs(args)})`
|
149
|
+
)
|
150
|
+
return a / b
|
151
|
+
},
|
152
|
+
[KEYWORDS.REMAINDER_OF_DIVISION]: (args, env) => {
|
153
|
+
if (args.length !== 2)
|
154
|
+
throw new RangeError(
|
155
|
+
`Invalid number of arguments for (${
|
156
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
157
|
+
}), expected (= 2) but got ${args.length}\n\n(${
|
158
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
159
|
+
} ${stringifyArgs(args)})`
|
160
|
+
)
|
161
|
+
const a = evaluate(args[0], env)
|
162
|
+
if (typeof a !== 'number')
|
163
|
+
throw new TypeError(
|
164
|
+
`First argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
165
|
+
RUNTIME_TYPES.NUMBER
|
166
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
167
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
168
|
+
} ${stringifyArgs(args)})`
|
169
|
+
)
|
170
|
+
const b = evaluate(args[1], env)
|
171
|
+
if (typeof b !== 'number')
|
172
|
+
throw new TypeError(
|
173
|
+
`Second argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
174
|
+
RUNTIME_TYPES.NUMBER
|
175
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
176
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
177
|
+
} ${stringifyArgs(args)})`
|
178
|
+
)
|
179
|
+
if (b === 0)
|
180
|
+
throw new TypeError(
|
181
|
+
`Second argument of (${
|
182
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
183
|
+
}) can't be a 0 (division by 0 is not allowed) but ${LISP.source(
|
184
|
+
args[1]
|
185
|
+
)} is 0\n\n(${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
|
186
|
+
)
|
187
|
+
|
188
|
+
return a % b
|
189
|
+
},
|
190
|
+
[KEYWORDS.BITWISE_AND]: (args, env) => {
|
191
|
+
if (args.length !== 2)
|
192
|
+
throw new RangeError(
|
193
|
+
`Invalid number of arguments to (${
|
194
|
+
KEYWORDS.BITWISE_AND
|
195
|
+
}) (= 2 required)\n\n(${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
|
196
|
+
)
|
197
|
+
const a = evaluate(args[0], env)
|
198
|
+
if (typeof a !== 'number')
|
199
|
+
throw new TypeError(
|
200
|
+
`First arguments of (${KEYWORDS.BITWISE_AND}) must be a (${
|
201
|
+
RUNTIME_TYPES.NUMBER
|
202
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
203
|
+
KEYWORDS.BITWISE_AND
|
204
|
+
} ${stringifyArgs(args)})`
|
205
|
+
)
|
206
|
+
const b = evaluate(args[1], env)
|
207
|
+
if (typeof b !== 'number')
|
208
|
+
throw new TypeError(
|
209
|
+
`Second arguments of (${KEYWORDS.BITWISE_AND}) must be a (${
|
210
|
+
RUNTIME_TYPES.NUMBER
|
211
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
212
|
+
KEYWORDS.BITWISE_AND
|
213
|
+
} ${stringifyArgs(args)})`
|
214
|
+
)
|
215
|
+
return a & b
|
216
|
+
},
|
217
|
+
[KEYWORDS.BITWISE_NOT]: (args, env) => {
|
218
|
+
if (args.length !== 1)
|
219
|
+
throw new RangeError(
|
220
|
+
`Invalid number of arguments to (${
|
221
|
+
KEYWORDS.BITWISE_NOT
|
222
|
+
}) (= 1 required)\n\n(${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
|
223
|
+
)
|
224
|
+
const operand = evaluate(args[0], env)
|
225
|
+
if (typeof operand !== 'number')
|
226
|
+
throw new TypeError(
|
227
|
+
`Argument of (${KEYWORDS.BITWISE_NOT}) must be a (${
|
228
|
+
RUNTIME_TYPES.NUMBER
|
229
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
230
|
+
KEYWORDS.BITWISE_NOT
|
231
|
+
} ${stringifyArgs(args)})`
|
232
|
+
)
|
233
|
+
return ~operand
|
234
|
+
},
|
235
|
+
[KEYWORDS.BITWISE_OR]: (args, env) => {
|
236
|
+
if (args.length !== 2)
|
237
|
+
throw new RangeError(
|
238
|
+
`Invalid number of arguments to (${
|
239
|
+
KEYWORDS.BITWISE_OR
|
240
|
+
}) (= 2 required)\n\n(${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
241
|
+
)
|
242
|
+
const a = evaluate(args[0], env)
|
243
|
+
if (typeof a !== 'number')
|
244
|
+
throw new TypeError(
|
245
|
+
`First arguments of (${KEYWORDS.BITWISE_OR}) must be a (${
|
246
|
+
RUNTIME_TYPES.NUMBER
|
247
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
248
|
+
KEYWORDS.BITWISE_OR
|
249
|
+
} ${stringifyArgs(args)})`
|
250
|
+
)
|
251
|
+
const b = evaluate(args[1], env)
|
252
|
+
if (typeof b !== 'number')
|
253
|
+
throw new TypeError(
|
254
|
+
`Second arguments of (${KEYWORDS.BITWISE_OR}) must be a (${
|
255
|
+
RUNTIME_TYPES.NUMBER
|
256
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
257
|
+
KEYWORDS.BITWISE_OR
|
258
|
+
} ${stringifyArgs(args)})`
|
259
|
+
)
|
260
|
+
return a | b
|
261
|
+
},
|
262
|
+
[KEYWORDS.BITWISE_XOR]: (args, env) => {
|
263
|
+
if (args.length !== 2)
|
264
|
+
throw new RangeError(
|
265
|
+
`Invalid number of arguments to (${
|
266
|
+
KEYWORDS.BITWISE_XOR
|
267
|
+
}) (= 2 required)\n\n(${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
268
|
+
)
|
269
|
+
const a = evaluate(args[0], env)
|
270
|
+
if (typeof a !== 'number')
|
271
|
+
throw new TypeError(
|
272
|
+
`First arguments of (${KEYWORDS.BITWISE_XOR}) must be a (${
|
273
|
+
RUNTIME_TYPES.NUMBER
|
274
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
275
|
+
KEYWORDS.BITWISE_XOR
|
276
|
+
} ${stringifyArgs(args)})`
|
277
|
+
)
|
278
|
+
const b = evaluate(args[1], env)
|
279
|
+
if (typeof b !== 'number')
|
280
|
+
throw new TypeError(
|
281
|
+
`Second arguments of (${KEYWORDS.BITWISE_XOR}) must be a (${
|
282
|
+
RUNTIME_TYPES.NUMBER
|
283
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
284
|
+
KEYWORDS.BITWISE_XOR
|
285
|
+
} ${stringifyArgs(args)})`
|
286
|
+
)
|
287
|
+
return a ^ b
|
288
|
+
},
|
289
|
+
[KEYWORDS.BITWISE_LEFT_SHIFT]: (args, env) => {
|
290
|
+
if (args.length !== 2)
|
291
|
+
throw new RangeError(
|
292
|
+
`Invalid number of arguments to (${
|
293
|
+
KEYWORDS.BITWISE_LEFT_SHIFT
|
294
|
+
}) (= 2 required)\n\n(${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(
|
295
|
+
args
|
296
|
+
)})`
|
297
|
+
)
|
298
|
+
const a = evaluate(args[0], env)
|
299
|
+
if (typeof a !== 'number')
|
300
|
+
throw new TypeError(
|
301
|
+
`First arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) must be a (${
|
302
|
+
RUNTIME_TYPES.NUMBER
|
303
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
304
|
+
KEYWORDS.BITWISE_LEFT_SHIFT
|
305
|
+
} ${stringifyArgs(args)})`
|
306
|
+
)
|
307
|
+
const b = evaluate(args[1], env)
|
308
|
+
if (typeof b !== 'number')
|
309
|
+
throw new TypeError(
|
310
|
+
`Second arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) must be a (${
|
311
|
+
RUNTIME_TYPES.NUMBER
|
312
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
313
|
+
KEYWORDS.BITWISE_LEFT_SHIFT
|
314
|
+
} ${stringifyArgs(args)})`
|
315
|
+
)
|
316
|
+
return a << b
|
317
|
+
},
|
318
|
+
[KEYWORDS.BITWISE_RIGHT_SHIFT]: (args, env) => {
|
319
|
+
if (args.length !== 2)
|
320
|
+
throw new RangeError(
|
321
|
+
`Invalid number of arguments to (${
|
322
|
+
KEYWORDS.BITWISE_RIGHT_SHIFT
|
323
|
+
}) (= 2 required)\n\n(${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(
|
324
|
+
args
|
325
|
+
)})`
|
326
|
+
)
|
327
|
+
const a = evaluate(args[0], env)
|
328
|
+
if (typeof a !== 'number')
|
329
|
+
throw new TypeError(
|
330
|
+
`First arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) must be a (${
|
331
|
+
RUNTIME_TYPES.NUMBER
|
332
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
333
|
+
KEYWORDS.BITWISE_RIGHT_SHIFT
|
334
|
+
} ${stringifyArgs(args)})`
|
335
|
+
)
|
336
|
+
const b = evaluate(args[1], env)
|
337
|
+
if (typeof b !== 'number')
|
338
|
+
throw new TypeError(
|
339
|
+
`Second arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) must be a (${
|
340
|
+
RUNTIME_TYPES.NUMBER
|
341
|
+
}) but ${LISP.source(args[1])} is not\n\n(${
|
342
|
+
KEYWORDS.BITWISE_RIGHT_SHIFT
|
343
|
+
} ${stringifyArgs(args)})`
|
344
|
+
)
|
345
|
+
return a >> b
|
346
|
+
},
|
347
|
+
[KEYWORDS.CREATE_ARRAY]: (args, env) => {
|
348
|
+
return args.length ? args.map((x) => evaluate(x, env)) : []
|
349
|
+
},
|
350
|
+
[KEYWORDS.GET_ARRAY]: (args, env) => {
|
351
|
+
if (args.length !== 2)
|
352
|
+
throw new RangeError(
|
353
|
+
`Invalid number of arguments for (${
|
354
|
+
KEYWORDS.GET_ARRAY
|
355
|
+
}) (= 2 required)\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
356
|
+
)
|
357
|
+
const array = evaluate(args[0], env)
|
358
|
+
if (!Array.isArray(array))
|
359
|
+
throw new TypeError(
|
360
|
+
`First argument of (${KEYWORDS.GET_ARRAY}) must be an (${
|
361
|
+
RUNTIME_TYPES.ARRAY
|
362
|
+
})\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
363
|
+
)
|
364
|
+
if (array.length === 0)
|
365
|
+
throw new RangeError(
|
366
|
+
`First argument of (${KEYWORDS.GET_ARRAY}) is an empty (${
|
367
|
+
RUNTIME_TYPES.ARRAY
|
368
|
+
})\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
369
|
+
)
|
370
|
+
const index = evaluate(args[1], env)
|
371
|
+
if (!Number.isInteger(index) || index < 0)
|
372
|
+
throw new TypeError(
|
373
|
+
`Second argument of (${
|
374
|
+
KEYWORDS.GET_ARRAY
|
375
|
+
}) must be a positive (interger ${
|
376
|
+
RUNTIME_TYPES.NUMBER
|
377
|
+
}) (= i ${index})\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
378
|
+
)
|
379
|
+
if (index > array.length - 1)
|
380
|
+
throw new RangeError(
|
381
|
+
`Second argument of (${KEYWORDS.GET_ARRAY}) is outside of (${
|
382
|
+
RUNTIME_TYPES.ARRAY
|
383
|
+
}) bounds (= i ${index}) expected (and (>= i 0) (< i ${
|
384
|
+
array.length
|
385
|
+
}))\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
386
|
+
)
|
387
|
+
const value = array.at(index)
|
388
|
+
if (value == undefined)
|
389
|
+
throw new RangeError(
|
390
|
+
`Trying to get a null value in (${RUNTIME_TYPES.ARRAY}) at (${
|
391
|
+
KEYWORDS.GET_ARRAY
|
392
|
+
})\n\n(${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
393
|
+
)
|
394
|
+
return value
|
395
|
+
},
|
396
|
+
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
397
|
+
if (args.length !== 3)
|
398
|
+
throw new RangeError(
|
399
|
+
`Invalid number of arguments for (${
|
400
|
+
KEYWORDS.SET_ARRAY
|
401
|
+
}) (= 3) required\n\n(${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
402
|
+
)
|
403
|
+
const array = evaluate(args[0], env)
|
404
|
+
if (!Array.isArray(array))
|
405
|
+
throw new TypeError(
|
406
|
+
`First argument of (${KEYWORDS.SET_ARRAY}) must be an (${
|
407
|
+
RUNTIME_TYPES.ARRAY
|
408
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
409
|
+
KEYWORDS.SET_ARRAY
|
410
|
+
} ${stringifyArgs(args)})`
|
411
|
+
)
|
412
|
+
const index = evaluate(args[1], env)
|
413
|
+
if (!Number.isInteger(index) || index < 0)
|
414
|
+
throw new TypeError(
|
415
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) must be a positive (${
|
416
|
+
RUNTIME_TYPES.NUMBER
|
417
|
+
} integer) (= i ${index}) but ${LISP.source(args[1])} is not\n\n(${
|
418
|
+
KEYWORDS.SET_ARRAY
|
419
|
+
} ${stringifyArgs(args)})`
|
420
|
+
)
|
421
|
+
if (index > array.length)
|
422
|
+
throw new RangeError(
|
423
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
424
|
+
RUNTIME_TYPES.ARRAY
|
425
|
+
}) bounds (${index}) expected (and (>= i 0) (< i ${
|
426
|
+
array.length
|
427
|
+
})) but ${LISP.source(args[1])} is not\n\n(${
|
428
|
+
KEYWORDS.SET_ARRAY
|
429
|
+
} ${stringifyArgs(args)})`
|
430
|
+
)
|
431
|
+
const value = evaluate(args[2], env)
|
432
|
+
if (value == undefined)
|
433
|
+
throw new RangeError(
|
434
|
+
`Trying to set a null value in (${RUNTIME_TYPES.ARRAY}) at (${
|
435
|
+
KEYWORDS.SET_ARRAY
|
436
|
+
})\n\n(${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
437
|
+
)
|
438
|
+
array[index] = value
|
439
|
+
return array
|
440
|
+
},
|
441
|
+
[KEYWORDS.POP_ARRAY]: (args, env) => {
|
442
|
+
if (args.length !== 1)
|
443
|
+
throw new RangeError(
|
444
|
+
`Invalid number of arguments for (${
|
445
|
+
KEYWORDS.POP_ARRAY
|
446
|
+
}) (= 1) required\n\n(${KEYWORDS.POP_ARRAY} ${stringifyArgs(args)})`
|
447
|
+
)
|
448
|
+
const array = evaluate(args[0], env)
|
449
|
+
if (!Array.isArray(array))
|
450
|
+
throw new TypeError(
|
451
|
+
`First argument of (${KEYWORDS.POP_ARRAY}) must be an (${
|
452
|
+
RUNTIME_TYPES.ARRAY
|
453
|
+
}) but ${LISP.source(args[0])} is not\n\n(${
|
454
|
+
KEYWORDS.POP_ARRAY
|
455
|
+
} ${stringifyArgs(args)})`
|
456
|
+
)
|
457
|
+
array.pop()
|
458
|
+
return array
|
459
|
+
},
|
460
|
+
[KEYWORDS.ARRAY_LENGTH]: (args, env) => {
|
461
|
+
if (args.length !== 1)
|
462
|
+
throw new RangeError(
|
463
|
+
`Invalid number of arguments for (${
|
464
|
+
KEYWORDS.ARRAY_LENGTH
|
465
|
+
}) (= 1 required)\n\n(${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
|
466
|
+
)
|
467
|
+
const array = evaluate(args[0], env)
|
468
|
+
if (!Array.isArray(array))
|
469
|
+
throw new TypeError(
|
470
|
+
`First argument of (${KEYWORDS.ARRAY_LENGTH}) must be an ${
|
471
|
+
RUNTIME_TYPES.ARRAY
|
472
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
473
|
+
KEYWORDS.ARRAY_LENGTH
|
474
|
+
} ${stringifyArgs(args)})`
|
475
|
+
)
|
476
|
+
return array.length
|
477
|
+
},
|
478
|
+
[KEYWORDS.IF]: (args, env) => {
|
479
|
+
if (args.length !== 3)
|
480
|
+
throw new RangeError(
|
481
|
+
`Invalid number of arguments for (${
|
482
|
+
KEYWORDS.IF
|
483
|
+
}), expected (= 3) but got ${args.length}\n\n(${
|
484
|
+
KEYWORDS.IF
|
485
|
+
} ${stringifyArgs(args)})`
|
486
|
+
)
|
487
|
+
const condition = evaluate(args[0], env)
|
488
|
+
if (condition !== FALSE && condition !== TRUE)
|
489
|
+
throw new TypeError(
|
490
|
+
`Condition of (${
|
491
|
+
KEYWORDS.IF
|
492
|
+
}) must be ${TRUE} or ${FALSE} but ${LISP.source(args[0])} is not\n\n(${
|
493
|
+
KEYWORDS.IF
|
494
|
+
} ${stringifyArgs(args)})`
|
495
|
+
)
|
496
|
+
return condition ? evaluate(args[1], env) : evaluate(args[2], env)
|
497
|
+
},
|
498
|
+
[KEYWORDS.NOT]: (args, env) => {
|
499
|
+
if (args.length !== 1)
|
500
|
+
throw new RangeError(
|
501
|
+
`Invalid number of arguments for (${KEYWORDS.NOT}) (= 1 required)\n\n(${
|
502
|
+
KEYWORDS.NOT
|
503
|
+
} ${stringifyArgs(args)})`
|
504
|
+
)
|
505
|
+
const operand = evaluate(args[0], env)
|
506
|
+
if (operand !== FALSE && operand !== TRUE)
|
507
|
+
throw new TypeError(
|
508
|
+
`Condition of (${
|
509
|
+
KEYWORDS.NOT
|
510
|
+
}) must be ${TRUE} or ${FALSE} but ${LISP.source(args[0])} is not\n\n(${
|
511
|
+
KEYWORDS.NOT
|
512
|
+
} ${stringifyArgs(args)})`
|
513
|
+
)
|
514
|
+
return +!operand
|
515
|
+
},
|
516
|
+
[KEYWORDS.EQUAL]: (args, env) => {
|
517
|
+
if (args.length !== 2)
|
518
|
+
throw new RangeError(
|
519
|
+
`Invalid number of arguments for (${
|
520
|
+
KEYWORDS.EQUAL
|
521
|
+
}) (= 2 required)\n\n(${KEYWORDS.EQUAL} ${stringifyArgs(args)})`
|
522
|
+
)
|
523
|
+
const a = evaluate(args[0], env)
|
524
|
+
const b = evaluate(args[1], env)
|
525
|
+
if (typeof a !== 'number')
|
526
|
+
throw new TypeError(
|
527
|
+
`First argument of (${KEYWORDS.EQUAL}) must be a ${
|
528
|
+
RUNTIME_TYPES.NUMBER
|
529
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
530
|
+
KEYWORDS.EQUAL
|
531
|
+
} ${stringifyArgs(args)})`
|
532
|
+
)
|
533
|
+
if (typeof b !== 'number')
|
534
|
+
throw new TypeError(
|
535
|
+
`Second argument of (${KEYWORDS.EQUAL}) must be a ${
|
536
|
+
RUNTIME_TYPES.NUMBER
|
537
|
+
} but ${LISP.source(args[1])} is not\n\n(${
|
538
|
+
KEYWORDS.EQUAL
|
539
|
+
} ${stringifyArgs(args)})`
|
540
|
+
)
|
541
|
+
return +(a === b)
|
542
|
+
},
|
543
|
+
[KEYWORDS.LESS_THAN]: (args, env) => {
|
544
|
+
if (args.length !== 2)
|
545
|
+
throw new RangeError(
|
546
|
+
`Invalid number of arguments for (${
|
547
|
+
KEYWORDS.LESS_THAN
|
548
|
+
}) (= 2 required)\n\n(${KEYWORDS.LESS_THAN} ${stringifyArgs(args)})`
|
549
|
+
)
|
550
|
+
const a = evaluate(args[0], env)
|
551
|
+
const b = evaluate(args[1], env)
|
552
|
+
if (typeof a !== 'number')
|
553
|
+
throw new TypeError(
|
554
|
+
`First argument of (${KEYWORDS.LESS_THAN}) must be a ${
|
555
|
+
RUNTIME_TYPES.NUMBER
|
556
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
557
|
+
KEYWORDS.LESS_THAN
|
558
|
+
} ${stringifyArgs(args)})`
|
559
|
+
)
|
560
|
+
if (typeof b !== 'number')
|
561
|
+
throw new TypeError(
|
562
|
+
`Second argument of (${KEYWORDS.LESS_THAN}) must be a ${
|
563
|
+
RUNTIME_TYPES.NUMBER
|
564
|
+
} but ${LISP.source(args[1])} is not\n\n(${
|
565
|
+
KEYWORDS.LESS_THAN
|
566
|
+
} ${stringifyArgs(args)})`
|
567
|
+
)
|
568
|
+
return +(a < b)
|
569
|
+
},
|
570
|
+
[KEYWORDS.GREATHER_THAN]: (args, env) => {
|
571
|
+
if (args.length !== 2)
|
572
|
+
throw new RangeError(
|
573
|
+
`Invalid number of arguments for (${
|
574
|
+
KEYWORDS.GREATHER_THAN
|
575
|
+
}) (= 2 required)\n\n(${KEYWORDS.GREATHER_THAN} ${stringifyArgs(args)})`
|
576
|
+
)
|
577
|
+
const a = evaluate(args[0], env)
|
578
|
+
const b = evaluate(args[1], env)
|
579
|
+
if (typeof a !== 'number')
|
580
|
+
throw new TypeError(
|
581
|
+
`First argument of (${KEYWORDS.GREATHER_THAN}) must be a ${
|
582
|
+
RUNTIME_TYPES.NUMBER
|
583
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
584
|
+
KEYWORDS.GREATHER_THAN
|
585
|
+
} ${stringifyArgs(args)})`
|
586
|
+
)
|
587
|
+
if (typeof b !== 'number')
|
588
|
+
throw new TypeError(
|
589
|
+
`Second argument of (${KEYWORDS.GREATHER_THAN}) must be a ${
|
590
|
+
RUNTIME_TYPES.NUMBER
|
591
|
+
} but ${LISP.source(args[1])} is not\n\n(${
|
592
|
+
KEYWORDS.GREATHER_THAN
|
593
|
+
} ${stringifyArgs(args)})`
|
594
|
+
)
|
595
|
+
return +(a > b)
|
596
|
+
},
|
597
|
+
[KEYWORDS.GREATHER_THAN_OR_EQUAL]: (args, env) => {
|
598
|
+
if (args.length !== 2)
|
599
|
+
throw new RangeError(
|
600
|
+
`Invalid number of arguments for (${
|
601
|
+
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
602
|
+
}) (= 2 required)\n\n(${
|
603
|
+
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
604
|
+
} ${stringifyArgs(args)})`
|
605
|
+
)
|
606
|
+
const a = evaluate(args[0], env)
|
607
|
+
const b = evaluate(args[1], env)
|
608
|
+
if (typeof a !== 'number')
|
609
|
+
throw new TypeError(
|
610
|
+
`First argument of (${KEYWORDS.GREATHER_THAN_OR_EQUAL}) must be a ${
|
611
|
+
RUNTIME_TYPES.NUMBER
|
612
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
613
|
+
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
614
|
+
} ${stringifyArgs(args)})`
|
615
|
+
)
|
616
|
+
if (typeof b !== 'number')
|
617
|
+
throw new TypeError(
|
618
|
+
`Second argument of (${KEYWORDS.GREATHER_THAN_OR_EQUAL}) must be a ${
|
619
|
+
RUNTIME_TYPES.NUMBER
|
620
|
+
} but ${LISP.source(args[1])} is not\n\n(${
|
621
|
+
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
622
|
+
} ${stringifyArgs(args)})`
|
623
|
+
)
|
624
|
+
return +(a >= b)
|
625
|
+
},
|
626
|
+
[KEYWORDS.LESS_THAN_OR_EQUAL]: (args, env) => {
|
627
|
+
if (args.length !== 2)
|
628
|
+
throw new RangeError(
|
629
|
+
`Invalid number of arguments for (${
|
630
|
+
KEYWORDS.LESS_THAN_OR_EQUAL
|
631
|
+
}) (= 2 required)\n\n(${KEYWORDS.LESS_THAN_OR_EQUAL} ${stringifyArgs(
|
632
|
+
args
|
633
|
+
)})`
|
634
|
+
)
|
635
|
+
const a = evaluate(args[0], env)
|
636
|
+
const b = evaluate(args[1], env)
|
637
|
+
if (typeof a !== 'number')
|
638
|
+
throw new TypeError(
|
639
|
+
`First argument of (${KEYWORDS.LESS_THAN_OR_EQUAL}) must be a ${
|
640
|
+
RUNTIME_TYPES.NUMBER
|
641
|
+
} but ${LISP.source(args[0])} is not\n\n(${
|
642
|
+
KEYWORDS.LESS_THAN_OR_EQUAL
|
643
|
+
} ${stringifyArgs(args)})`
|
644
|
+
)
|
645
|
+
if (typeof b !== 'number')
|
646
|
+
throw new TypeError(
|
647
|
+
`Second argument of (${KEYWORDS.LESS_THAN_OR_EQUAL}) must be a ${
|
648
|
+
RUNTIME_TYPES.NUMBER
|
649
|
+
} but ${LISP.source(args[1])} is not\n\n(${
|
650
|
+
KEYWORDS.LESS_THAN_OR_EQUAL
|
651
|
+
} ${stringifyArgs(args)})`
|
652
|
+
)
|
653
|
+
return +(a <= b)
|
654
|
+
},
|
655
|
+
[KEYWORDS.AND]: (args, env) => {
|
656
|
+
if (args.length !== 2)
|
657
|
+
throw new RangeError(
|
658
|
+
`Invalid number of arguments for (${KEYWORDS.AND}) (= 2 required)\n\n(${
|
659
|
+
KEYWORDS.AND
|
660
|
+
} ${stringifyArgs(args)})`
|
661
|
+
)
|
662
|
+
const a = evaluate(args[0], env)
|
663
|
+
if (a !== FALSE && a !== TRUE)
|
664
|
+
throw new TypeError(
|
665
|
+
`Condition of (${
|
666
|
+
KEYWORDS.AND
|
667
|
+
}) must be ${TRUE} or ${FALSE} but got\n\n(${
|
668
|
+
KEYWORDS.AND
|
669
|
+
} ${stringifyArgs(args)})`
|
670
|
+
)
|
671
|
+
if (!a) return FALSE
|
672
|
+
const b = evaluate(args[1], env)
|
673
|
+
if (b !== FALSE && b !== TRUE)
|
674
|
+
throw new TypeError(
|
675
|
+
`Condition of (${
|
676
|
+
KEYWORDS.AND
|
677
|
+
}) must be ${TRUE} or ${FALSE} but got\n\n(${
|
678
|
+
KEYWORDS.AND
|
679
|
+
} ${stringifyArgs(args)})`
|
680
|
+
)
|
681
|
+
return b
|
682
|
+
},
|
683
|
+
[KEYWORDS.OR]: (args, env) => {
|
684
|
+
if (args.length !== 2)
|
685
|
+
throw new RangeError(
|
686
|
+
`Invalid number of arguments for (${KEYWORDS.OR}) (= 2 required)\n\n(${
|
687
|
+
KEYWORDS.OR
|
688
|
+
} ${stringifyArgs(args)})`
|
689
|
+
)
|
690
|
+
const a = evaluate(args[0], env)
|
691
|
+
if (a !== FALSE && a !== TRUE)
|
692
|
+
throw new TypeError(
|
693
|
+
`Condition of (${
|
694
|
+
KEYWORDS.OR
|
695
|
+
}) must be ${TRUE} or ${FALSE} but got\n\n(${
|
696
|
+
KEYWORDS.OR
|
697
|
+
} ${stringifyArgs(args)})`
|
698
|
+
)
|
699
|
+
if (a) return TRUE
|
700
|
+
const b = evaluate(args[1], env)
|
701
|
+
if (b !== FALSE && b !== TRUE)
|
702
|
+
throw new TypeError(
|
703
|
+
`Condition of (${
|
704
|
+
KEYWORDS.OR
|
705
|
+
}) must be ${TRUE} or ${FALSE} but got\n\n(${
|
706
|
+
KEYWORDS.OR
|
707
|
+
} ${stringifyArgs(args)})`
|
708
|
+
)
|
709
|
+
return b
|
710
|
+
},
|
711
|
+
[KEYWORDS.DEFINE_VARIABLE]: (args, env) => {
|
712
|
+
if (args.length !== 2)
|
713
|
+
throw new RangeError(
|
714
|
+
`Invalid number of arguments to (${
|
715
|
+
KEYWORDS.DEFINE_VARIABLE
|
716
|
+
}) (= 2 required)\n\n(${KEYWORDS.DEFINE_VARIABLE} ${stringifyArgs(
|
717
|
+
args
|
718
|
+
)})`
|
719
|
+
)
|
720
|
+
const word = args[0]
|
721
|
+
const type = word[TYPE]
|
722
|
+
const name = word[VALUE]
|
723
|
+
if (type !== WORD)
|
724
|
+
throw new SyntaxError(
|
725
|
+
`First argument of (${KEYWORDS.DEFINE_VARIABLE}) must be word but got ${
|
726
|
+
TYPES[type]
|
727
|
+
}\n\n(${KEYWORDS.DEFINE_VARIABLE} ${stringifyArgs(args)})`
|
728
|
+
)
|
729
|
+
else if (isForbiddenVariableName(name))
|
730
|
+
throw new ReferenceError(
|
731
|
+
`Variable name ${name} is forbidden \n\n(${
|
732
|
+
KEYWORDS.DEFINE_VARIABLE
|
733
|
+
} ${stringifyArgs(args)})`
|
734
|
+
)
|
735
|
+
env[name] = evaluate(args[1], env)
|
736
|
+
return env[name]
|
737
|
+
},
|
738
|
+
[KEYWORDS.ANONYMOUS_FUNCTION]: (args, env) => {
|
739
|
+
if (!args.length)
|
740
|
+
throw new RangeError(
|
741
|
+
`At lest one argument (the body) is required for (${KEYWORDS.ANONYMOUS_FUNCTION})`
|
742
|
+
)
|
743
|
+
const params = args.slice(0, -1)
|
744
|
+
const body = args.at(-1)
|
745
|
+
return (props = [], scope, name) => {
|
746
|
+
if (props.length !== params.length)
|
747
|
+
throw new RangeError(
|
748
|
+
`Incorrect number of arguments for (${KEYWORDS.ANONYMOUS_FUNCTION}${
|
749
|
+
name ? ` ${name}` : ''
|
750
|
+
}) are provided (expects ${params.length} but got ${
|
751
|
+
props.length
|
752
|
+
})\n\n${
|
753
|
+
name
|
754
|
+
? `(${name} ${stringifyArgs(params)})`
|
755
|
+
: `(${KEYWORDS.ANONYMOUS_FUNCTION} ${stringifyArgs(params)})`
|
756
|
+
}`
|
757
|
+
)
|
758
|
+
const localEnv = Object.create(env)
|
759
|
+
for (let i = 0; i < props.length; ++i) {
|
760
|
+
const value = evaluate(props[i], scope)
|
761
|
+
localEnv[params[i][VALUE]] = value
|
762
|
+
}
|
763
|
+
return evaluate(body, localEnv)
|
764
|
+
}
|
765
|
+
},
|
766
|
+
[KEYWORDS.CALL_FUNCTION]: (args, env) => {
|
767
|
+
if (!args.length)
|
768
|
+
throw new RangeError(
|
769
|
+
`Invalid number of arguments to (${
|
770
|
+
KEYWORDS.CALL_FUNCTION
|
771
|
+
}) (>= 1 required)\n\n(${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(
|
772
|
+
args
|
773
|
+
)})`
|
774
|
+
)
|
775
|
+
const first = args.at(-1)
|
776
|
+
if (first[TYPE] === WORD && first[VALUE] in keywords)
|
777
|
+
throw new TypeError(
|
778
|
+
`Preceeding arguments of (${
|
779
|
+
KEYWORDS.CALL_FUNCTION
|
780
|
+
}) must not be an reserved word\n\n(${
|
781
|
+
KEYWORDS.CALL_FUNCTION
|
782
|
+
} ${stringifyArgs(args)})`
|
783
|
+
)
|
784
|
+
const apply = evaluate(first, env)
|
785
|
+
if (typeof apply !== 'function')
|
786
|
+
throw new TypeError(
|
787
|
+
`Last argument of (${KEYWORDS.CALL_FUNCTION}) must be a (${
|
788
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
789
|
+
}) but got ${LISP.stringify(apply)}\n\n(${
|
790
|
+
KEYWORDS.CALL_FUNCTION
|
791
|
+
} ${stringifyArgs(args)})`
|
792
|
+
)
|
793
|
+
|
794
|
+
return apply(args.slice(0, -1), env)
|
795
|
+
},
|
796
|
+
[KEYWORDS.BLOCK]: (args, env) => {
|
797
|
+
if (!args.length)
|
798
|
+
throw new RangeError(
|
799
|
+
`Invalid number of arguments to (${
|
800
|
+
KEYWORDS.BLOCK
|
801
|
+
}) (>= 1 required)\n\n(${KEYWORDS.BLOCK} ${stringifyArgs(args)})`
|
802
|
+
)
|
803
|
+
let out = FALSE
|
804
|
+
for (const exp of args) out = evaluate(exp, env)
|
805
|
+
return out
|
806
|
+
},
|
807
|
+
[KEYWORDS.IS_ATOM]: (args, env) => {
|
808
|
+
if (args.length !== 1)
|
809
|
+
throw new RangeError(
|
810
|
+
`Invalid number of arguments for (${
|
811
|
+
KEYWORDS.IS_ATOM
|
812
|
+
}) (= 1 required)\n\n(${KEYWORDS.IS_ATOM} ${stringifyArgs(args)})`
|
813
|
+
)
|
814
|
+
return +(typeof evaluate(args[0], env) === 'number')
|
815
|
+
},
|
816
|
+
[KEYWORDS.IS_LAMBDA]: (args, env) => {
|
817
|
+
if (args.length !== 1)
|
818
|
+
throw new RangeError(
|
819
|
+
`Invalid number of arguments for (${
|
820
|
+
KEYWORDS.IS_LAMBDA
|
821
|
+
}) (= 1 required)\n\n(${KEYWORDS.IS_LAMBDA} ${stringifyArgs(args)})`
|
822
|
+
)
|
823
|
+
return +(typeof evaluate(args[0], env) === 'function')
|
824
|
+
},
|
825
|
+
// Only for type checking
|
826
|
+
[STATIC_TYPES.UNKNOWN]: (args, env) => evaluate(args[0], env),
|
827
|
+
[STATIC_TYPES.ANY]: (args, env) => evaluate(args[0], env),
|
828
|
+
|
829
|
+
[STATIC_TYPES.ATOM]: (args, env) => evaluate(args[0], env),
|
830
|
+
[STATIC_TYPES.ABSTRACTION]: (args, env) => evaluate(args[0], env),
|
831
|
+
[STATIC_TYPES.COLLECTION]: (args, env) => evaluate(args[0], env),
|
832
|
+
|
833
|
+
[STATIC_TYPES.BOOLEAN]: (args, env) => evaluate(args[0], env),
|
834
|
+
[STATIC_TYPES.NUMBER]: (args, env) => evaluate(args[0], env),
|
835
|
+
[STATIC_TYPES.NUMBERS]: (args, env) => evaluate(args[0], env),
|
836
|
+
[STATIC_TYPES.COLLECTIONS]: (args, env) => evaluate(args[0], env),
|
837
|
+
[STATIC_TYPES.ABSTRACTIONS]: (args, env) => evaluate(args[0], env),
|
838
|
+
[STATIC_TYPES.BOOLEANS]: (args, env) => evaluate(args[0], env)
|
839
|
+
}
|
840
|
+
const debugStack = []
|
841
|
+
const evaluate = (exp, env = keywords) => {
|
842
|
+
const [head, ...tail] = isLeaf(exp) ? [exp] : exp
|
843
|
+
if (head == undefined) return []
|
844
|
+
const type = head[TYPE]
|
845
|
+
const value = head[VALUE]
|
846
|
+
let res
|
847
|
+
switch (type) {
|
848
|
+
case WORD:
|
849
|
+
res = env[value]
|
850
|
+
return res
|
851
|
+
case APPLY:
|
852
|
+
res = env[value](tail, env)
|
853
|
+
if (
|
854
|
+
value !== KEYWORDS.BLOCK &&
|
855
|
+
value !== KEYWORDS.CALL_FUNCTION &&
|
856
|
+
value !== KEYWORDS.ANONYMOUS_FUNCTION &&
|
857
|
+
value !== KEYWORDS.DEFINE_VARIABLE
|
858
|
+
) {
|
859
|
+
// debugStack.push(
|
860
|
+
// `\x1b[31m${LISP.source(exp)}\x1b[32m\n${
|
861
|
+
// typeof res === 'function' ? '(lambda)' : serialise(res)
|
862
|
+
// }\x1b[0m`
|
863
|
+
// )
|
864
|
+
const out = typeof res === 'function' ? '(lambda)' : LISP.serialise(res)
|
865
|
+
if (debugStack.at(-1)?.result !== out)
|
866
|
+
debugStack.push({
|
867
|
+
function: value,
|
868
|
+
source: LISP.source(exp),
|
869
|
+
result: out
|
870
|
+
})
|
871
|
+
// else debugStack[debugStack.length - 1].src += LISP.source(exp)
|
872
|
+
}
|
873
|
+
return res
|
874
|
+
case ATOM:
|
875
|
+
res = value
|
876
|
+
return res
|
877
|
+
}
|
878
|
+
}
|
879
|
+
export const debugStackToString = (stack) =>
|
880
|
+
stack.map(({ source, result }) => `${source}\n${result}`).join('\n')
|
881
|
+
export const startDebug = (ast, speed = 250, start, end) => {
|
882
|
+
debugStack.length = 0
|
883
|
+
evaluate(ast)
|
884
|
+
start = start ? debugStack.findIndex(start) : 0
|
885
|
+
end = end ? debugStack.findIndex(end) : debugStack.length
|
886
|
+
const stack = debugStack.slice(start, end).reverse()
|
887
|
+
if (speed !== 0) {
|
888
|
+
const rec = () => {
|
889
|
+
setTimeout(() => {
|
890
|
+
if (stack.length) {
|
891
|
+
const { source, result } = stack.pop()
|
892
|
+
console.log(`\x1b[31m${source}\x1b[32m\n${result}\x1b[0m`)
|
893
|
+
rec()
|
894
|
+
}
|
895
|
+
}, speed)
|
896
|
+
}
|
897
|
+
rec()
|
898
|
+
}
|
899
|
+
return stack
|
900
|
+
}
|
901
|
+
|
902
|
+
// const types = typeCheck(std[0], withCtxTypes(definedTypes(stdT)))[1]
|
903
|
+
const __debugStack__ = []
|
904
|
+
export const debug = (ast, checkTypes = true, userDefinedTypes) => {
|
905
|
+
let types = new Map()
|
906
|
+
const debugEnv = {
|
907
|
+
...keywords,
|
908
|
+
[STATIC_TYPES.ABSTRACTION]: (args, env) => {
|
909
|
+
const T = evaluate(args[0], env)
|
910
|
+
const t = typeof T
|
911
|
+
if (t !== 'function')
|
912
|
+
throw new TypeError(
|
913
|
+
`Argument of (${STATIC_TYPES.ABSTRACTION}) must be an (${
|
914
|
+
RUNTIME_TYPES.LAMBDA
|
915
|
+
}) but got something else (${
|
916
|
+
STATIC_TYPES.ABSTRACTION
|
917
|
+
} ${stringifyArgs(args)})`
|
918
|
+
)
|
919
|
+
return T
|
920
|
+
},
|
921
|
+
[STATIC_TYPES.ATOM]: (args, env) => {
|
922
|
+
const T = evaluate(args[0], env)
|
923
|
+
const t = typeof T
|
924
|
+
if (t !== 'number')
|
925
|
+
throw new TypeError(
|
926
|
+
`Argument of (${STATIC_TYPES.ATOM}) must be an (${
|
927
|
+
RUNTIME_TYPES.NUMBER
|
928
|
+
}) but got something else (${STATIC_TYPES.ATOM} ${stringifyArgs(
|
929
|
+
args
|
930
|
+
)})`
|
931
|
+
)
|
932
|
+
return T
|
933
|
+
},
|
934
|
+
[STATIC_TYPES.COLLECTION]: (args, env) => {
|
935
|
+
const T = evaluate(args[0], env)
|
936
|
+
if (!Array.isArray(T))
|
937
|
+
throw new TypeError(
|
938
|
+
`Argument of (${STATIC_TYPES.COLLECTION}) must be an (${
|
939
|
+
RUNTIME_TYPES.ARRAY
|
940
|
+
}) but got something else (${STATIC_TYPES.COLLECTION} ${stringifyArgs(
|
941
|
+
args
|
942
|
+
)})`
|
943
|
+
)
|
944
|
+
return T
|
945
|
+
},
|
946
|
+
[DEBUG.TYPE_SIGNATURE]: (args, env) => {
|
947
|
+
if (args.length !== 2)
|
948
|
+
throw new RangeError(
|
949
|
+
`Invalid number of arguments to (${DEBUG.TYPE_SIGNATURE}) (= 1) (${
|
950
|
+
DEBUG.TYPE_SIGNATURE
|
951
|
+
} ${stringifyArgs(args)})`
|
952
|
+
)
|
953
|
+
const name =
|
954
|
+
Array.isArray(args[0]) && args[0][0][VALUE] === DEBUG.STRING
|
955
|
+
? args[0][1]
|
956
|
+
.slice(1)
|
957
|
+
.map((x) => String.fromCharCode(x[VALUE]))
|
958
|
+
.join('')
|
959
|
+
: args[0][VALUE]
|
960
|
+
const option = args[1][VALUE]
|
961
|
+
const wildcard = name === '*'
|
962
|
+
if (option === 'Scope') {
|
963
|
+
if (wildcard) {
|
964
|
+
return [...types.entries()]
|
965
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
966
|
+
.map(([k, v]) => `${k}\n${v()}`)
|
967
|
+
.join('\n\n')
|
968
|
+
}
|
969
|
+
const t = types.get(`; 1 ${name}`)
|
970
|
+
return t ? t() : ''
|
971
|
+
} else if (option === 'Search') {
|
972
|
+
return [...types.entries()]
|
973
|
+
.filter((x) => x[0].includes(name))
|
974
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
975
|
+
.map(([k, v]) => `${k}\n${v()}`)
|
976
|
+
.join('\n\n')
|
977
|
+
} else if (option === 'Special') {
|
978
|
+
return formatType(name, SPECIAL_FORM_TYPES)
|
979
|
+
} else if (option === 'Type') {
|
980
|
+
const [from, to] = name.split(KEYWORDS.BLOCK).map((x) => x.trim())
|
981
|
+
return [...types.entries()]
|
982
|
+
.filter(([k, v]) => {
|
983
|
+
const T = v()
|
984
|
+
if (T && T.includes(KEYWORDS.BLOCK)) {
|
985
|
+
const [left, right] = T.split(KEYWORDS.BLOCK).map((x) => x.trim())
|
986
|
+
return left.includes(from) && right.includes(to)
|
987
|
+
}
|
988
|
+
})
|
989
|
+
.sort((a, b) => a[0].length - b[0].length)
|
990
|
+
.map(([k, v]) => `${k}\n${v()}`)
|
991
|
+
.join('\n\n')
|
992
|
+
} else if (option === 'Library') {
|
993
|
+
types = typeCheck(std[0], withCtxTypes(definedTypes(stdT)))[1]
|
994
|
+
const matches = wildcard
|
995
|
+
? [...types.entries()]
|
996
|
+
: [...types.entries()].filter(([k, v]) => v().includes(name))
|
997
|
+
return matches
|
998
|
+
.sort((a, b) => a[0].length - b[0].length)
|
999
|
+
.map(([k, v]) => `${k}\n${v()}`)
|
1000
|
+
.join('\n\n')
|
1001
|
+
} else {
|
1002
|
+
return ''
|
1003
|
+
}
|
1004
|
+
},
|
1005
|
+
[DEBUG.SIGNATURE]: (args, env) => {
|
1006
|
+
const name =
|
1007
|
+
Array.isArray(args[0]) && args[0][0][VALUE] === DEBUG.STRING
|
1008
|
+
? args[0][1]
|
1009
|
+
.slice(1)
|
1010
|
+
.map((x) => String.fromCharCode(x[VALUE]))
|
1011
|
+
.join('')
|
1012
|
+
: args[0][VALUE]
|
1013
|
+
const signatures =
|
1014
|
+
args.length === 0
|
1015
|
+
? std[0][1][1].slice(1)
|
1016
|
+
: std[0][1][1].filter(
|
1017
|
+
(x) =>
|
1018
|
+
x[0][TYPE] === APPLY &&
|
1019
|
+
x[0][VALUE] === KEYWORDS.DEFINE_VARIABLE &&
|
1020
|
+
x[1][TYPE] === WORD &&
|
1021
|
+
x[1][VALUE].toString().includes(name)
|
1022
|
+
)
|
1023
|
+
return signatures.length === 0
|
1024
|
+
? 'Not defined in library'
|
1025
|
+
: signatures.map(LISP.source).join('\n\n')
|
1026
|
+
},
|
1027
|
+
[DEBUG.STRING]: (args, env) => {
|
1028
|
+
if (args.length !== 1)
|
1029
|
+
throw new RangeError(
|
1030
|
+
`Invalid number of arguments to (${DEBUG.STRING}) (= 1) (${
|
1031
|
+
DEBUG.STRING
|
1032
|
+
} but got (${args.length}) ${stringifyArgs(args)})`
|
1033
|
+
)
|
1034
|
+
const expression = evaluate(args[0], env)
|
1035
|
+
if (!Array.isArray(expression))
|
1036
|
+
throw new TypeError(
|
1037
|
+
`Argument of (${DEBUG.STRING}) must be an (${
|
1038
|
+
RUNTIME_TYPES.ARRAY
|
1039
|
+
}) but got (${expression}) (${DEBUG.STRING} ${stringifyArgs(args)})`
|
1040
|
+
)
|
1041
|
+
return `"${expression.map((x) => String.fromCharCode(x)).join('')}"`
|
1042
|
+
},
|
1043
|
+
[DEBUG.UNQUOTED_STRING]: (args, env) => {
|
1044
|
+
if (args.length !== 1)
|
1045
|
+
throw new RangeError(
|
1046
|
+
`Invalid number of arguments to (${DEBUG.UNQUOTED_STRING}) (= 1) (${
|
1047
|
+
DEBUG.UNQUOTED_STRING
|
1048
|
+
} but got (${args.length}) ${stringifyArgs(args)})`
|
1049
|
+
)
|
1050
|
+
const expression = evaluate(args[0], env)
|
1051
|
+
if (!Array.isArray(expression))
|
1052
|
+
throw new TypeError(
|
1053
|
+
`Argument of (${DEBUG.UNQUOTED_STRING}) must be an (${
|
1054
|
+
RUNTIME_TYPES.ARRAY
|
1055
|
+
}) but got (${expression}) (${DEBUG.UNQUOTED_STRING} ${stringifyArgs(
|
1056
|
+
args
|
1057
|
+
)})`
|
1058
|
+
)
|
1059
|
+
return `${expression.map((x) => String.fromCharCode(x)).join('')}`
|
1060
|
+
},
|
1061
|
+
[DEBUG.LOG]: (args, env) => {
|
1062
|
+
if (args.length !== 1 && args.length !== 2)
|
1063
|
+
throw new RangeError(
|
1064
|
+
`Invalid number of arguments to (${DEBUG.LOG}) (or (= 1) (= 2)) (${
|
1065
|
+
DEBUG.LOG
|
1066
|
+
} ${stringifyArgs(args)})`
|
1067
|
+
)
|
1068
|
+
const expression = evaluate(args[0], env)
|
1069
|
+
if (args.length === 2) {
|
1070
|
+
const option = evaluate(args[1], env)
|
1071
|
+
if (!Array.isArray(option)) {
|
1072
|
+
throw new TypeError(
|
1073
|
+
`Second argument of (${DEBUG.LOG}) must be an (${
|
1074
|
+
RUNTIME_TYPES.ARRAY
|
1075
|
+
}) but got (${expression}) (${DEBUG.LOG} ${stringifyArgs(args)})`
|
1076
|
+
)
|
1077
|
+
}
|
1078
|
+
const type = option.map((x) => String.fromCharCode(x)).join('')
|
1079
|
+
switch (type) {
|
1080
|
+
case 'string':
|
1081
|
+
case 'str':
|
1082
|
+
{
|
1083
|
+
if (!Array.isArray(expression))
|
1084
|
+
throw new TypeError(
|
1085
|
+
`Argument of (${DEBUG.LOG}) must be an (${
|
1086
|
+
RUNTIME_TYPES.ARRAY
|
1087
|
+
}) in the case ${type} but got (${expression}) (${
|
1088
|
+
DEBUG.LOG
|
1089
|
+
} ${stringifyArgs(args)})`
|
1090
|
+
)
|
1091
|
+
console.log(
|
1092
|
+
expression.map((x) => String.fromCharCode(x)).join('')
|
1093
|
+
)
|
1094
|
+
}
|
1095
|
+
break
|
1096
|
+
case 'char':
|
1097
|
+
case 'ch':
|
1098
|
+
{
|
1099
|
+
if (typeof expression !== 'number')
|
1100
|
+
throw new TypeError(
|
1101
|
+
`Argument argument of (${DEBUG.LOG}) must be a (${
|
1102
|
+
RUNTIME_TYPES.NUMBER
|
1103
|
+
}) in the case ${type} but got (${expression}) (${
|
1104
|
+
DEBUG.LOG
|
1105
|
+
} ${stringifyArgs(args)})`
|
1106
|
+
)
|
1107
|
+
console.log(String.fromCharCode(expression))
|
1108
|
+
}
|
1109
|
+
|
1110
|
+
break
|
1111
|
+
case '*':
|
1112
|
+
console.log(expression)
|
1113
|
+
break
|
1114
|
+
case '!':
|
1115
|
+
__debugStack__.push(structuredClone(expression))
|
1116
|
+
break
|
1117
|
+
default:
|
1118
|
+
throw new TypeError(
|
1119
|
+
`Invalid option to (${DEBUG.LOG}) got ${option} ${stringifyArgs(
|
1120
|
+
args
|
1121
|
+
)})`
|
1122
|
+
)
|
1123
|
+
}
|
1124
|
+
} else console.log(expression)
|
1125
|
+
return expression
|
1126
|
+
}
|
1127
|
+
}
|
1128
|
+
try {
|
1129
|
+
types = checkTypes
|
1130
|
+
? typeCheck(
|
1131
|
+
ast,
|
1132
|
+
withCtxTypes(
|
1133
|
+
userDefinedTypes
|
1134
|
+
? {
|
1135
|
+
...definedTypes(filteredDefinedTypes(ast, std, stdT)),
|
1136
|
+
...definedTypes(LISP.parse(removeNoCode(userDefinedTypes)))
|
1137
|
+
}
|
1138
|
+
: definedTypes(filteredDefinedTypes(ast, std, stdT))
|
1139
|
+
)
|
1140
|
+
)[1]
|
1141
|
+
: new Map()
|
1142
|
+
const evaluated = evaluate(enhance(ast), debugEnv)
|
1143
|
+
const exp = ast.at(-1).at(-1).at(-1)
|
1144
|
+
const [head, ...rest] = isLeaf(exp) ? [exp] : exp
|
1145
|
+
let type = ''
|
1146
|
+
switch (head[TYPE]) {
|
1147
|
+
case ATOM:
|
1148
|
+
type = STATIC_TYPES.ATOM
|
1149
|
+
break
|
1150
|
+
case WORD:
|
1151
|
+
case APPLY:
|
1152
|
+
switch (head[VALUE]) {
|
1153
|
+
case DEBUG.TYPE_SIGNATURE:
|
1154
|
+
break
|
1155
|
+
case KEYWORDS.DEFINE_VARIABLE:
|
1156
|
+
type = debugEnv[DEBUG.TYPE_SIGNATURE]([rest[0], [WORD, 'Scope']])
|
1157
|
+
break
|
1158
|
+
default:
|
1159
|
+
if (SPECIAL_FORMS_SET.has(head[VALUE])) {
|
1160
|
+
// type = debugEnv[DEBUG.TYPE_SIGNATURE]([head, [WORD, 'Special']])
|
1161
|
+
type = `${debugEnv[DEBUG.TYPE_SIGNATURE]([
|
1162
|
+
head,
|
1163
|
+
[WORD, 'Special']
|
1164
|
+
])}`
|
1165
|
+
} else
|
1166
|
+
type = `${debugEnv[DEBUG.TYPE_SIGNATURE]([
|
1167
|
+
head,
|
1168
|
+
[WORD, 'Scope']
|
1169
|
+
])}`
|
1170
|
+
break
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
break
|
1174
|
+
}
|
1175
|
+
type = type.split('\n').pop()
|
1176
|
+
return {
|
1177
|
+
type,
|
1178
|
+
evaluated,
|
1179
|
+
error: null
|
1180
|
+
}
|
1181
|
+
} catch (error) {
|
1182
|
+
// console.log(error)
|
1183
|
+
return {
|
1184
|
+
type: null,
|
1185
|
+
evaluated: null,
|
1186
|
+
error: {
|
1187
|
+
message: error.message
|
1188
|
+
}
|
1189
|
+
}
|
1190
|
+
}
|
1191
|
+
}
|