fez-lisp 1.3.2 → 1.3.4
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 +9 -6
- package/src/interpreter.js +346 -338
- package/src/keywords.js +5 -2
- package/src/macros.js +6 -8
- package/src/utils.js +10 -8
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()
|
@@ -230,7 +233,7 @@ const compile = (tree, Drill) => {
|
|
230
233
|
case KEYWORDS.IS_LAMBDA:
|
231
234
|
Drill.Helpers.add('lambda_predicate')
|
232
235
|
return `lambda_predicate(${compile(Arguments[0], Drill)});`
|
233
|
-
case KEYWORDS.
|
236
|
+
case KEYWORDS.CREATE_ARRAY:
|
234
237
|
return `[${parseArgs(Arguments, Drill)}];`
|
235
238
|
case KEYWORDS.ARRAY_LENGTH:
|
236
239
|
Drill.Helpers.add('length')
|
package/src/interpreter.js
CHANGED
@@ -1,40 +1,87 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
TYPE,
|
3
|
+
VALUE,
|
4
|
+
WORD,
|
5
|
+
KEYWORDS,
|
6
|
+
FALSE,
|
7
|
+
TRUE,
|
8
|
+
TYPES,
|
9
|
+
RUNTIME_TYPES
|
10
|
+
} from './keywords.js'
|
2
11
|
import { evaluate } from './evaluator.js'
|
3
12
|
import { isForbiddenVariableName, stringifyArgs } from './utils.js'
|
4
13
|
export const keywords = {
|
5
|
-
[KEYWORDS.
|
6
|
-
if (args.length !== 2)
|
14
|
+
[KEYWORDS.ADDITION]: (args, env) => {
|
15
|
+
if (args.length !== 0 && args.length !== 2)
|
7
16
|
throw new RangeError(
|
8
17
|
`Invalid number of arguments for (${
|
9
|
-
KEYWORDS.
|
10
|
-
}), expected (= 2) but got ${args.length}. (${
|
11
|
-
KEYWORDS.
|
18
|
+
KEYWORDS.ADDITION
|
19
|
+
}), expected (or (= 2) (= 0)) but got ${args.length}. (${
|
20
|
+
KEYWORDS.ADDITION
|
12
21
|
} ${stringifyArgs(args)})`
|
13
22
|
)
|
14
23
|
const a = evaluate(args[0], env)
|
15
24
|
if (typeof a !== 'number')
|
16
25
|
throw new TypeError(
|
17
|
-
`First
|
18
|
-
|
19
|
-
}) (${KEYWORDS.
|
26
|
+
`First arguments of (${KEYWORDS.ADDITION}) is not a (${
|
27
|
+
RUNTIME_TYPES.NUMBER
|
28
|
+
}) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
|
20
29
|
)
|
21
30
|
const b = evaluate(args[1], env)
|
22
31
|
if (typeof b !== 'number')
|
23
32
|
throw new TypeError(
|
24
|
-
`Second
|
25
|
-
|
26
|
-
}) (${KEYWORDS.
|
33
|
+
`Second arguments of (${KEYWORDS.ADDITION}) is not a (${
|
34
|
+
RUNTIME_TYPES.NUMBER
|
35
|
+
}) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
|
27
36
|
)
|
28
|
-
|
37
|
+
return a + b
|
38
|
+
},
|
39
|
+
[KEYWORDS.MULTIPLICATION]: (args, env) => {
|
40
|
+
if (args.length !== 2)
|
41
|
+
throw new RangeError(
|
42
|
+
`Invalid number of arguments for (${KEYWORDS.MULTIPLICATION}), expected (= 2) but got ${args.length}.`
|
43
|
+
)
|
44
|
+
const a = evaluate(args[0], env)
|
45
|
+
if (typeof a !== 'number')
|
29
46
|
throw new TypeError(
|
30
|
-
`
|
31
|
-
|
32
|
-
})
|
33
|
-
|
47
|
+
`First arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
|
48
|
+
RUNTIME_TYPES.NUMBER
|
49
|
+
}) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
|
50
|
+
)
|
51
|
+
const b = evaluate(args[1], env)
|
52
|
+
if (typeof b !== 'number')
|
53
|
+
throw new TypeError(
|
54
|
+
`Second arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
|
55
|
+
RUNTIME_TYPES.NUMBER
|
56
|
+
}) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
|
57
|
+
)
|
58
|
+
return a * b
|
59
|
+
},
|
60
|
+
[KEYWORDS.SUBTRACTION]: (args, env) => {
|
61
|
+
if (args.length !== 1 && args.length !== 2)
|
62
|
+
throw new RangeError(
|
63
|
+
`Invalid number of arguments for (${
|
64
|
+
KEYWORDS.SUBTRACTION
|
65
|
+
}), expected (or (= 1)) (= 2) but got ${args.length}. (${
|
66
|
+
KEYWORDS.SUBTRACTION
|
34
67
|
} ${stringifyArgs(args)})`
|
35
68
|
)
|
36
|
-
|
37
|
-
|
69
|
+
const a = evaluate(args[0], env)
|
70
|
+
if (typeof a !== 'number')
|
71
|
+
throw new TypeError(
|
72
|
+
`First argument of (${KEYWORDS.SUBTRACTION}) is not a (${
|
73
|
+
RUNTIME_TYPES.NUMBER
|
74
|
+
}) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
|
75
|
+
)
|
76
|
+
if (args.length === 1) return -a
|
77
|
+
const b = evaluate(args[1], env)
|
78
|
+
if (typeof b !== 'number')
|
79
|
+
throw new TypeError(
|
80
|
+
`Second argument of (${KEYWORDS.SUBTRACTION}) is not a (${
|
81
|
+
RUNTIME_TYPES.NUMBER
|
82
|
+
}) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
|
83
|
+
)
|
84
|
+
return a - b
|
38
85
|
},
|
39
86
|
[KEYWORDS.DIVISION]: (args, env) => {
|
40
87
|
if (args.length !== 2)
|
@@ -45,14 +92,14 @@ export const keywords = {
|
|
45
92
|
if (typeof a !== 'number')
|
46
93
|
throw new TypeError(
|
47
94
|
`First argument of (${KEYWORDS.DIVISION}) is not (${
|
48
|
-
|
95
|
+
RUNTIME_TYPES.NUMBER
|
49
96
|
}) (${KEYWORDS.DIVISION} ${stringifyArgs(args)})`
|
50
97
|
)
|
51
98
|
const b = evaluate(args[1], env)
|
52
99
|
if (typeof b !== 'number')
|
53
100
|
throw new TypeError(
|
54
101
|
`Second argument of (${KEYWORDS.DIVISION}) is not (${
|
55
|
-
|
102
|
+
RUNTIME_TYPES.NUMBER
|
56
103
|
}) (${KEYWORDS.DIVISION} ${stringifyArgs(args)})`
|
57
104
|
)
|
58
105
|
if (b === 0)
|
@@ -65,135 +112,164 @@ export const keywords = {
|
|
65
112
|
)
|
66
113
|
return a / b
|
67
114
|
},
|
68
|
-
[KEYWORDS.
|
69
|
-
if (args.length !==
|
115
|
+
[KEYWORDS.REMAINDER_OF_DIVISION]: (args, env) => {
|
116
|
+
if (args.length !== 2)
|
70
117
|
throw new RangeError(
|
71
118
|
`Invalid number of arguments for (${
|
72
|
-
KEYWORDS.
|
73
|
-
}) (=
|
119
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
120
|
+
}), expected (= 2) but got ${args.length}. (${
|
121
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
122
|
+
} ${stringifyArgs(args)})`
|
74
123
|
)
|
75
|
-
const
|
76
|
-
if (
|
124
|
+
const a = evaluate(args[0], env)
|
125
|
+
if (typeof a !== 'number')
|
77
126
|
throw new TypeError(
|
78
|
-
`First argument of (${KEYWORDS.
|
79
|
-
|
80
|
-
} (${KEYWORDS.
|
127
|
+
`First argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
128
|
+
RUNTIME_TYPES.NUMBER
|
129
|
+
}) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
|
81
130
|
)
|
82
|
-
|
131
|
+
const b = evaluate(args[1], env)
|
132
|
+
if (typeof b !== 'number')
|
133
|
+
throw new TypeError(
|
134
|
+
`Second argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
|
135
|
+
RUNTIME_TYPES.NUMBER
|
136
|
+
}) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
|
137
|
+
)
|
138
|
+
if (b === 0)
|
139
|
+
throw new TypeError(
|
140
|
+
`Second argument of (${
|
141
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
142
|
+
}) can't be a (0) (division by 0 is not allowed) (${
|
143
|
+
KEYWORDS.REMAINDER_OF_DIVISION
|
144
|
+
} ${stringifyArgs(args)})`
|
145
|
+
)
|
146
|
+
|
147
|
+
return a % b
|
83
148
|
},
|
84
|
-
[KEYWORDS.
|
85
|
-
if (args.length
|
149
|
+
[KEYWORDS.BITWISE_AND]: (args, env) => {
|
150
|
+
if (args.length < 2)
|
86
151
|
throw new RangeError(
|
87
|
-
`Invalid number of arguments
|
88
|
-
KEYWORDS.
|
89
|
-
}) (=
|
152
|
+
`Invalid number of arguments to (${
|
153
|
+
KEYWORDS.BITWISE_AND
|
154
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
|
90
155
|
)
|
91
|
-
|
156
|
+
const operands = args.map((a) => evaluate(a, env))
|
157
|
+
if (operands.some((x) => typeof x !== 'number'))
|
158
|
+
throw new TypeError(
|
159
|
+
`Not all arguments of (${KEYWORDS.BITWISE_AND}) are ${
|
160
|
+
RUNTIME_TYPES.NUMBER
|
161
|
+
} (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
|
162
|
+
)
|
163
|
+
return operands.reduce((acc, x) => acc & x)
|
92
164
|
},
|
93
|
-
[KEYWORDS.
|
165
|
+
[KEYWORDS.BITWISE_NOT]: (args, env) => {
|
94
166
|
if (args.length !== 1)
|
95
167
|
throw new RangeError(
|
96
|
-
`Invalid number of arguments
|
97
|
-
KEYWORDS.
|
98
|
-
}) (= 1 required) (${KEYWORDS.
|
168
|
+
`Invalid number of arguments to (${
|
169
|
+
KEYWORDS.BITWISE_NOT
|
170
|
+
}) (= 1 required). (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
|
99
171
|
)
|
100
|
-
|
172
|
+
const operand = evaluate(args[0], env)
|
173
|
+
if (typeof operand !== 'number')
|
174
|
+
throw new TypeError(
|
175
|
+
`Argument of (${KEYWORDS.BITWISE_NOT}) is not a (${
|
176
|
+
RUNTIME_TYPES.NUMBER
|
177
|
+
}) (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
|
178
|
+
)
|
179
|
+
return ~operand
|
101
180
|
},
|
102
|
-
[KEYWORDS.
|
103
|
-
if (args.length !==
|
181
|
+
[KEYWORDS.BITWISE_OR]: (args, env) => {
|
182
|
+
if (args.length !== 2)
|
104
183
|
throw new RangeError(
|
105
|
-
`Invalid number of arguments
|
106
|
-
KEYWORDS.
|
107
|
-
})
|
108
|
-
KEYWORDS.ADDITION
|
109
|
-
} ${stringifyArgs(args)})`
|
184
|
+
`Invalid number of arguments to (${
|
185
|
+
KEYWORDS.BITWISE_OR
|
186
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
110
187
|
)
|
111
188
|
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
189
|
const b = evaluate(args[1], env)
|
119
|
-
if (typeof b !== 'number')
|
190
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
120
191
|
throw new TypeError(
|
121
|
-
`
|
122
|
-
|
123
|
-
}) (${KEYWORDS.
|
192
|
+
`Not all arguments of (${KEYWORDS.BITWISE_OR}) are (${
|
193
|
+
RUNTIME_TYPES.NUMBER
|
194
|
+
}) (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
|
124
195
|
)
|
125
|
-
return a
|
196
|
+
return a | b
|
126
197
|
},
|
127
|
-
[KEYWORDS.
|
198
|
+
[KEYWORDS.BITWISE_XOR]: (args, env) => {
|
128
199
|
if (args.length !== 2)
|
129
200
|
throw new RangeError(
|
130
|
-
`Invalid number of arguments
|
201
|
+
`Invalid number of arguments to (${
|
202
|
+
KEYWORDS.BITWISE_XOR
|
203
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
131
204
|
)
|
132
205
|
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
206
|
const b = evaluate(args[1], env)
|
140
|
-
if (typeof b !== 'number')
|
207
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
141
208
|
throw new TypeError(
|
142
|
-
`
|
143
|
-
|
144
|
-
}) (${KEYWORDS.
|
209
|
+
`Not all arguments of (${KEYWORDS.BITWISE_XOR}) are (${
|
210
|
+
RUNTIME_TYPES.NUMBER
|
211
|
+
}) (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
|
145
212
|
)
|
146
|
-
return a
|
213
|
+
return a ^ b
|
147
214
|
},
|
148
|
-
[KEYWORDS.
|
149
|
-
if (args.length !==
|
215
|
+
[KEYWORDS.BITWISE_LEFT_SHIFT]: (args, env) => {
|
216
|
+
if (args.length !== 2)
|
150
217
|
throw new RangeError(
|
151
|
-
`Invalid number of arguments
|
152
|
-
KEYWORDS.
|
153
|
-
})
|
154
|
-
|
155
|
-
|
218
|
+
`Invalid number of arguments to (${
|
219
|
+
KEYWORDS.BITWISE_LEFT_SHIFT
|
220
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(
|
221
|
+
args
|
222
|
+
)})`
|
156
223
|
)
|
157
224
|
const a = evaluate(args[0], env)
|
158
|
-
|
225
|
+
const b = evaluate(args[1], env)
|
226
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
159
227
|
throw new TypeError(
|
160
|
-
`
|
161
|
-
|
162
|
-
}) (${KEYWORDS.
|
228
|
+
`Not all arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) are (${
|
229
|
+
RUNTIME_TYPES.NUMBER
|
230
|
+
}) (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(args)})`
|
163
231
|
)
|
164
|
-
|
232
|
+
return a << b
|
233
|
+
},
|
234
|
+
[KEYWORDS.BITWISE_RIGHT_SHIFT]: (args, env) => {
|
235
|
+
if (args.length !== 2)
|
236
|
+
throw new RangeError(
|
237
|
+
`Invalid number of arguments to (${
|
238
|
+
KEYWORDS.BITWISE_RIGHT_SHIFT
|
239
|
+
}) (= 2 required). (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(
|
240
|
+
args
|
241
|
+
)})`
|
242
|
+
)
|
243
|
+
const a = evaluate(args[0], env)
|
165
244
|
const b = evaluate(args[1], env)
|
166
|
-
if (typeof b !== 'number')
|
245
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
167
246
|
throw new TypeError(
|
168
|
-
`
|
169
|
-
|
170
|
-
}) (${KEYWORDS.
|
247
|
+
`Not all arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) are (${
|
248
|
+
RUNTIME_TYPES.NUMBER
|
249
|
+
}) (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
171
250
|
)
|
172
|
-
return a
|
251
|
+
return a >> b
|
173
252
|
},
|
174
|
-
[KEYWORDS.
|
175
|
-
if (args.length
|
253
|
+
[KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT]: (args, env) => {
|
254
|
+
if (args.length !== 2)
|
176
255
|
throw new RangeError(
|
177
|
-
`Invalid number of arguments
|
178
|
-
KEYWORDS.
|
179
|
-
})
|
180
|
-
KEYWORDS.
|
256
|
+
`Invalid number of arguments to (${
|
257
|
+
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
258
|
+
}) (= 2 required). (${
|
259
|
+
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
181
260
|
} ${stringifyArgs(args)})`
|
182
261
|
)
|
183
|
-
const
|
184
|
-
|
262
|
+
const a = evaluate(args[0], env)
|
263
|
+
const b = evaluate(args[1], env)
|
264
|
+
if (typeof a !== 'number' || typeof b !== 'number')
|
185
265
|
throw new TypeError(
|
186
|
-
`
|
187
|
-
|
188
|
-
} ${stringifyArgs(args)})`
|
266
|
+
`Not all arguments of (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT}) are (${
|
267
|
+
RUNTIME_TYPES.NUMBER
|
268
|
+
}) (${KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT} ${stringifyArgs(args)})`
|
189
269
|
)
|
190
|
-
return
|
191
|
-
? evaluate(args[1], env)
|
192
|
-
: args.length === 3
|
193
|
-
? evaluate(args[2], env)
|
194
|
-
: 0
|
270
|
+
return a >>> b
|
195
271
|
},
|
196
|
-
[KEYWORDS.
|
272
|
+
[KEYWORDS.CREATE_ARRAY]: (args, env) => {
|
197
273
|
return args.length ? args.map((x) => evaluate(x, env)) : []
|
198
274
|
},
|
199
275
|
[KEYWORDS.GET_ARRAY]: (args, env) => {
|
@@ -207,71 +283,117 @@ export const keywords = {
|
|
207
283
|
if (!Array.isArray(array))
|
208
284
|
throw new TypeError(
|
209
285
|
`First argument of (${KEYWORDS.GET_ARRAY}) must be an (${
|
210
|
-
|
286
|
+
RUNTIME_TYPES.ARRAY
|
211
287
|
})) (${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
212
288
|
)
|
213
289
|
if (array.length === 0)
|
214
290
|
throw new RangeError(
|
215
291
|
`First argument of (${KEYWORDS.GET_ARRAY}) is an empty (${
|
216
|
-
|
292
|
+
RUNTIME_TYPES.ARRAY
|
217
293
|
})) (${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)}))`
|
218
294
|
)
|
219
295
|
const index = evaluate(args[1], env)
|
220
296
|
if (!Number.isInteger(index))
|
221
297
|
throw new TypeError(
|
222
298
|
`Second argument of (${KEYWORDS.GET_ARRAY}) must be an (32 bit ${
|
223
|
-
|
299
|
+
RUNTIME_TYPES.NUMBER
|
224
300
|
}) (${index}) (${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
225
301
|
)
|
226
302
|
if (index > array.length - 1 || index * -1 > array.length)
|
227
303
|
throw new RangeError(
|
228
304
|
`Second argument of (${KEYWORDS.GET_ARRAY}) is outside of (${
|
229
|
-
|
305
|
+
RUNTIME_TYPES.ARRAY
|
230
306
|
}) bounds (${index}) (${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
231
307
|
)
|
232
308
|
const value = array.at(index)
|
233
309
|
if (value == undefined)
|
234
310
|
throw new RangeError(
|
235
|
-
`Trying to get a null value in (${
|
311
|
+
`Trying to get a null value in (${RUNTIME_TYPES.ARRAY}) at (${
|
236
312
|
KEYWORDS.GET_ARRAY
|
237
313
|
}) (${KEYWORDS.GET_ARRAY} ${stringifyArgs(args)})`
|
238
314
|
)
|
239
315
|
return value
|
240
316
|
},
|
241
|
-
[KEYWORDS.
|
242
|
-
if (
|
317
|
+
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
318
|
+
if (args.length !== 1 && args.length !== 3)
|
243
319
|
throw new RangeError(
|
244
|
-
`Invalid number of arguments
|
245
|
-
KEYWORDS.
|
246
|
-
} ${stringifyArgs(args)})`
|
320
|
+
`Invalid number of arguments for (${
|
321
|
+
KEYWORDS.SET_ARRAY
|
322
|
+
}) (or 1 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
247
323
|
)
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
324
|
+
const array = evaluate(args[0], env)
|
325
|
+
if (!Array.isArray(array))
|
326
|
+
throw new TypeError(
|
327
|
+
`First argument of (${KEYWORDS.SET_ARRAY}) must be an (${
|
328
|
+
RUNTIME_TYPES.ARRAY
|
329
|
+
}) but got (${array}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
330
|
+
)
|
331
|
+
if (args.length === 1) {
|
332
|
+
array.pop()
|
333
|
+
} else {
|
334
|
+
const index = evaluate(args[1], env)
|
335
|
+
if (!Number.isInteger(index) || index < 0)
|
336
|
+
throw new TypeError(
|
337
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) must be a positive (${
|
338
|
+
RUNTIME_TYPES.NUMBER
|
339
|
+
} integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
340
|
+
)
|
341
|
+
if (index > array.length)
|
255
342
|
throw new RangeError(
|
256
|
-
`
|
257
|
-
|
258
|
-
} ${
|
259
|
-
|
260
|
-
} but got ${props.length}) (${
|
261
|
-
KEYWORDS.ANONYMOUS_FUNCTION
|
343
|
+
`Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
|
344
|
+
RUNTIME_TYPES.ARRAY
|
345
|
+
}) bounds (index ${index} bounds ${array.length}) (${
|
346
|
+
KEYWORDS.SET_ARRAY
|
262
347
|
} ${stringifyArgs(args)})`
|
263
348
|
)
|
264
|
-
const
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
}
|
273
|
-
return evaluate(body, localEnv)
|
349
|
+
const value = evaluate(args[2], env)
|
350
|
+
if (value == undefined)
|
351
|
+
throw new RangeError(
|
352
|
+
`Trying to set a null value in (${RUNTIME_TYPES.ARRAY}) at (${
|
353
|
+
KEYWORDS.SET_ARRAY
|
354
|
+
}). (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
|
355
|
+
)
|
356
|
+
array[index] = value
|
274
357
|
}
|
358
|
+
return array
|
359
|
+
},
|
360
|
+
[KEYWORDS.ARRAY_LENGTH]: (args, env) => {
|
361
|
+
if (args.length !== 1)
|
362
|
+
throw new RangeError(
|
363
|
+
`Invalid number of arguments for (${
|
364
|
+
KEYWORDS.ARRAY_LENGTH
|
365
|
+
}) (= 1 required) (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
|
366
|
+
)
|
367
|
+
const array = evaluate(args[0], env)
|
368
|
+
if (!Array.isArray(array))
|
369
|
+
throw new TypeError(
|
370
|
+
`First argument of (${KEYWORDS.ARRAY_LENGTH}) must be an ${
|
371
|
+
RUNTIME_TYPES.ARRAY
|
372
|
+
} (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
|
373
|
+
)
|
374
|
+
return array.length
|
375
|
+
},
|
376
|
+
[KEYWORDS.IF]: (args, env) => {
|
377
|
+
if (args.length > 3 || args.length < 2)
|
378
|
+
throw new RangeError(
|
379
|
+
`Invalid number of arguments for (${
|
380
|
+
KEYWORDS.IF
|
381
|
+
}), expected (or (= 3) (= 2)) but got ${args.length} (${
|
382
|
+
KEYWORDS.IF
|
383
|
+
} ${stringifyArgs(args)})`
|
384
|
+
)
|
385
|
+
const condition = evaluate(args[0], env)
|
386
|
+
if (condition !== FALSE && condition !== TRUE)
|
387
|
+
throw new TypeError(
|
388
|
+
`Condition of (${KEYWORDS.IF}) must be ${TRUE} or ${FALSE} but got (${
|
389
|
+
KEYWORDS.IF
|
390
|
+
} ${stringifyArgs(args)})`
|
391
|
+
)
|
392
|
+
return condition
|
393
|
+
? evaluate(args[1], env)
|
394
|
+
: args.length === 3
|
395
|
+
? evaluate(args[2], env)
|
396
|
+
: 0
|
275
397
|
},
|
276
398
|
[KEYWORDS.NOT]: (args, env) => {
|
277
399
|
if (args.length !== 1)
|
@@ -294,13 +416,13 @@ export const keywords = {
|
|
294
416
|
if (typeof a !== 'number')
|
295
417
|
throw new TypeError(
|
296
418
|
`Invalid use of (${KEYWORDS.EQUAL}), first argument is not an ${
|
297
|
-
|
419
|
+
RUNTIME_TYPES.NUMBER
|
298
420
|
} (${KEYWORDS.EQUAL} ${stringifyArgs(args)})`
|
299
421
|
)
|
300
422
|
if (typeof b !== 'number')
|
301
423
|
throw new TypeError(
|
302
424
|
`Invalid use of (${KEYWORDS.EQUAL}), second argument are not an ${
|
303
|
-
|
425
|
+
RUNTIME_TYPES.NUMBER
|
304
426
|
} (${KEYWORDS.EQUAL} ${stringifyArgs(args)})`
|
305
427
|
)
|
306
428
|
return +(a === b)
|
@@ -317,13 +439,13 @@ export const keywords = {
|
|
317
439
|
if (typeof a !== 'number')
|
318
440
|
throw new TypeError(
|
319
441
|
`Invalid use of (${KEYWORDS.LESS_THAN}), first argument is not an ${
|
320
|
-
|
442
|
+
RUNTIME_TYPES.NUMBER
|
321
443
|
} (${KEYWORDS.LESS_THAN} ${stringifyArgs(args)})`
|
322
444
|
)
|
323
445
|
if (typeof b !== 'number')
|
324
446
|
throw new TypeError(
|
325
447
|
`Invalid use of (${KEYWORDS.LESS_THAN}), second argument are not an ${
|
326
|
-
|
448
|
+
RUNTIME_TYPES.NUMBER
|
327
449
|
} (${KEYWORDS.LESS_THAN} ${stringifyArgs(args)})`
|
328
450
|
)
|
329
451
|
return +(a < b)
|
@@ -340,14 +462,14 @@ export const keywords = {
|
|
340
462
|
if (typeof a !== 'number')
|
341
463
|
throw new TypeError(
|
342
464
|
`Invalid use of (${KEYWORDS.GREATHER_THAN}), first argument is not an ${
|
343
|
-
|
465
|
+
RUNTIME_TYPES.NUMBER
|
344
466
|
} (${KEYWORDS.GREATHER_THAN} ${stringifyArgs(args)})`
|
345
467
|
)
|
346
468
|
if (typeof b !== 'number')
|
347
469
|
throw new TypeError(
|
348
470
|
`Invalid use of (${
|
349
471
|
KEYWORDS.GREATHER_THAN
|
350
|
-
}), second argument are not an ${
|
472
|
+
}), second argument are not an ${RUNTIME_TYPES.NUMBER} (${
|
351
473
|
KEYWORDS.GREATHER_THAN
|
352
474
|
} ${stringifyArgs(args)})`
|
353
475
|
)
|
@@ -368,7 +490,7 @@ export const keywords = {
|
|
368
490
|
throw new TypeError(
|
369
491
|
`Invalid use of (${
|
370
492
|
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
371
|
-
}), first argument is not an ${
|
493
|
+
}), first argument is not an ${RUNTIME_TYPES.NUMBER} (${
|
372
494
|
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
373
495
|
} ${stringifyArgs(args)})`
|
374
496
|
)
|
@@ -376,7 +498,7 @@ export const keywords = {
|
|
376
498
|
throw new TypeError(
|
377
499
|
`Invalid use of (${
|
378
500
|
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
379
|
-
}), second argument are not an ${
|
501
|
+
}), second argument are not an ${RUNTIME_TYPES.NUMBER} (${
|
380
502
|
KEYWORDS.GREATHER_THAN_OR_EQUAL
|
381
503
|
} ${stringifyArgs(args)})`
|
382
504
|
)
|
@@ -397,7 +519,7 @@ export const keywords = {
|
|
397
519
|
throw new TypeError(
|
398
520
|
`Invalid use of (${
|
399
521
|
KEYWORDS.LESS_THAN_OR_EQUAL
|
400
|
-
}), first argument is not an ${
|
522
|
+
}), first argument is not an ${RUNTIME_TYPES.NUMBER} (${
|
401
523
|
KEYWORDS.LESS_THAN_OR_EQUAL
|
402
524
|
} ${stringifyArgs(args)})`
|
403
525
|
)
|
@@ -405,7 +527,7 @@ export const keywords = {
|
|
405
527
|
throw new TypeError(
|
406
528
|
`Invalid use of (${
|
407
529
|
KEYWORDS.LESS_THAN_OR_EQUAL
|
408
|
-
}), second argument are not an ${
|
530
|
+
}), second argument are not an ${RUNTIME_TYPES.NUMBER} (${
|
409
531
|
KEYWORDS.LESS_THAN_OR_EQUAL
|
410
532
|
} ${stringifyArgs(args)})`
|
411
533
|
)
|
@@ -459,32 +581,6 @@ export const keywords = {
|
|
459
581
|
)
|
460
582
|
return b
|
461
583
|
},
|
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
584
|
[KEYWORDS.DEFINE_VARIABLE]: (args, env) => {
|
489
585
|
if (args.length !== 2)
|
490
586
|
throw new RangeError(
|
@@ -513,171 +609,101 @@ export const keywords = {
|
|
513
609
|
})
|
514
610
|
return env[name]
|
515
611
|
},
|
516
|
-
[KEYWORDS.
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
612
|
+
[KEYWORDS.ANONYMOUS_FUNCTION]: (args, env) => {
|
613
|
+
const params = args.slice(0, -1)
|
614
|
+
const body = args.at(-1)
|
615
|
+
return (props = [], scope) => {
|
616
|
+
if (props.length !== params.length)
|
617
|
+
throw new RangeError(
|
618
|
+
`Incorrect number of arguments for (${
|
619
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
620
|
+
} ${stringifyArgs(params)}) are provided. (expects ${
|
621
|
+
params.length
|
622
|
+
} but got ${props.length}) (${
|
623
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
624
|
+
} ${stringifyArgs(args)})`
|
625
|
+
)
|
626
|
+
const localEnv = Object.create(env)
|
627
|
+
// localEnv[KEYWORDS.BLOCK] = block[KEYWORDS.BLOCK]
|
628
|
+
for (let i = 0; i < props.length; ++i) {
|
629
|
+
const value = evaluate(props[i], scope)
|
630
|
+
Object.defineProperty(localEnv, params[i][VALUE], {
|
631
|
+
value,
|
632
|
+
writable: true
|
633
|
+
})
|
634
|
+
}
|
635
|
+
return evaluate(body, localEnv)
|
636
|
+
}
|
531
637
|
},
|
532
|
-
[KEYWORDS.
|
533
|
-
if (args.length
|
638
|
+
[KEYWORDS.CALL_FUNCTION]: (args, env) => {
|
639
|
+
if (!args.length)
|
534
640
|
throw new RangeError(
|
535
641
|
`Invalid number of arguments to (${
|
536
|
-
KEYWORDS.
|
537
|
-
}) (
|
642
|
+
KEYWORDS.CALL_FUNCTION
|
643
|
+
}) (>= 1 required) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
538
644
|
)
|
539
|
-
const
|
540
|
-
if (
|
645
|
+
const [first, ...rest] = args
|
646
|
+
if (first[TYPE] === WORD && first[VALUE] in keywords)
|
541
647
|
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)})`
|
648
|
+
`Following argument of (${
|
649
|
+
KEYWORDS.CALL_FUNCTION
|
650
|
+
}) must not be an reserved word (${
|
651
|
+
KEYWORDS.CALL_FUNCTION
|
652
|
+
} ${stringifyArgs(args)})`
|
554
653
|
)
|
555
|
-
const
|
556
|
-
|
557
|
-
if (typeof a !== 'number' || typeof b !== 'number')
|
654
|
+
const apply = evaluate(first, env)
|
655
|
+
if (typeof apply !== 'function')
|
558
656
|
throw new TypeError(
|
559
|
-
`
|
560
|
-
KEYWORDS.
|
561
|
-
}) (${KEYWORDS.
|
657
|
+
`First argument of (${KEYWORDS.CALL_FUNCTION}) must be a (${
|
658
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
659
|
+
}) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
|
562
660
|
)
|
563
|
-
|
661
|
+
|
662
|
+
return apply(rest, env)
|
564
663
|
},
|
565
|
-
[KEYWORDS.
|
566
|
-
if (args.length
|
664
|
+
[KEYWORDS.BLOCK]: (args, env) => {
|
665
|
+
if (!args.length)
|
567
666
|
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)})`
|
667
|
+
`Invalid number of arguments to (${KEYWORDS.BLOCK}) (>= 1 required) (${
|
668
|
+
KEYWORDS.BLOCK
|
669
|
+
} ${stringifyArgs(args)})`
|
579
670
|
)
|
580
|
-
return
|
671
|
+
return args.reduce((_, x) => evaluate(x, env), 0)
|
581
672
|
},
|
582
|
-
[KEYWORDS.
|
583
|
-
if (args.length !==
|
673
|
+
[KEYWORDS.IS_ATOM]: (args, env) => {
|
674
|
+
if (args.length !== 1)
|
584
675
|
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)})`
|
676
|
+
`Invalid number of arguments for (${
|
677
|
+
KEYWORDS.IS_ATOM
|
678
|
+
}) (= 1 required) (${KEYWORDS.IS_ATOM} ${stringifyArgs(args)})`
|
598
679
|
)
|
599
|
-
return
|
680
|
+
return +(typeof evaluate(args[0], env) === 'number')
|
600
681
|
},
|
601
|
-
[KEYWORDS.
|
602
|
-
if (args.length !==
|
682
|
+
[KEYWORDS.IS_LAMBDA]: (args, env) => {
|
683
|
+
if (args.length !== 1)
|
603
684
|
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)})`
|
685
|
+
`Invalid number of arguments for (${
|
686
|
+
KEYWORDS.IS_LAMBDA
|
687
|
+
}) (= 1 required) (${KEYWORDS.IS_LAMBDA} ${stringifyArgs(args)})`
|
617
688
|
)
|
618
|
-
return
|
689
|
+
return +(typeof evaluate(args[0], env) === 'function')
|
619
690
|
},
|
620
|
-
|
621
|
-
|
691
|
+
// Not sure about these
|
692
|
+
[KEYWORDS.THROW]: (args, env) => {
|
693
|
+
if (args.length !== 1)
|
622
694
|
throw new RangeError(
|
623
|
-
`Invalid number of arguments to (${
|
624
|
-
KEYWORDS.
|
625
|
-
}) (= 2 required). (${
|
626
|
-
KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT
|
695
|
+
`Invalid number of arguments to (${KEYWORDS.THROW}) (= 1 required) (${
|
696
|
+
KEYWORDS.THROW
|
627
697
|
} ${stringifyArgs(args)})`
|
628
698
|
)
|
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))
|
699
|
+
const expression = evaluate(args[0], env)
|
700
|
+
if (!Array.isArray(expression))
|
648
701
|
throw new TypeError(
|
649
|
-
`
|
650
|
-
|
651
|
-
}) but got (${
|
702
|
+
`Argument of (${KEYWORDS.THROW}) must be an (${
|
703
|
+
RUNTIME_TYPES.ARRAY
|
704
|
+
}) but got (${expression}) (${KEYWORDS.THROW} ${stringifyArgs(args)})`
|
652
705
|
)
|
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
|
706
|
+
throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
|
681
707
|
},
|
682
708
|
[KEYWORDS.LOG]: (args, env) => {
|
683
709
|
if (args.length !== 1)
|
@@ -701,7 +727,7 @@ export const keywords = {
|
|
701
727
|
if (!Array.isArray(expression))
|
702
728
|
throw new TypeError(
|
703
729
|
`Argument of (${KEYWORDS.LOG_STRING}) must be an (${
|
704
|
-
|
730
|
+
RUNTIME_TYPES.ARRAY
|
705
731
|
}) but got (${expression}) (${KEYWORDS.LOG_STRING} ${stringifyArgs(
|
706
732
|
args
|
707
733
|
)})`
|
@@ -720,7 +746,7 @@ export const keywords = {
|
|
720
746
|
if (typeof expression !== 'number')
|
721
747
|
throw new TypeError(
|
722
748
|
`Argument of (${KEYWORDS.LOG_CHAR}) must be a (${
|
723
|
-
|
749
|
+
RUNTIME_TYPES.NUMBER
|
724
750
|
}) but got (${expression}) (${KEYWORDS.LOG_CHAR} ${stringifyArgs(
|
725
751
|
args
|
726
752
|
)})`
|
@@ -737,23 +763,5 @@ export const keywords = {
|
|
737
763
|
)
|
738
764
|
console.clear()
|
739
765
|
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
766
|
}
|
759
767
|
}
|
package/src/keywords.js
CHANGED
@@ -7,8 +7,7 @@ export const TRUE = 1
|
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
9
|
export const KEYWORDS = {
|
10
|
-
|
11
|
-
ARRAY_TYPE: 'array',
|
10
|
+
CREATE_ARRAY: 'array',
|
12
11
|
IDENTITY: 'identity',
|
13
12
|
ARRAY_LENGTH: 'length',
|
14
13
|
IS_ATOM: 'atom?',
|
@@ -56,3 +55,7 @@ export const TYPES = {
|
|
56
55
|
[WORD]: 'WORD',
|
57
56
|
[ATOM]: 'ATOM'
|
58
57
|
}
|
58
|
+
export const RUNTIME_TYPES = {
|
59
|
+
NUMBER: 'number',
|
60
|
+
ARRAY: 'array'
|
61
|
+
}
|
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`)
|
@@ -148,10 +146,10 @@ export const deSuggarAst = (ast) => {
|
|
148
146
|
exp.length = 0
|
149
147
|
let temp = exp
|
150
148
|
for (const item of rest) {
|
151
|
-
temp.push([APPLY, KEYWORDS.
|
149
|
+
temp.push([APPLY, KEYWORDS.CREATE_ARRAY], item, [])
|
152
150
|
temp = temp.at(-1)
|
153
151
|
}
|
154
|
-
temp.push([APPLY, KEYWORDS.
|
152
|
+
temp.push([APPLY, KEYWORDS.CREATE_ARRAY])
|
155
153
|
}
|
156
154
|
deSuggarAst(exp)
|
157
155
|
break
|
@@ -388,7 +386,7 @@ export const replaceStrings = (source) => {
|
|
388
386
|
for (const q of quotes)
|
389
387
|
source = source.replaceAll(
|
390
388
|
q,
|
391
|
-
`(${KEYWORDS.
|
389
|
+
`(${KEYWORDS.CREATE_ARRAY} ${[...q.replaceAll('\r', '')]
|
392
390
|
.slice(1, -1)
|
393
391
|
.map((x) => x.charCodeAt(0))
|
394
392
|
.join(' ')})`
|
@@ -397,9 +395,9 @@ export const replaceStrings = (source) => {
|
|
397
395
|
}
|
398
396
|
export const replaceQuotes = (source) =>
|
399
397
|
source
|
400
|
-
.replaceAll(/\'\(/g, `(${KEYWORDS.
|
398
|
+
.replaceAll(/\'\(/g, `(${KEYWORDS.CREATE_ARRAY} `)
|
401
399
|
.replaceAll(/\`\(/g, `(${SUGGAR.LIST_TYPE} `)
|
402
|
-
.replaceAll(/\(\)/g, `(${KEYWORDS.
|
400
|
+
.replaceAll(/\(\)/g, `(${KEYWORDS.CREATE_ARRAY})`)
|
403
401
|
export const deSuggarSource = (source) => replaceQuotes(replaceStrings(source))
|
404
402
|
export const handleUnbalancedQuotes = (source) => {
|
405
403
|
const diff = (source.match(/\"/g) ?? []).length % 2
|
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')
|
@@ -59,7 +58,7 @@ export const stringifyType = (type) => {
|
|
59
58
|
if (!isLeaf(type)) {
|
60
59
|
const [car] = type
|
61
60
|
if (car == undefined) return '(array)'
|
62
|
-
else if (car[TYPE] === APPLY && car[VALUE] === KEYWORDS.
|
61
|
+
else if (car[TYPE] === APPLY && car[VALUE] === KEYWORDS.CREATE_ARRAY)
|
63
62
|
return `(array ${type
|
64
63
|
.map((t) => stringifyType(t))
|
65
64
|
.join(' ')
|
@@ -84,15 +83,18 @@ export const stringifyArgs = (args) =>
|
|
84
83
|
.replace(new RegExp(/"/g), '')
|
85
84
|
)
|
86
85
|
.join(' ')
|
86
|
+
const KEYWORDS_SET = Object.values(KEYWORDS).reduce((a, b) => {
|
87
|
+
a.add(b)
|
88
|
+
return a
|
89
|
+
}, new Set())
|
87
90
|
export const isForbiddenVariableName = (name) => {
|
88
91
|
switch (name) {
|
89
92
|
case '_':
|
90
|
-
case
|
91
|
-
case
|
92
|
-
case SUGGAR.CACHE:
|
93
|
+
case OPTIMIZATIONS.RECURSION:
|
94
|
+
case OPTIMIZATIONS.CACHE:
|
93
95
|
return true
|
94
96
|
default:
|
95
|
-
return !isNaN(name[0])
|
97
|
+
return KEYWORDS_SET.has(name) || !isNaN(name[0])
|
96
98
|
}
|
97
99
|
}
|
98
100
|
export const isEqual = (a, b) =>
|