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