fez-lisp 1.0.50 → 1.0.52

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