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 CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "fez-lisp",
3
3
  "description": "Lisp interpreted & compiled to JavaScript",
4
4
  "author": "AT290690",
5
- "version": "1.3.2",
5
+ "version": "1.3.3",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
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 === SUGGAR.RECURSION) {
182
+ if (prefix === OPTIMIZATIONS.RECURSION) {
180
183
  const name = lispToJavaScriptVariableName(n)
181
- const newName = `${SUGGAR.RECURSION}_${performance
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 === SUGGAR.CACHE) {
205
+ } else if (prefix === OPTIMIZATIONS.CACHE) {
203
206
  // memoization here
204
207
  const name = lispToJavaScriptVariableName(n)
205
- const newName = name.substring(SUGGAR.CACHE.length + 1)
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()
@@ -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.REMAINDER_OF_DIVISION]: (args, env) => {
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.REMAINDER_OF_DIVISION
10
- }), expected (= 2) but got ${args.length}. (${
11
- KEYWORDS.REMAINDER_OF_DIVISION
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 argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
17
+ `First arguments of (${KEYWORDS.ADDITION}) is not a (${
18
18
  KEYWORDS.NUMBER_TYPE
19
- }) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
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 argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
24
+ `Second arguments of (${KEYWORDS.ADDITION}) is not a (${
25
25
  KEYWORDS.NUMBER_TYPE
26
- }) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
26
+ }) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
27
27
  )
28
- if (b === 0)
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
- `Second argument of (${
31
- KEYWORDS.REMAINDER_OF_DIVISION
32
- }) can't be a (0) (division by 0 is not allowed) (${
33
- KEYWORDS.REMAINDER_OF_DIVISION
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
- return a % b
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.ARRAY_LENGTH]: (args, env) => {
69
- if (args.length !== 1)
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.ARRAY_LENGTH
73
- }) (= 1 required) (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
110
+ KEYWORDS.REMAINDER_OF_DIVISION
111
+ }), expected (= 2) but got ${args.length}. (${
112
+ KEYWORDS.REMAINDER_OF_DIVISION
113
+ } ${stringifyArgs(args)})`
74
114
  )
75
- const array = evaluate(args[0], env)
76
- if (!Array.isArray(array))
115
+ const a = evaluate(args[0], env)
116
+ if (typeof a !== 'number')
77
117
  throw new TypeError(
78
- `First argument of (${KEYWORDS.ARRAY_LENGTH}) must be an ${
79
- KEYWORDS.ARRAY_TYPE
80
- } (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
118
+ `First argument of (${KEYWORDS.REMAINDER_OF_DIVISION}) is not (${
119
+ KEYWORDS.NUMBER_TYPE
120
+ }) (${KEYWORDS.REMAINDER_OF_DIVISION} ${stringifyArgs(args)})`
81
121
  )
82
- return array.length
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.IS_ATOM]: (args, env) => {
85
- if (args.length !== 1)
140
+ [KEYWORDS.BITWISE_AND]: (args, env) => {
141
+ if (args.length < 2)
86
142
  throw new RangeError(
87
- `Invalid number of arguments for (${
88
- KEYWORDS.IS_ATOM
89
- }) (= 1 required) (${KEYWORDS.IS_ATOM} ${stringifyArgs(args)})`
143
+ `Invalid number of arguments to (${
144
+ KEYWORDS.BITWISE_AND
145
+ }) (= 2 required). (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
90
146
  )
91
- return +(typeof evaluate(args[0], env) === 'number')
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.IS_LAMBDA]: (args, env) => {
156
+ [KEYWORDS.BITWISE_NOT]: (args, env) => {
94
157
  if (args.length !== 1)
95
158
  throw new RangeError(
96
- `Invalid number of arguments for (${
97
- KEYWORDS.IS_LAMBDA
98
- }) (= 1 required) (${KEYWORDS.IS_LAMBDA} ${stringifyArgs(args)})`
159
+ `Invalid number of arguments to (${
160
+ KEYWORDS.BITWISE_NOT
161
+ }) (= 1 required). (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
99
162
  )
100
- return +(typeof evaluate(args[0], env) === 'function')
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.ADDITION]: (args, env) => {
103
- if (args.length !== 0 && args.length !== 2)
172
+ [KEYWORDS.BITWISE_OR]: (args, env) => {
173
+ if (args.length !== 2)
104
174
  throw new RangeError(
105
- `Invalid number of arguments for (${
106
- KEYWORDS.ADDITION
107
- }), expected (or (= 2) (= 0)) but got ${args.length}. (${
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
- `Second arguments of (${KEYWORDS.ADDITION}) is not a (${
183
+ `Not all arguments of (${KEYWORDS.BITWISE_OR}) are (${
122
184
  KEYWORDS.NUMBER_TYPE
123
- }) (${KEYWORDS.ADDITION} ${stringifyArgs(args)})`
185
+ }) (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
124
186
  )
125
- return a + b
187
+ return a | b
126
188
  },
127
- [KEYWORDS.MULTIPLICATION]: (args, env) => {
189
+ [KEYWORDS.BITWISE_XOR]: (args, env) => {
128
190
  if (args.length !== 2)
129
191
  throw new RangeError(
130
- `Invalid number of arguments for (${KEYWORDS.MULTIPLICATION}), expected (= 2) but got ${args.length}.`
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
- `Second arguments of (${KEYWORDS.MULTIPLICATION}) is not a (${
200
+ `Not all arguments of (${KEYWORDS.BITWISE_XOR}) are (${
143
201
  KEYWORDS.NUMBER_TYPE
144
- }) (${KEYWORDS.MULTIPLICATION} ${stringifyArgs(args)})`
202
+ }) (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
145
203
  )
146
- return a * b
204
+ return a ^ b
147
205
  },
148
- [KEYWORDS.SUBTRACTION]: (args, env) => {
149
- if (args.length !== 1 && args.length !== 2)
206
+ [KEYWORDS.BITWISE_LEFT_SHIFT]: (args, env) => {
207
+ if (args.length !== 2)
150
208
  throw new RangeError(
151
- `Invalid number of arguments for (${
152
- KEYWORDS.SUBTRACTION
153
- }), expected (or (= 1)) (= 2) but got ${args.length}. (${
154
- KEYWORDS.SUBTRACTION
155
- } ${stringifyArgs(args)})`
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
- if (typeof a !== 'number')
216
+ const b = evaluate(args[1], env)
217
+ if (typeof a !== 'number' || typeof b !== 'number')
159
218
  throw new TypeError(
160
- `First argument of (${KEYWORDS.SUBTRACTION}) is not a (${
219
+ `Not all arguments of (${KEYWORDS.BITWISE_LEFT_SHIFT}) are (${
161
220
  KEYWORDS.NUMBER_TYPE
162
- }) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
221
+ }) (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(args)})`
163
222
  )
164
- if (args.length === 1) return -a
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
- `Second argument of (${KEYWORDS.SUBTRACTION}) is not a (${
238
+ `Not all arguments of (${KEYWORDS.BITWISE_RIGHT_SHIFT}) are (${
169
239
  KEYWORDS.NUMBER_TYPE
170
- }) (${KEYWORDS.SUBTRACTION} ${stringifyArgs(args)})`
240
+ }) (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(args)})`
171
241
  )
172
- return a - b
242
+ return a >> b
173
243
  },
174
- [KEYWORDS.IF]: (args, env) => {
175
- if (args.length > 3 || args.length < 2)
244
+ [KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT]: (args, env) => {
245
+ if (args.length !== 2)
176
246
  throw new RangeError(
177
- `Invalid number of arguments for (${
178
- KEYWORDS.IF
179
- }), expected (or (= 3) (= 2)) but got ${args.length} (${
180
- KEYWORDS.IF
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 condition = evaluate(args[0], env)
184
- if (condition !== FALSE && condition !== TRUE)
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
- `Condition of (${KEYWORDS.IF}) must be ${TRUE} or ${FALSE} but got (${
187
- KEYWORDS.IF
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 condition
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.BLOCK]: (args, env) => {
242
- if (!args.length)
308
+ [KEYWORDS.SET_ARRAY]: (args, env) => {
309
+ if (args.length !== 1 && args.length !== 3)
243
310
  throw new RangeError(
244
- `Invalid number of arguments to (${KEYWORDS.BLOCK}) (>= 1 required) (${
245
- KEYWORDS.BLOCK
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
- return args.reduce((_, x) => evaluate(x, env), 0)
249
- },
250
- [KEYWORDS.ANONYMOUS_FUNCTION]: (args, env) => {
251
- const params = args.slice(0, -1)
252
- const body = args.at(-1)
253
- return (props = [], scope) => {
254
- if (props.length !== params.length)
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
- `Incorrect number of arguments for (${
257
- KEYWORDS.ANONYMOUS_FUNCTION
258
- } ${stringifyArgs(params)}) are provided. (expects ${
259
- params.length
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 localEnv = Object.create(env)
265
- // localEnv[KEYWORDS.BLOCK] = block[KEYWORDS.BLOCK]
266
- for (let i = 0; i < props.length; ++i) {
267
- const value = evaluate(props[i], scope)
268
- Object.defineProperty(localEnv, params[i][VALUE], {
269
- value,
270
- writable: true
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.BITWISE_AND]: (args, env) => {
517
- if (args.length < 2)
518
- throw new RangeError(
519
- `Invalid number of arguments to (${
520
- KEYWORDS.BITWISE_AND
521
- }) (= 2 required). (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
522
- )
523
- const operands = args.map((a) => evaluate(a, env))
524
- if (operands.some((x) => typeof x !== 'number'))
525
- throw new TypeError(
526
- `Not all arguments of (${KEYWORDS.BITWISE_AND}) are ${
527
- KEYWORDS.NUMBER_TYPE
528
- } (${KEYWORDS.BITWISE_AND} ${stringifyArgs(args)})`
529
- )
530
- return operands.reduce((acc, x) => acc & x)
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.BITWISE_NOT]: (args, env) => {
533
- if (args.length !== 1)
629
+ [KEYWORDS.CALL_FUNCTION]: (args, env) => {
630
+ if (!args.length)
534
631
  throw new RangeError(
535
632
  `Invalid number of arguments to (${
536
- KEYWORDS.BITWISE_NOT
537
- }) (= 1 required). (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
633
+ KEYWORDS.CALL_FUNCTION
634
+ }) (>= 1 required) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
538
635
  )
539
- const operand = evaluate(args[0], env)
540
- if (typeof operand !== 'number')
636
+ const [first, ...rest] = args
637
+ if (first[TYPE] === WORD && first[VALUE] in keywords)
541
638
  throw new TypeError(
542
- `Argument of (${KEYWORDS.BITWISE_NOT}) is not a (${
543
- KEYWORDS.NUMBER_TYPE
544
- }) (${KEYWORDS.BITWISE_NOT} ${stringifyArgs(args)})`
545
- )
546
- return ~operand
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 a = evaluate(args[0], env)
556
- const b = evaluate(args[1], env)
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
- `Not all arguments of (${KEYWORDS.BITWISE_OR}) are (${
560
- KEYWORDS.NUMBER_TYPE
561
- }) (${KEYWORDS.BITWISE_OR} ${stringifyArgs(args)})`
648
+ `First argument of (${KEYWORDS.CALL_FUNCTION}) must be a (${
649
+ KEYWORDS.ANONYMOUS_FUNCTION
650
+ }) (${KEYWORDS.CALL_FUNCTION} ${stringifyArgs(args)})`
562
651
  )
563
- return a | b
652
+
653
+ return apply(rest, env)
564
654
  },
565
- [KEYWORDS.BITWISE_XOR]: (args, env) => {
566
- if (args.length !== 2)
655
+ [KEYWORDS.BLOCK]: (args, env) => {
656
+ if (!args.length)
567
657
  throw new RangeError(
568
- `Invalid number of arguments to (${
569
- KEYWORDS.BITWISE_XOR
570
- }) (= 2 required). (${KEYWORDS.BITWISE_XOR} ${stringifyArgs(args)})`
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 a ^ b
662
+ return args.reduce((_, x) => evaluate(x, env), 0)
581
663
  },
582
- [KEYWORDS.BITWISE_LEFT_SHIFT]: (args, env) => {
583
- if (args.length !== 2)
664
+ [KEYWORDS.IS_ATOM]: (args, env) => {
665
+ if (args.length !== 1)
584
666
  throw new RangeError(
585
- `Invalid number of arguments to (${
586
- KEYWORDS.BITWISE_LEFT_SHIFT
587
- }) (= 2 required). (${KEYWORDS.BITWISE_LEFT_SHIFT} ${stringifyArgs(
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 a << b
671
+ return +(typeof evaluate(args[0], env) === 'number')
600
672
  },
601
- [KEYWORDS.BITWISE_RIGHT_SHIFT]: (args, env) => {
602
- if (args.length !== 2)
673
+ [KEYWORDS.IS_LAMBDA]: (args, env) => {
674
+ if (args.length !== 1)
603
675
  throw new RangeError(
604
- `Invalid number of arguments to (${
605
- KEYWORDS.BITWISE_RIGHT_SHIFT
606
- }) (= 2 required). (${KEYWORDS.BITWISE_RIGHT_SHIFT} ${stringifyArgs(
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 a >> b
680
+ return +(typeof evaluate(args[0], env) === 'function')
619
681
  },
620
- [KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT]: (args, env) => {
621
- if (args.length !== 2)
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.BITWISE_UNSIGNED_RIGHT_SHIFT
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 a = evaluate(args[0], env)
630
- const b = evaluate(args[1], env)
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
- `First argument of (${KEYWORDS.SET_ARRAY}) must be an (${
693
+ `Argument of (${KEYWORDS.THROW}) must be an (${
650
694
  KEYWORDS.ARRAY_TYPE
651
- }) but got (${array}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
695
+ }) but got (${expression}) (${KEYWORDS.THROW} ${stringifyArgs(args)})`
652
696
  )
653
- if (args.length === 1) {
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 SUGGAR.RECURSION:
92
- case SUGGAR.CACHE:
90
+ case OPTIMIZATIONS.RECURSION:
91
+ case OPTIMIZATIONS.CACHE:
93
92
  return true
94
93
  default:
95
94
  return !isNaN(name[0])