fez-lisp 1.3.2 → 1.3.3
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/package.json +1 -1
- package/src/compiler.js +8 -5
- package/src/interpreter.js +309 -310
- package/src/macros.js +1 -3
- package/src/utils.js +4 -5
package/package.json
CHANGED
package/src/compiler.js
CHANGED
@@ -7,8 +7,11 @@ import {
|
|
7
7
|
VALUE,
|
8
8
|
WORD
|
9
9
|
} from './keywords.js'
|
10
|
-
import { SUGGAR } from './macros.js'
|
11
10
|
import { leaf, isLeaf, AST } from './parser.js'
|
11
|
+
export const OPTIMIZATIONS = {
|
12
|
+
RECURSION: 'recursive',
|
13
|
+
CACHE: 'memoized'
|
14
|
+
}
|
12
15
|
const deepRename = (name, newName, tree) => {
|
13
16
|
if (!isLeaf(tree))
|
14
17
|
for (const leaf of tree) {
|
@@ -176,9 +179,9 @@ const compile = (tree, Drill) => {
|
|
176
179
|
case KEYWORDS.DEFINE_VARIABLE: {
|
177
180
|
const n = Arguments[0][VALUE]
|
178
181
|
const prefix = n.split(':')[0]
|
179
|
-
if (prefix ===
|
182
|
+
if (prefix === OPTIMIZATIONS.RECURSION) {
|
180
183
|
const name = lispToJavaScriptVariableName(n)
|
181
|
-
const newName = `${
|
184
|
+
const newName = `${OPTIMIZATIONS.RECURSION}_${performance
|
182
185
|
.now()
|
183
186
|
.toString()
|
184
187
|
.replace('.', 7)}`
|
@@ -199,10 +202,10 @@ const compile = (tree, Drill) => {
|
|
199
202
|
)})=>{${vars}return ${evaluatedBody
|
200
203
|
.toString()
|
201
204
|
.trim()}}, ${newName})));`
|
202
|
-
} else if (prefix ===
|
205
|
+
} else if (prefix === OPTIMIZATIONS.CACHE) {
|
203
206
|
// memoization here
|
204
207
|
const name = lispToJavaScriptVariableName(n)
|
205
|
-
const newName = name.substring(
|
208
|
+
const newName = name.substring(OPTIMIZATIONS.CACHE.length + 1)
|
206
209
|
Drill.Variables.add(name)
|
207
210
|
const functionArgs = Arguments.at(-1).slice(1)
|
208
211
|
const body = functionArgs.pop()
|
package/src/interpreter.js
CHANGED
@@ -2,39 +2,77 @@ import { TYPE, VALUE, WORD, KEYWORDS, FALSE, TRUE, TYPES } from './keywords.js'
|
|
2
2
|
import { evaluate } from './evaluator.js'
|
3
3
|
import { isForbiddenVariableName, stringifyArgs } from './utils.js'
|
4
4
|
export const keywords = {
|
5
|
-
[KEYWORDS.
|
6
|
-
if (args.length !== 2)
|
5
|
+
[KEYWORDS.ADDITION]: (args, env) => {
|
6
|
+
if (args.length !== 0 && args.length !== 2)
|
7
7
|
throw new RangeError(
|
8
8
|
`Invalid number of arguments for (${
|
9
|
-
KEYWORDS.
|
10
|
-
}), expected (= 2) but got ${args.length}. (${
|
11
|
-
KEYWORDS.
|
9
|
+
KEYWORDS.ADDITION
|
10
|
+
}), expected (or (= 2) (= 0)) but got ${args.length}. (${
|
11
|
+
KEYWORDS.ADDITION
|
12
12
|
} ${stringifyArgs(args)})`
|
13
13
|
)
|
14
14
|
const a = evaluate(args[0], env)
|
15
15
|
if (typeof a !== 'number')
|
16
16
|
throw new TypeError(
|
17
|
-
`First
|
17
|
+
`First arguments of (${KEYWORDS.ADDITION}) is not a (${
|
18
18
|
KEYWORDS.NUMBER_TYPE
|
19
|
-
}) (${KEYWORDS.
|
19
|
+
}) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
|
20
20
|
)
|
21
21
|
const b = evaluate(args[1], env)
|
22
22
|
if (typeof b !== 'number')
|
23
23
|
throw new TypeError(
|
24
|
-
`Second
|
24
|
+
`Second arguments of (${KEYWORDS.ADDITION}) is not a (${
|
25
25
|
KEYWORDS.NUMBER_TYPE
|
26
|
-
}) (${KEYWORDS.
|
26
|
+
}) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
|
27
27
|
)
|
28
|
-
|
28
|
+
return a + b
|
29
|
+
},
|
30
|
+
[KEYWORDS.MULTIPLICATION]: (args, env) => {
|
31
|
+
if (args.length !== 2)
|
32
|
+
throw new RangeError(
|
33
|
+
`Invalid number of arguments for (${KEYWORDS.MULTIPLICATION}), expected (= 2) but got ${args.length}.`
|
34
|
+
)
|
35
|
+
const a = evaluate(args[0], env)
|
36
|
+
if (typeof a !== 'number')
|
29
37
|
throw new TypeError(
|
30
|
-
`
|
31
|
-
KEYWORDS.
|
32
|
-
})
|
33
|
-
|
38
|
+
`First arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
|
39
|
+
KEYWORDS.NUMBER_TYPE
|
40
|
+
}) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
|
41
|
+
)
|
42
|
+
const b = evaluate(args[1], env)
|
43
|
+
if (typeof b !== 'number')
|
44
|
+
throw new TypeError(
|
45
|
+
`Second arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
|
46
|
+
KEYWORDS.NUMBER_TYPE
|
47
|
+
}) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
|
48
|
+
)
|
49
|
+
return a * b
|
50
|
+
},
|
51
|
+
[KEYWORDS.SUBTRACTION]: (args, env) => {
|
52
|
+
if (args.length !== 1 && args.length !== 2)
|
53
|
+
throw new RangeError(
|
54
|
+
`Invalid number of arguments for (${
|
55
|
+
KEYWORDS.SUBTRACTION
|
56
|
+
}), expected (or (= 1)) (= 2) but got ${args.length}. (${
|
57
|
+
KEYWORDS.SUBTRACTION
|
34
58
|
} ${stringifyArgs(args)})`
|
35
59
|
)
|
36
|
-
|
37
|
-
|
60
|
+
const a = evaluate(args[0], env)
|
61
|
+
if (typeof a !== 'number')
|
62
|
+
throw new TypeError(
|
63
|
+
`First argument of (${KEYWORDS.SUBTRACTION}) is not a (${
|
64
|
+
KEYWORDS.NUMBER_TYPE
|
65
|
+
}) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
|
66
|
+
)
|
67
|
+
if (args.length === 1) return -a
|
68
|
+
const b = evaluate(args[1], env)
|
69
|
+
if (typeof b !== 'number')
|
70
|
+
throw new TypeError(
|
71
|
+
`Second argument of (${KEYWORDS.SUBTRACTION}) is not a (${
|
72
|
+
KEYWORDS.NUMBER_TYPE
|
73
|
+
}) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
|
74
|
+
)
|
75
|
+
return a - b
|
38
76
|
},
|
39
77
|
[KEYWORDS.DIVISION]: (args, env) => {
|
40
78
|
if (args.length !== 2)
|
@@ -65,133 +103,162 @@ export const keywords = {
|
|
65
103
|
)
|
66
104
|
return a / b
|
67
105
|
},
|
68
|
-
[KEYWORDS.
|
69
|
-
if (args.length !==
|
106
|
+
[KEYWORDS.REMAINDER_OF_DIVISION]: (args, env) => {
|
107
|
+
if (args.length !== 2)
|
70
108
|
throw new RangeError(
|
71
109
|
`Invalid number of arguments for (${
|
72
|
-
KEYWORDS.
|
73
|
-
}) (=
|
110
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
111
|
+
}), expected (= 2) but got ${args.length}. (${
|
112
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
113
|
+
} ${stringifyArgs(args)})`
|
74
114
|
)
|
75
|
-
const
|
76
|
-
if (
|
115
|
+
const a = evaluate(args[0], env)
|
116
|
+
if (typeof a !== 'number')
|
77
117
|
throw new TypeError(
|
78
|
-
`First argument of (${KEYWORDS.
|
79
|
-
KEYWORDS.
|
80
|
-
} (${KEYWORDS.
|
118
|
+
`First argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
119
|
+
KEYWORDS.NUMBER_TYPE
|
120
|
+
}) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
|
81
121
|
)
|
82
|
-
|
122
|
+
const b = evaluate(args[1], env)
|
123
|
+
if (typeof b !== 'number')
|
124
|
+
throw new TypeError(
|
125
|
+
`Second argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
126
|
+
KEYWORDS.NUMBER_TYPE
|
127
|
+
}) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
|
128
|
+
)
|
129
|
+
if (b === 0)
|
130
|
+
throw new TypeError(
|
131
|
+
`Second argument of (${
|
132
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
133
|
+
}) can't be a (0) (division by 0 is not allowed) (${
|
134
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
135
|
+
} ${stringifyArgs(args)})`
|
136
|
+
)
|
137
|
+
|
138
|
+
return a % b
|
83
139
|
},
|
84
|
-
[KEYWORDS.
|
85
|
-
if (args.length
|
140
|
+
[KEYWORDS.BITWISE_AND]: (args, env) => {
|
141
|
+
if (args.length < 2)
|
86
142
|
throw new RangeError(
|
87
|
-
`Invalid number of arguments
|
88
|
-
KEYWORDS.
|
89
|
-
}) (=
|
143
|
+
`Invalid number of arguments to (${
|
144
|
+
KEYWORDS.BITWISE_AND
|
145
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
|
90
146
|
)
|
91
|
-
|
147
|
+
const operands = args.map((a) => evaluate(a, env))
|
148
|
+
if (operands.some((x) => typeof x !== 'number'))
|
149
|
+
throw new TypeError(
|
150
|
+
`Not all arguments of (${KEYWORDS.BITWISE_AND}) are ${
|
151
|
+
KEYWORDS.NUMBER_TYPE
|
152
|
+
} (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
|
153
|
+
)
|
154
|
+
return operands.reduce((acc, x) => acc & x)
|
92
155
|
},
|
93
|
-
[KEYWORDS.
|
156
|
+
[KEYWORDS.BITWISE_NOT]: (args, env) => {
|
94
157
|
if (args.length !== 1)
|
95
158
|
throw new RangeError(
|
96
|
-
`Invalid number of arguments
|
97
|
-
KEYWORDS.
|
98
|
-
}) (= 1 required) (${KEYWORDS.
|
159
|
+
`Invalid number of arguments to (${
|
160
|
+
KEYWORDS.BITWISE_NOT
|
161
|
+
}) (= 1 required). (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
|
99
162
|
)
|
100
|
-
|
163
|
+
const operand = evaluate(args[0], env)
|
164
|
+
if (typeof operand !== 'number')
|
165
|
+
throw new TypeError(
|
166
|
+
`Argument of (${KEYWORDS.BITWISE_NOT}) is not a (${
|
167
|
+
KEYWORDS.NUMBER_TYPE
|
168
|
+
}) (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
|
169
|
+
)
|
170
|
+
return ~operand
|
101
171
|
},
|
102
|
-
[KEYWORDS.
|
103
|
-
if (args.length !==
|
172
|
+
[KEYWORDS.BITWISE_OR]: (args, env) => {
|
173
|
+
if (args.length !== 2)
|
104
174
|
throw new RangeError(
|
105
|
-
`Invalid number of arguments
|
106
|
-
KEYWORDS.
|
107
|
-
})
|
108
|
-
KEYWORDS.ADDITION
|
109
|
-
} ${stringifyArgs(args)})`
|
175
|
+
`Invalid number of arguments to (${
|
176
|
+
KEYWORDS.BITWISE_OR
|
177
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
110
178
|
)
|
111
179
|
const a = evaluate(args[0], env)
|
112
|
-
if (typeof a !== 'number')
|
113
|
-
throw new TypeError(
|
114
|
-
`First arguments of (${KEYWORDS.ADDITION}) is not a (${
|
115
|
-
KEYWORDS.NUMBER_TYPE
|
116
|
-
}) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
|
117
|
-
)
|
118
180
|
const b = evaluate(args[1], env)
|
119
|
-
if (typeof b !== 'number')
|
181
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
120
182
|
throw new TypeError(
|
121
|
-
`
|
183
|
+
`Not all arguments of (${KEYWORDS.BITWISE_OR}) are (${
|
122
184
|
KEYWORDS.NUMBER_TYPE
|
123
|
-
}) (${KEYWORDS.
|
185
|
+
}) (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
124
186
|
)
|
125
|
-
return a
|
187
|
+
return a | b
|
126
188
|
},
|
127
|
-
[KEYWORDS.
|
189
|
+
[KEYWORDS.BITWISE_XOR]: (args, env) => {
|
128
190
|
if (args.length !== 2)
|
129
191
|
throw new RangeError(
|
130
|
-
`Invalid number of arguments
|
192
|
+
`Invalid number of arguments to (${
|
193
|
+
KEYWORDS.BITWISE_XOR
|
194
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
131
195
|
)
|
132
196
|
const a = evaluate(args[0], env)
|
133
|
-
if (typeof a !== 'number')
|
134
|
-
throw new TypeError(
|
135
|
-
`First arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
|
136
|
-
KEYWORDS.NUMBER_TYPE
|
137
|
-
}) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
|
138
|
-
)
|
139
197
|
const b = evaluate(args[1], env)
|
140
|
-
if (typeof b !== 'number')
|
198
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
141
199
|
throw new TypeError(
|
142
|
-
`
|
200
|
+
`Not all arguments of (${KEYWORDS.BITWISE_XOR}) are (${
|
143
201
|
KEYWORDS.NUMBER_TYPE
|
144
|
-
}) (${KEYWORDS.
|
202
|
+
}) (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
145
203
|
)
|
146
|
-
return a
|
204
|
+
return a ^ b
|
147
205
|
},
|
148
|
-
[KEYWORDS.
|
149
|
-
if (args.length !==
|
206
|
+
[KEYWORDS.BITWISE_LEFT_SHIFT]: (args, env) => {
|
207
|
+
if (args.length !== 2)
|
150
208
|
throw new RangeError(
|
151
|
-
`Invalid number of arguments
|
152
|
-
KEYWORDS.
|
153
|
-
})
|
154
|
-
|
155
|
-
|
209
|
+
`Invalid number of arguments to (${
|
210
|
+
KEYWORDS.BITWISE_LEFT_SHIFT
|
211
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(
|
212
|
+
args
|
213
|
+
)})`
|
156
214
|
)
|
157
215
|
const a = evaluate(args[0], env)
|
158
|
-
|
216
|
+
const b = evaluate(args[1], env)
|
217
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
159
218
|
throw new TypeError(
|
160
|
-
`
|
219
|
+
`Not all arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) are (${
|
161
220
|
KEYWORDS.NUMBER_TYPE
|
162
|
-
}) (${KEYWORDS.
|
221
|
+
}) (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(args)})`
|
163
222
|
)
|
164
|
-
|
223
|
+
return a << b
|
224
|
+
},
|
225
|
+
[KEYWORDS.BITWISE_RIGHT_SHIFT]: (args, env) => {
|
226
|
+
if (args.length !== 2)
|
227
|
+
throw new RangeError(
|
228
|
+
`Invalid number of arguments to (${
|
229
|
+
KEYWORDS.BITWISE_RIGHT_SHIFT
|
230
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(
|
231
|
+
args
|
232
|
+
)})`
|
233
|
+
)
|
234
|
+
const a = evaluate(args[0], env)
|
165
235
|
const b = evaluate(args[1], env)
|
166
|
-
if (typeof b !== 'number')
|
236
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
167
237
|
throw new TypeError(
|
168
|
-
`
|
238
|
+
`Not all arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) are (${
|
169
239
|
KEYWORDS.NUMBER_TYPE
|
170
|
-
}) (${KEYWORDS.
|
240
|
+
}) (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
171
241
|
)
|
172
|
-
return a
|
242
|
+
return a >> b
|
173
243
|
},
|
174
|
-
[KEYWORDS.
|
175
|
-
if (args.length
|
244
|
+
[KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT]: (args, env) => {
|
245
|
+
if (args.length !== 2)
|
176
246
|
throw new RangeError(
|
177
|
-
`Invalid number of arguments
|
178
|
-
KEYWORDS.
|
179
|
-
})
|
180
|
-
KEYWORDS.
|
247
|
+
`Invalid number of arguments to (${
|
248
|
+
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
249
|
+
}) (= 2 required). (${
|
250
|
+
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
181
251
|
} ${stringifyArgs(args)})`
|
182
252
|
)
|
183
|
-
const
|
184
|
-
|
253
|
+
const a = evaluate(args[0], env)
|
254
|
+
const b = evaluate(args[1], env)
|
255
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
185
256
|
throw new TypeError(
|
186
|
-
`
|
187
|
-
KEYWORDS.
|
188
|
-
} ${stringifyArgs(args)})`
|
257
|
+
`Not all arguments of (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT}) are (${
|
258
|
+
KEYWORDS.NUMBER_TYPE
|
259
|
+
}) (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
189
260
|
)
|
190
|
-
return
|
191
|
-
? evaluate(args[1], env)
|
192
|
-
: args.length === 3
|
193
|
-
? evaluate(args[2], env)
|
194
|
-
: 0
|
261
|
+
return a >>> b
|
195
262
|
},
|
196
263
|
[KEYWORDS.ARRAY_TYPE]: (args, env) => {
|
197
264
|
return args.length ? args.map((x) => evaluate(x, env)) : []
|
@@ -238,40 +305,86 @@ export const keywords = {
|
|
238
305
|
)
|
239
306
|
return value
|
240
307
|
},
|
241
|
-
[KEYWORDS.
|
242
|
-
if (
|
308
|
+
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
309
|
+
if (args.length !== 1 && args.length !== 3)
|
243
310
|
throw new RangeError(
|
244
|
-
`Invalid number of arguments
|
245
|
-
KEYWORDS.
|
246
|
-
} ${stringifyArgs(args)})`
|
311
|
+
`Invalid number of arguments for (${
|
312
|
+
KEYWORDS.SET_ARRAY
|
313
|
+
}) (or 1 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
247
314
|
)
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
315
|
+
const array = evaluate(args[0], env)
|
316
|
+
if (!Array.isArray(array))
|
317
|
+
throw new TypeError(
|
318
|
+
`First argument of (${KEYWORDS.SET_ARRAY}) must be an (${
|
319
|
+
KEYWORDS.ARRAY_TYPE
|
320
|
+
}) but got (${array}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
321
|
+
)
|
322
|
+
if (args.length === 1) {
|
323
|
+
array.pop()
|
324
|
+
} else {
|
325
|
+
const index = evaluate(args[1], env)
|
326
|
+
if (!Number.isInteger(index) || index < 0)
|
327
|
+
throw new TypeError(
|
328
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) must be a positive (${
|
329
|
+
KEYWORDS.NUMBER_TYPE
|
330
|
+
} integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
331
|
+
)
|
332
|
+
if (index > array.length)
|
255
333
|
throw new RangeError(
|
256
|
-
`
|
257
|
-
KEYWORDS.
|
258
|
-
} ${
|
259
|
-
|
260
|
-
} but got ${props.length}) (${
|
261
|
-
KEYWORDS.ANONYMOUS_FUNCTION
|
334
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
335
|
+
KEYWORDS.ARRAY_TYPE
|
336
|
+
}) bounds (index ${index} bounds ${array.length}) (${
|
337
|
+
KEYWORDS.SET_ARRAY
|
262
338
|
} ${stringifyArgs(args)})`
|
263
339
|
)
|
264
|
-
const
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
}
|
273
|
-
return evaluate(body, localEnv)
|
340
|
+
const value = evaluate(args[2], env)
|
341
|
+
if (value == undefined)
|
342
|
+
throw new RangeError(
|
343
|
+
`Trying to set a null value in (${KEYWORDS.ARRAY_TYPE}) at (${
|
344
|
+
KEYWORDS.SET_ARRAY
|
345
|
+
}). (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
346
|
+
)
|
347
|
+
array[index] = value
|
274
348
|
}
|
349
|
+
return array
|
350
|
+
},
|
351
|
+
[KEYWORDS.ARRAY_LENGTH]: (args, env) => {
|
352
|
+
if (args.length !== 1)
|
353
|
+
throw new RangeError(
|
354
|
+
`Invalid number of arguments for (${
|
355
|
+
KEYWORDS.ARRAY_LENGTH
|
356
|
+
}) (= 1 required) (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
|
357
|
+
)
|
358
|
+
const array = evaluate(args[0], env)
|
359
|
+
if (!Array.isArray(array))
|
360
|
+
throw new TypeError(
|
361
|
+
`First argument of (${KEYWORDS.ARRAY_LENGTH}) must be an ${
|
362
|
+
KEYWORDS.ARRAY_TYPE
|
363
|
+
} (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
|
364
|
+
)
|
365
|
+
return array.length
|
366
|
+
},
|
367
|
+
[KEYWORDS.IF]: (args, env) => {
|
368
|
+
if (args.length > 3 || args.length < 2)
|
369
|
+
throw new RangeError(
|
370
|
+
`Invalid number of arguments for (${
|
371
|
+
KEYWORDS.IF
|
372
|
+
}), expected (or (= 3) (= 2)) but got ${args.length} (${
|
373
|
+
KEYWORDS.IF
|
374
|
+
} ${stringifyArgs(args)})`
|
375
|
+
)
|
376
|
+
const condition = evaluate(args[0], env)
|
377
|
+
if (condition !== FALSE && condition !== TRUE)
|
378
|
+
throw new TypeError(
|
379
|
+
`Condition of (${KEYWORDS.IF}) must be ${TRUE} or ${FALSE} but got (${
|
380
|
+
KEYWORDS.IF
|
381
|
+
} ${stringifyArgs(args)})`
|
382
|
+
)
|
383
|
+
return condition
|
384
|
+
? evaluate(args[1], env)
|
385
|
+
: args.length === 3
|
386
|
+
? evaluate(args[2], env)
|
387
|
+
: 0
|
275
388
|
},
|
276
389
|
[KEYWORDS.NOT]: (args, env) => {
|
277
390
|
if (args.length !== 1)
|
@@ -459,32 +572,6 @@ export const keywords = {
|
|
459
572
|
)
|
460
573
|
return b
|
461
574
|
},
|
462
|
-
[KEYWORDS.CALL_FUNCTION]: (args, env) => {
|
463
|
-
if (!args.length)
|
464
|
-
throw new RangeError(
|
465
|
-
`Invalid number of arguments to (${
|
466
|
-
KEYWORDS.CALL_FUNCTION
|
467
|
-
}) (>= 1 required) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
468
|
-
)
|
469
|
-
const [first, ...rest] = args
|
470
|
-
if (first[TYPE] === WORD && first[VALUE] in keywords)
|
471
|
-
throw new TypeError(
|
472
|
-
`Following argument of (${
|
473
|
-
KEYWORDS.CALL_FUNCTION
|
474
|
-
}) must not be an reserved word (${
|
475
|
-
KEYWORDS.CALL_FUNCTION
|
476
|
-
} ${stringifyArgs(args)})`
|
477
|
-
)
|
478
|
-
const apply = evaluate(first, env)
|
479
|
-
if (typeof apply !== 'function')
|
480
|
-
throw new TypeError(
|
481
|
-
`First argument of (${KEYWORDS.CALL_FUNCTION}) must be a (${
|
482
|
-
KEYWORDS.ANONYMOUS_FUNCTION
|
483
|
-
}) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
484
|
-
)
|
485
|
-
|
486
|
-
return apply(rest, env)
|
487
|
-
},
|
488
575
|
[KEYWORDS.DEFINE_VARIABLE]: (args, env) => {
|
489
576
|
if (args.length !== 2)
|
490
577
|
throw new RangeError(
|
@@ -513,171 +600,101 @@ export const keywords = {
|
|
513
600
|
})
|
514
601
|
return env[name]
|
515
602
|
},
|
516
|
-
[KEYWORDS.
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
603
|
+
[KEYWORDS.ANONYMOUS_FUNCTION]: (args, env) => {
|
604
|
+
const params = args.slice(0, -1)
|
605
|
+
const body = args.at(-1)
|
606
|
+
return (props = [], scope) => {
|
607
|
+
if (props.length !== params.length)
|
608
|
+
throw new RangeError(
|
609
|
+
`Incorrect number of arguments for (${
|
610
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
611
|
+
} ${stringifyArgs(params)}) are provided. (expects ${
|
612
|
+
params.length
|
613
|
+
} but got ${props.length}) (${
|
614
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
615
|
+
} ${stringifyArgs(args)})`
|
616
|
+
)
|
617
|
+
const localEnv = Object.create(env)
|
618
|
+
// localEnv[KEYWORDS.BLOCK] = block[KEYWORDS.BLOCK]
|
619
|
+
for (let i = 0; i < props.length; ++i) {
|
620
|
+
const value = evaluate(props[i], scope)
|
621
|
+
Object.defineProperty(localEnv, params[i][VALUE], {
|
622
|
+
value,
|
623
|
+
writable: true
|
624
|
+
})
|
625
|
+
}
|
626
|
+
return evaluate(body, localEnv)
|
627
|
+
}
|
531
628
|
},
|
532
|
-
[KEYWORDS.
|
533
|
-
if (args.length
|
629
|
+
[KEYWORDS.CALL_FUNCTION]: (args, env) => {
|
630
|
+
if (!args.length)
|
534
631
|
throw new RangeError(
|
535
632
|
`Invalid number of arguments to (${
|
536
|
-
KEYWORDS.
|
537
|
-
}) (
|
633
|
+
KEYWORDS.CALL_FUNCTION
|
634
|
+
}) (>= 1 required) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
538
635
|
)
|
539
|
-
const
|
540
|
-
if (
|
636
|
+
const [first, ...rest] = args
|
637
|
+
if (first[TYPE] === WORD && first[VALUE] in keywords)
|
541
638
|
throw new TypeError(
|
542
|
-
`
|
543
|
-
KEYWORDS.
|
544
|
-
}) (${
|
545
|
-
|
546
|
-
|
547
|
-
},
|
548
|
-
[KEYWORDS.BITWISE_OR]: (args, env) => {
|
549
|
-
if (args.length !== 2)
|
550
|
-
throw new RangeError(
|
551
|
-
`Invalid number of arguments to (${
|
552
|
-
KEYWORDS.BITWISE_OR
|
553
|
-
}) (= 2 required). (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
639
|
+
`Following argument of (${
|
640
|
+
KEYWORDS.CALL_FUNCTION
|
641
|
+
}) must not be an reserved word (${
|
642
|
+
KEYWORDS.CALL_FUNCTION
|
643
|
+
} ${stringifyArgs(args)})`
|
554
644
|
)
|
555
|
-
const
|
556
|
-
|
557
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
645
|
+
const apply = evaluate(first, env)
|
646
|
+
if (typeof apply !== 'function')
|
558
647
|
throw new TypeError(
|
559
|
-
`
|
560
|
-
KEYWORDS.
|
561
|
-
}) (${KEYWORDS.
|
648
|
+
`First argument of (${KEYWORDS.CALL_FUNCTION}) must be a (${
|
649
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
650
|
+
}) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
562
651
|
)
|
563
|
-
|
652
|
+
|
653
|
+
return apply(rest, env)
|
564
654
|
},
|
565
|
-
[KEYWORDS.
|
566
|
-
if (args.length
|
655
|
+
[KEYWORDS.BLOCK]: (args, env) => {
|
656
|
+
if (!args.length)
|
567
657
|
throw new RangeError(
|
568
|
-
`Invalid number of arguments to (${
|
569
|
-
KEYWORDS.
|
570
|
-
}
|
571
|
-
)
|
572
|
-
const a = evaluate(args[0], env)
|
573
|
-
const b = evaluate(args[1], env)
|
574
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
575
|
-
throw new TypeError(
|
576
|
-
`Not all arguments of (${KEYWORDS.BITWISE_XOR}) are (${
|
577
|
-
KEYWORDS.NUMBER_TYPE
|
578
|
-
}) (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
658
|
+
`Invalid number of arguments to (${KEYWORDS.BLOCK}) (>= 1 required) (${
|
659
|
+
KEYWORDS.BLOCK
|
660
|
+
} ${stringifyArgs(args)})`
|
579
661
|
)
|
580
|
-
return
|
662
|
+
return args.reduce((_, x) => evaluate(x, env), 0)
|
581
663
|
},
|
582
|
-
[KEYWORDS.
|
583
|
-
if (args.length !==
|
664
|
+
[KEYWORDS.IS_ATOM]: (args, env) => {
|
665
|
+
if (args.length !== 1)
|
584
666
|
throw new RangeError(
|
585
|
-
`Invalid number of arguments
|
586
|
-
KEYWORDS.
|
587
|
-
}) (=
|
588
|
-
args
|
589
|
-
)})`
|
590
|
-
)
|
591
|
-
const a = evaluate(args[0], env)
|
592
|
-
const b = evaluate(args[1], env)
|
593
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
594
|
-
throw new TypeError(
|
595
|
-
`Not all arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) are (${
|
596
|
-
KEYWORDS.NUMBER_TYPE
|
597
|
-
}) (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(args)})`
|
667
|
+
`Invalid number of arguments for (${
|
668
|
+
KEYWORDS.IS_ATOM
|
669
|
+
}) (= 1 required) (${KEYWORDS.IS_ATOM} ${stringifyArgs(args)})`
|
598
670
|
)
|
599
|
-
return
|
671
|
+
return +(typeof evaluate(args[0], env) === 'number')
|
600
672
|
},
|
601
|
-
[KEYWORDS.
|
602
|
-
if (args.length !==
|
673
|
+
[KEYWORDS.IS_LAMBDA]: (args, env) => {
|
674
|
+
if (args.length !== 1)
|
603
675
|
throw new RangeError(
|
604
|
-
`Invalid number of arguments
|
605
|
-
KEYWORDS.
|
606
|
-
}) (=
|
607
|
-
args
|
608
|
-
)})`
|
609
|
-
)
|
610
|
-
const a = evaluate(args[0], env)
|
611
|
-
const b = evaluate(args[1], env)
|
612
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
613
|
-
throw new TypeError(
|
614
|
-
`Not all arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) are (${
|
615
|
-
KEYWORDS.NUMBER_TYPE
|
616
|
-
}) (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
676
|
+
`Invalid number of arguments for (${
|
677
|
+
KEYWORDS.IS_LAMBDA
|
678
|
+
}) (= 1 required) (${KEYWORDS.IS_LAMBDA} ${stringifyArgs(args)})`
|
617
679
|
)
|
618
|
-
return
|
680
|
+
return +(typeof evaluate(args[0], env) === 'function')
|
619
681
|
},
|
620
|
-
|
621
|
-
|
682
|
+
// Not sure about these
|
683
|
+
[KEYWORDS.THROW]: (args, env) => {
|
684
|
+
if (args.length !== 1)
|
622
685
|
throw new RangeError(
|
623
|
-
`Invalid number of arguments to (${
|
624
|
-
KEYWORDS.
|
625
|
-
}) (= 2 required). (${
|
626
|
-
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
686
|
+
`Invalid number of arguments to (${KEYWORDS.THROW}) (= 1 required) (${
|
687
|
+
KEYWORDS.THROW
|
627
688
|
} ${stringifyArgs(args)})`
|
628
689
|
)
|
629
|
-
const
|
630
|
-
|
631
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
632
|
-
throw new TypeError(
|
633
|
-
`Not all arguments of (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT}) are (${
|
634
|
-
KEYWORDS.NUMBER_TYPE
|
635
|
-
}) (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
636
|
-
)
|
637
|
-
return a >>> b
|
638
|
-
},
|
639
|
-
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
640
|
-
if (args.length !== 1 && args.length !== 3)
|
641
|
-
throw new RangeError(
|
642
|
-
`Invalid number of arguments for (${
|
643
|
-
KEYWORDS.SET_ARRAY
|
644
|
-
}) (or 1 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
645
|
-
)
|
646
|
-
const array = evaluate(args[0], env)
|
647
|
-
if (!Array.isArray(array))
|
690
|
+
const expression = evaluate(args[0], env)
|
691
|
+
if (!Array.isArray(expression))
|
648
692
|
throw new TypeError(
|
649
|
-
`
|
693
|
+
`Argument of (${KEYWORDS.THROW}) must be an (${
|
650
694
|
KEYWORDS.ARRAY_TYPE
|
651
|
-
}) but got (${
|
695
|
+
}) but got (${expression}) (${KEYWORDS.THROW} ${stringifyArgs(args)})`
|
652
696
|
)
|
653
|
-
|
654
|
-
array.pop()
|
655
|
-
} else {
|
656
|
-
const index = evaluate(args[1], env)
|
657
|
-
if (!Number.isInteger(index) || index < 0)
|
658
|
-
throw new TypeError(
|
659
|
-
`Second argument of (${KEYWORDS.SET_ARRAY}) must be a positive (${
|
660
|
-
KEYWORDS.NUMBER_TYPE
|
661
|
-
} integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
662
|
-
)
|
663
|
-
if (index > array.length)
|
664
|
-
throw new RangeError(
|
665
|
-
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
666
|
-
KEYWORDS.ARRAY_TYPE
|
667
|
-
}) bounds (index ${index} bounds ${array.length}) (${
|
668
|
-
KEYWORDS.SET_ARRAY
|
669
|
-
} ${stringifyArgs(args)})`
|
670
|
-
)
|
671
|
-
const value = evaluate(args[2], env)
|
672
|
-
if (value == undefined)
|
673
|
-
throw new RangeError(
|
674
|
-
`Trying to set a null value in (${KEYWORDS.ARRAY_TYPE}) at (${
|
675
|
-
KEYWORDS.SET_ARRAY
|
676
|
-
}). (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
677
|
-
)
|
678
|
-
array[index] = value
|
679
|
-
}
|
680
|
-
return array
|
697
|
+
throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
|
681
698
|
},
|
682
699
|
[KEYWORDS.LOG]: (args, env) => {
|
683
700
|
if (args.length !== 1)
|
@@ -737,23 +754,5 @@ export const keywords = {
|
|
737
754
|
)
|
738
755
|
console.clear()
|
739
756
|
return 0
|
740
|
-
},
|
741
|
-
|
742
|
-
// Not sure about these
|
743
|
-
[KEYWORDS.THROW]: (args, env) => {
|
744
|
-
if (args.length !== 1)
|
745
|
-
throw new RangeError(
|
746
|
-
`Invalid number of arguments to (${KEYWORDS.THROW}) (= 1 required) (${
|
747
|
-
KEYWORDS.THROW
|
748
|
-
} ${stringifyArgs(args)})`
|
749
|
-
)
|
750
|
-
const expression = evaluate(args[0], env)
|
751
|
-
if (!Array.isArray(expression))
|
752
|
-
throw new TypeError(
|
753
|
-
`Argument of (${KEYWORDS.THROW}) must be an (${
|
754
|
-
KEYWORDS.ARRAY_TYPE
|
755
|
-
}) but got (${expression}) (${KEYWORDS.THROW} ${stringifyArgs(args)})`
|
756
|
-
)
|
757
|
-
throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
|
758
757
|
}
|
759
758
|
}
|
package/src/macros.js
CHANGED
@@ -25,9 +25,7 @@ export const SUGGAR = {
|
|
25
25
|
LIST_TYPE: 'list',
|
26
26
|
POWER: '**',
|
27
27
|
INTEGER_DEVISION: '//',
|
28
|
-
CONDITION: 'cond'
|
29
|
-
RECURSION: 'recursive',
|
30
|
-
CACHE: 'memoized'
|
28
|
+
CONDITION: 'cond'
|
31
29
|
}
|
32
30
|
export const deSuggarAst = (ast) => {
|
33
31
|
if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
|
package/src/utils.js
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
|
-
import { comp } from './compiler.js'
|
2
|
+
import { comp, OPTIMIZATIONS } from './compiler.js'
|
3
3
|
import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
|
4
4
|
import { evaluate, run } from './evaluator.js'
|
5
5
|
import { AST, isLeaf, LISP } from './parser.js'
|
6
6
|
import {
|
7
7
|
deSuggarAst,
|
8
8
|
deSuggarSource,
|
9
|
-
handleUnbalancedQuotes
|
10
|
-
SUGGAR
|
9
|
+
handleUnbalancedQuotes
|
11
10
|
} from './macros.js'
|
12
11
|
export const logError = (error) =>
|
13
12
|
console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
|
@@ -88,8 +87,8 @@ export const isForbiddenVariableName = (name) => {
|
|
88
87
|
switch (name) {
|
89
88
|
case '_':
|
90
89
|
case KEYWORDS.DEFINE_VARIABLE:
|
91
|
-
case
|
92
|
-
case
|
90
|
+
case OPTIMIZATIONS.RECURSION:
|
91
|
+
case OPTIMIZATIONS.CACHE:
|
93
92
|
return true
|
94
93
|
default:
|
95
94
|
return !isNaN(name[0])
|