fez-lisp 1.0.51 → 1.0.52
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/index.js +3 -3
- package/package.json +1 -1
- package/src/compiler.js +1 -1
- package/src/evaluator.js +43 -0
- package/src/interpreter.js +1129 -34
- package/src/parser.js +1 -1
- package/src/utils.js +2 -2
- package/src/tokeniser.js +0 -1145
- /package/src/{enums.js → keywords.js} +0 -0
package/src/tokeniser.js
DELETED
@@ -1,1145 +0,0 @@
|
|
1
|
-
import std from '../lib/baked/std.js'
|
2
|
-
import { TYPE, VALUE, WORD, KEYWORDS, APPLY } from './enums.js'
|
3
|
-
import { evaluate, isAtom } from './interpreter.js'
|
4
|
-
import { LISP } from './parser.js'
|
5
|
-
import {
|
6
|
-
isEqual,
|
7
|
-
isEqualTypes,
|
8
|
-
isForbiddenVariableName,
|
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
|
-
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))
|
926
|
-
throw new ReferenceError(
|
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
|
-
try {
|
984
|
-
if (
|
985
|
-
args.some(
|
986
|
-
([[type, car]]) => !(type === APPLY && car === KEYWORDS.TEST_CASE)
|
987
|
-
)
|
988
|
-
)
|
989
|
-
throw new TypeError(
|
990
|
-
`Arguments of (${KEYWORDS.TEST_BED}) must be (${
|
991
|
-
KEYWORDS.TEST_CASE
|
992
|
-
}) (${KEYWORDS.TEST_BED} ${stringifyArgs(args)})`
|
993
|
-
)
|
994
|
-
tests = args.map((x) => evaluate(x, env))
|
995
|
-
tests.forEach(([state, describe, ...rest]) =>
|
996
|
-
!state
|
997
|
-
? console.log(
|
998
|
-
'\x1b[31m',
|
999
|
-
`${describe} Failed:\n`,
|
1000
|
-
`${rest[0]} => ${LISP.stringify(rest[1])} != ${LISP.stringify(
|
1001
|
-
rest[2]
|
1002
|
-
)}`,
|
1003
|
-
'\n',
|
1004
|
-
'\x1b[0m'
|
1005
|
-
)
|
1006
|
-
: console.log(
|
1007
|
-
'\x1b[32m',
|
1008
|
-
`${describe} Passed:\n`,
|
1009
|
-
`${rest[0]} => ${LISP.stringify(rest[1])}`,
|
1010
|
-
'\n',
|
1011
|
-
'\x1b[0m'
|
1012
|
-
)
|
1013
|
-
)
|
1014
|
-
} catch (err) {
|
1015
|
-
console.log('\x1b[31m', 'Tests failed: \n', err.toString())
|
1016
|
-
}
|
1017
|
-
const result = !tests.length || tests.some(([t]) => !t)
|
1018
|
-
result
|
1019
|
-
? console.log('\x1b[31m', 'Some tests failed!', '\n', '\x1b[0m')
|
1020
|
-
: console.log('\x1b[32m', 'All tests passed!', '\n', '\x1b[0m')
|
1021
|
-
return +!result
|
1022
|
-
},
|
1023
|
-
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
1024
|
-
if (args.length !== 2 && args.length !== 3)
|
1025
|
-
throw new RangeError(
|
1026
|
-
`Invalid number of arguments for (${
|
1027
|
-
KEYWORDS.SET_ARRAY
|
1028
|
-
}) (or 2 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
1029
|
-
)
|
1030
|
-
const array = evaluate(args[0], env)
|
1031
|
-
if (!Array.isArray(array))
|
1032
|
-
throw new TypeError(
|
1033
|
-
`First argument of (${KEYWORDS.SET_ARRAY}) must be an (${
|
1034
|
-
KEYWORDS.ARRAY_TYPE
|
1035
|
-
}) but got (${array}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
1036
|
-
)
|
1037
|
-
const index = evaluate(args[1], env)
|
1038
|
-
if (!Number.isInteger(index))
|
1039
|
-
throw new TypeError(
|
1040
|
-
`Second argument of (${KEYWORDS.SET_ARRAY}) must be an (${
|
1041
|
-
KEYWORDS.NUMBER_TYPE
|
1042
|
-
} integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
1043
|
-
)
|
1044
|
-
if (index > array.length)
|
1045
|
-
throw new RangeError(
|
1046
|
-
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
1047
|
-
KEYWORDS.ARRAY_TYPE
|
1048
|
-
}) bounds (index ${index} bounds ${array.length}) (${
|
1049
|
-
KEYWORDS.SET_ARRAY
|
1050
|
-
} ${stringifyArgs(args)})`
|
1051
|
-
)
|
1052
|
-
if (index < 0) {
|
1053
|
-
if (args.length !== 2)
|
1054
|
-
throw new RangeError(
|
1055
|
-
`Invalid number of arguments for (${
|
1056
|
-
KEYWORDS.SET_ARRAY
|
1057
|
-
}) (if (< index 0) then 2 required) (${
|
1058
|
-
KEYWORDS.SET_ARRAY
|
1059
|
-
} ${stringifyArgs(args)})`
|
1060
|
-
)
|
1061
|
-
if (index * -1 > array.length)
|
1062
|
-
throw new RangeError(
|
1063
|
-
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
1064
|
-
KEYWORDS.ARRAY_TYPE
|
1065
|
-
}) bounds (index ${index} bounds ${array.length}) (${
|
1066
|
-
KEYWORDS.SET_ARRAY
|
1067
|
-
} ${stringifyArgs(args)})`
|
1068
|
-
)
|
1069
|
-
const target = array.length + index
|
1070
|
-
while (array.length !== target) array.pop()
|
1071
|
-
} else {
|
1072
|
-
if (args.length !== 3)
|
1073
|
-
throw new RangeError(
|
1074
|
-
`Invalid number of arguments for (${
|
1075
|
-
KEYWORDS.SET_ARRAY
|
1076
|
-
}) (if (>= index 0) then 3 required) (${
|
1077
|
-
KEYWORDS.SET_ARRAY
|
1078
|
-
} ${stringifyArgs(args)})`
|
1079
|
-
)
|
1080
|
-
const value = evaluate(args[2], env)
|
1081
|
-
if (value == undefined)
|
1082
|
-
throw new RangeError(
|
1083
|
-
`Trying to set a null value in (${KEYWORDS.ARRAY_TYPE}) at (${
|
1084
|
-
KEYWORDS.SET_ARRAY
|
1085
|
-
}). (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
1086
|
-
)
|
1087
|
-
array[index] = value
|
1088
|
-
}
|
1089
|
-
return array
|
1090
|
-
},
|
1091
|
-
[KEYWORDS.LOG]: (args, env) => {
|
1092
|
-
if (!args.length)
|
1093
|
-
throw new RangeError(
|
1094
|
-
`Invalid number of arguments to (${KEYWORDS.LOG}) (>= 1 required) (${
|
1095
|
-
KEYWORDS.LOG
|
1096
|
-
} ${stringifyArgs(args)})`
|
1097
|
-
)
|
1098
|
-
const expressions = args.map((x) => evaluate(x, env))
|
1099
|
-
console.log(...expressions)
|
1100
|
-
return expressions.at(-1)
|
1101
|
-
},
|
1102
|
-
[KEYWORDS.CLEAR_CONSOLE]: (args) => {
|
1103
|
-
if (args.length)
|
1104
|
-
throw new RangeError(
|
1105
|
-
`Invalid number of arguments to (${
|
1106
|
-
KEYWORDS.CLEAR_CONSOLE
|
1107
|
-
}) (= 0 required) (${KEYWORDS.CLEAR_CONSOLE} ${stringifyArgs(args)})`
|
1108
|
-
)
|
1109
|
-
console.clear()
|
1110
|
-
return 0
|
1111
|
-
}
|
1112
|
-
}
|
1113
|
-
keywords[KEYWORDS.NOT_COMPILED_BLOCK] = keywords[KEYWORDS.BLOCK]
|
1114
|
-
keywords[KEYWORDS.DOC] = (args, env) => {
|
1115
|
-
if (args.length !== 1)
|
1116
|
-
throw new RangeError(
|
1117
|
-
`Invalid number of arguments to (${KEYWORDS.DOC}) (= 1 required) (${
|
1118
|
-
KEYWORDS.DOC
|
1119
|
-
} ${stringifyArgs(args)})`
|
1120
|
-
)
|
1121
|
-
const lib = evaluate(args[0], env)
|
1122
|
-
const kw = Object.keys(env).map((x) => [x])
|
1123
|
-
const standard = std.map(([_, [_0, name], [_1, ...arg]]) => {
|
1124
|
-
const args = arg
|
1125
|
-
.slice(0, -1)
|
1126
|
-
.map((x) => x[VALUE])
|
1127
|
-
.filter((x) => x !== 'lambda')
|
1128
|
-
|
1129
|
-
return [name, ...args]
|
1130
|
-
})
|
1131
|
-
const all = [...kw, ...standard]
|
1132
|
-
switch (lib) {
|
1133
|
-
case '*':
|
1134
|
-
return all.map((x) => `(${x.join(' ')})`)
|
1135
|
-
case 'keywords':
|
1136
|
-
return kw.map((x) => `(${x.join(' ')})`)
|
1137
|
-
case 'std':
|
1138
|
-
return standard.map((x) => `(${x.join(' ')})`)
|
1139
|
-
default:
|
1140
|
-
return all
|
1141
|
-
.filter((name) => name[0].includes(lib))
|
1142
|
-
.map((x) => `(${x.join(' ')})`)
|
1143
|
-
}
|
1144
|
-
}
|
1145
|
-
export { keywords }
|