fez-lisp 1.4.4 → 1.4.6
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/README.md +1 -1
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/evaluator.js +4 -2
- package/src/interpreter.js +9 -3
- package/src/keywords.js +4 -2
- package/src/utils.js +140 -144
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
@@ -2,6 +2,7 @@ import { keywords } from './interpreter.js'
|
|
2
2
|
import {
|
3
3
|
APPLY,
|
4
4
|
ATOM,
|
5
|
+
DEBUG,
|
5
6
|
KEYWORDS,
|
6
7
|
SPECIAL_FORMS_SET,
|
7
8
|
TYPE,
|
@@ -9,7 +10,7 @@ import {
|
|
9
10
|
WORD
|
10
11
|
} from './keywords.js'
|
11
12
|
import { isLeaf } from './parser.js'
|
12
|
-
import {
|
13
|
+
import { stringifyArgs } from './utils.js'
|
13
14
|
export const evaluate = (exp, env = keywords) => {
|
14
15
|
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
15
16
|
if (first == undefined) return []
|
@@ -33,7 +34,8 @@ export const evaluate = (exp, env = keywords) => {
|
|
33
34
|
)
|
34
35
|
const isSpecial = SPECIAL_FORMS_SET.has(value)
|
35
36
|
const result = apply(rest, env, value)
|
36
|
-
if (!isSpecial
|
37
|
+
if (!isSpecial && Array.isArray(env[DEBUG.CALLSTACK]))
|
38
|
+
env[DEBUG.CALLSTACK].pop()
|
37
39
|
return result
|
38
40
|
}
|
39
41
|
case ATOM:
|
package/src/interpreter.js
CHANGED
@@ -6,10 +6,11 @@ import {
|
|
6
6
|
FALSE,
|
7
7
|
TRUE,
|
8
8
|
TYPES,
|
9
|
-
RUNTIME_TYPES
|
9
|
+
RUNTIME_TYPES,
|
10
|
+
DEBUG
|
10
11
|
} from './keywords.js'
|
11
12
|
import { evaluate } from './evaluator.js'
|
12
|
-
import {
|
13
|
+
import { isForbiddenVariableName, stringifyArgs } from './utils.js'
|
13
14
|
import { LISP } from './parser.js'
|
14
15
|
export const keywords = {
|
15
16
|
[KEYWORDS.ADDITION]: (args, env) => {
|
@@ -773,7 +774,12 @@ export const keywords = {
|
|
773
774
|
writable: true
|
774
775
|
})
|
775
776
|
}
|
776
|
-
if (
|
777
|
+
if (
|
778
|
+
name &&
|
779
|
+
Array.isArray(env[DEBUG.CALLSTACK]) &&
|
780
|
+
name !== env[DEBUG.CALLSTACK].at(-1)
|
781
|
+
)
|
782
|
+
env[DEBUG.CALLSTACK].push(name)
|
777
783
|
return evaluate(body, localEnv)
|
778
784
|
}
|
779
785
|
},
|
package/src/keywords.js
CHANGED
@@ -40,7 +40,7 @@ export const KEYWORDS = {
|
|
40
40
|
DEFINE_VARIABLE: 'let',
|
41
41
|
|
42
42
|
SET_ARRAY: 'set!',
|
43
|
-
ERROR: 'throw'
|
43
|
+
ERROR: 'throw',
|
44
44
|
}
|
45
45
|
|
46
46
|
export const TYPES = {
|
@@ -54,7 +54,9 @@ export const RUNTIME_TYPES = {
|
|
54
54
|
}
|
55
55
|
export const DEBUG = {
|
56
56
|
LOG: 'log',
|
57
|
-
ASSERT: 'assert'
|
57
|
+
ASSERT: 'assert',
|
58
|
+
CALLSTACK: '(CALLSTACK)' // so that you can't use it in the code
|
59
|
+
|
58
60
|
}
|
59
61
|
|
60
62
|
export const SPECIAL_FORMS_SET = new Set(Object.values(KEYWORDS))
|
package/src/utils.js
CHANGED
@@ -232,39 +232,6 @@ export const wrapInBlock = (ast) => [
|
|
232
232
|
]
|
233
233
|
export const interpret = (ast, keywords) =>
|
234
234
|
ast.reduce((_, x) => evaluate(x, keywords), 0)
|
235
|
-
export const fez = (source, options = {}) => {
|
236
|
-
const env = { ...keywords }
|
237
|
-
try {
|
238
|
-
if (typeof source === 'string') {
|
239
|
-
source = deSuggarSource(source)
|
240
|
-
const valid = handleUnbalancedQuotes(
|
241
|
-
handleUnbalancedParens(removeNoCode(source))
|
242
|
-
)
|
243
|
-
const code = !options.mutation ? removeMutation(valid) : valid
|
244
|
-
if (!code.length && options.throw) throw new Error('Nothing to parse!')
|
245
|
-
const parsed = LISP.parse(code)
|
246
|
-
const scope = deSuggarAst(parsed)
|
247
|
-
const ast = wrapInBlock(shake(scope, std))
|
248
|
-
// if (options.check) typeCheck(ast)
|
249
|
-
if (options.compile) return compile(ast)
|
250
|
-
return evaluate(ast, env)
|
251
|
-
} else if (Array.isArray(source)) {
|
252
|
-
const ast = !options.mutation
|
253
|
-
? AST.parse(AST.stringify(source).replace(new RegExp(/!/g), 'ǃ'))
|
254
|
-
: source
|
255
|
-
if (options.compile) return compile(ast)
|
256
|
-
return evaluate(ast, env)
|
257
|
-
} else {
|
258
|
-
throw new Error('Source has to be either a lisp source code or an AST')
|
259
|
-
}
|
260
|
-
} catch (error) {
|
261
|
-
const err = error.message.replace("'[object Array]'", '(array)')
|
262
|
-
// .replace('object', '(array)')
|
263
|
-
logError(err)
|
264
|
-
if (options.throw) throw err
|
265
|
-
return err
|
266
|
-
}
|
267
|
-
}
|
268
235
|
export const shake = (parsed, std) => treeShake(parsed, std).concat(parsed)
|
269
236
|
export const tree = (source, std) =>
|
270
237
|
std
|
@@ -317,133 +284,162 @@ const identity = (name) => [
|
|
317
284
|
[1, 'x']
|
318
285
|
]
|
319
286
|
]
|
320
|
-
export const
|
287
|
+
// export const result = (ast) => {
|
288
|
+
// try {
|
289
|
+
// return { output: evaluate(ast, keywords), error: null }
|
290
|
+
// } catch (error) {
|
291
|
+
// const isMaxCallStack =
|
292
|
+
// error.message.includes('Maximum call stack size exceeded') ||
|
293
|
+
// error.message.includes('too much recursion')
|
294
|
+
// return {
|
295
|
+
// output: null,
|
296
|
+
// error: {
|
297
|
+
// stack: [...callStack],
|
298
|
+
// message: isMaxCallStack
|
299
|
+
// ? error
|
300
|
+
// : `${error}\n${callStack
|
301
|
+
// .reverse()
|
302
|
+
// .map((x, i) => `${Array(i + 2).join(' ')}(${x} ...)`)
|
303
|
+
// .join('\n')}`
|
304
|
+
// }
|
305
|
+
// }
|
306
|
+
// }
|
307
|
+
// }
|
321
308
|
export const debug = (ast) => {
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
309
|
+
const debugEnv = {
|
310
|
+
...keywords,
|
311
|
+
[DEBUG.CALLSTACK]: [KEYWORDS.BLOCK],
|
312
|
+
[DEBUG.LOG]: (args, env) => {
|
313
|
+
if (args.length !== 1 && args.length !== 2)
|
314
|
+
throw new RangeError(
|
315
|
+
`Invalid number of arguments to (${DEBUG.LOG}) (or (= 1) (= 2)) (${
|
316
|
+
DEBUG.LOG
|
317
|
+
} ${stringifyArgs(args)})`
|
318
|
+
)
|
319
|
+
const expression = evaluate(args[0], env)
|
320
|
+
if (args.length === 2) {
|
321
|
+
const option = evaluate(args[1], env)
|
322
|
+
if (!Array.isArray(option)) {
|
323
|
+
throw new TypeError(
|
324
|
+
`Second argument of (${DEBUG.LOG}) must be an (${
|
325
|
+
RUNTIME_TYPES.ARRAY
|
326
|
+
}) but got (${expression}) (${DEBUG.LOG} ${stringifyArgs(args)})`
|
333
327
|
)
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
case 'str':
|
348
|
-
{
|
349
|
-
if (!Array.isArray(expression))
|
350
|
-
throw new TypeError(
|
351
|
-
`Argument of (${DEBUG.LOG}) must be an (${
|
352
|
-
RUNTIME_TYPES.ARRAY
|
353
|
-
}) in the case ${type} but got (${expression}) (${
|
354
|
-
DEBUG.LOG
|
355
|
-
} ${stringifyArgs(args)})`
|
356
|
-
)
|
357
|
-
console.log(
|
358
|
-
expression.map((x) => String.fromCharCode(x)).join('')
|
328
|
+
}
|
329
|
+
const type = option.map((x) => String.fromCharCode(x)).join('')
|
330
|
+
switch (type) {
|
331
|
+
case 'string':
|
332
|
+
case 'str':
|
333
|
+
{
|
334
|
+
if (!Array.isArray(expression))
|
335
|
+
throw new TypeError(
|
336
|
+
`Argument of (${DEBUG.LOG}) must be an (${
|
337
|
+
RUNTIME_TYPES.ARRAY
|
338
|
+
}) in the case ${type} but got (${expression}) (${
|
339
|
+
DEBUG.LOG
|
340
|
+
} ${stringifyArgs(args)})`
|
359
341
|
)
|
360
|
-
|
361
|
-
|
362
|
-
case 'char':
|
363
|
-
case 'ch':
|
364
|
-
{
|
365
|
-
if (typeof expression !== 'number')
|
366
|
-
throw new TypeError(
|
367
|
-
`Argument argument of (${DEBUG.LOG}) must be a (${
|
368
|
-
RUNTIME_TYPES.NUMBER
|
369
|
-
}) in the case ${type} but got (${expression}) (${
|
370
|
-
DEBUG.LOG
|
371
|
-
} ${stringifyArgs(args)})`
|
372
|
-
)
|
373
|
-
console.log(String.fromCharCode(expression))
|
374
|
-
}
|
375
|
-
|
376
|
-
break
|
377
|
-
case '*':
|
378
|
-
console.log(expression)
|
379
|
-
break
|
380
|
-
default:
|
381
|
-
throw new TypeError(
|
382
|
-
`Invalid number of option to (${
|
383
|
-
DEBUG.LOG
|
384
|
-
}) got ${option} ${stringifyArgs(args)})`
|
342
|
+
console.log(
|
343
|
+
expression.map((x) => String.fromCharCode(x)).join('')
|
385
344
|
)
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
345
|
+
}
|
346
|
+
break
|
347
|
+
case 'char':
|
348
|
+
case 'ch':
|
349
|
+
{
|
350
|
+
if (typeof expression !== 'number')
|
351
|
+
throw new TypeError(
|
352
|
+
`Argument argument of (${DEBUG.LOG}) must be a (${
|
353
|
+
RUNTIME_TYPES.NUMBER
|
354
|
+
}) in the case ${type} but got (${expression}) (${
|
355
|
+
DEBUG.LOG
|
356
|
+
} ${stringifyArgs(args)})`
|
357
|
+
)
|
358
|
+
console.log(String.fromCharCode(expression))
|
359
|
+
}
|
360
|
+
|
361
|
+
break
|
362
|
+
case '*':
|
363
|
+
console.log(expression)
|
364
|
+
break
|
365
|
+
default:
|
366
|
+
throw new TypeError(
|
367
|
+
`Invalid number of option to (${
|
368
|
+
DEBUG.LOG
|
369
|
+
}) got ${option} ${stringifyArgs(args)})`
|
370
|
+
)
|
371
|
+
}
|
372
|
+
} else console.log(expression)
|
373
|
+
return expression
|
374
|
+
},
|
375
|
+
[DEBUG.ASSERT]: (args, env) => {
|
376
|
+
if (args.length < 2)
|
377
|
+
throw new RangeError(
|
378
|
+
`Invalid number of arguments for (${
|
379
|
+
DEBUG.ASSERT
|
380
|
+
}), expected (> 2 required) but got ${args.length} (${
|
381
|
+
DEBUG.ASSERT
|
382
|
+
} ${stringifyArgs(args)})`
|
383
|
+
)
|
384
|
+
if (args.length % 2 !== 0)
|
385
|
+
throw new RangeError(
|
386
|
+
`Invalid number of arguments for (${
|
387
|
+
DEBUG.ASSERT
|
388
|
+
}), expected even number of arguments but got ${args.length} (${
|
389
|
+
DEBUG.ASSERT
|
390
|
+
} ${stringifyArgs(args)})`
|
391
|
+
)
|
392
|
+
for (let i = 0; i < args.length; i += 2) {
|
393
|
+
const condition = evaluate(args[i], env)
|
394
|
+
if (condition !== FALSE && condition !== TRUE)
|
395
|
+
throw new TypeError(
|
396
|
+
`Condition of (${
|
402
397
|
DEBUG.ASSERT
|
403
|
-
})
|
398
|
+
}) must be ${TRUE} or ${FALSE} but got (${
|
404
399
|
DEBUG.ASSERT
|
405
400
|
} ${stringifyArgs(args)})`
|
406
401
|
)
|
407
|
-
|
408
|
-
const
|
409
|
-
if (
|
402
|
+
if (condition) {
|
403
|
+
const error = args[i + 1]
|
404
|
+
if (error[0][TYPE] === APPLY && error[0][VALUE] === KEYWORDS.ERROR)
|
405
|
+
return evaluate(error, env)
|
406
|
+
else
|
410
407
|
throw new TypeError(
|
411
|
-
`
|
412
|
-
|
413
|
-
})
|
414
|
-
DEBUG.ASSERT
|
415
|
-
} ${stringifyArgs(args)})`
|
408
|
+
`Concequence of (${DEBUG.ASSERT}) must be (${
|
409
|
+
KEYWORDS.ERROR
|
410
|
+
}) but got (${DEBUG.ASSERT} ${stringifyArgs(args)})`
|
416
411
|
)
|
417
|
-
if (condition) {
|
418
|
-
const error = args[i + 1]
|
419
|
-
if (error[0][TYPE] === APPLY && error[0][VALUE] === KEYWORDS.ERROR)
|
420
|
-
return evaluate(error, env)
|
421
|
-
else
|
422
|
-
throw new TypeError(
|
423
|
-
`Concequence of (${DEBUG.ASSERT}) must be (${
|
424
|
-
KEYWORDS.ERROR
|
425
|
-
}) but got (${DEBUG.ASSERT} ${stringifyArgs(args)})`
|
426
|
-
)
|
427
|
-
}
|
428
412
|
}
|
429
|
-
return 0
|
430
413
|
}
|
414
|
+
return 0
|
415
|
+
}
|
416
|
+
}
|
417
|
+
try {
|
418
|
+
const evaluated = evaluate(ast, debugEnv)
|
419
|
+
const block = ast[1][1]
|
420
|
+
const temp = block.shift()
|
421
|
+
block.unshift(temp, identity(DEBUG.LOG), identity(DEBUG.ASSERT))
|
422
|
+
return {
|
423
|
+
evaluated,
|
424
|
+
compiled: compile(ast),
|
425
|
+
error: null
|
431
426
|
}
|
432
|
-
evaluate(ast, debugEnv)
|
433
427
|
} catch (error) {
|
434
428
|
const isMaxCallStack =
|
435
429
|
error.message.includes('Maximum call stack size exceeded') ||
|
436
430
|
error.message.includes('too much recursion')
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
431
|
+
return {
|
432
|
+
compiled: null,
|
433
|
+
evaluated: null,
|
434
|
+
error: {
|
435
|
+
message: isMaxCallStack
|
436
|
+
? error.message
|
437
|
+
: `${error.message}\n${debugEnv[DEBUG.CALLSTACK]
|
438
|
+
.reverse()
|
439
|
+
.map((x, i) => `${Array(i + 2).join(' ')}(${x} ...)`)
|
440
|
+
.join('\n')}`,
|
441
|
+
stack: debugEnv[DEBUG.CALLSTACK]
|
442
|
+
}
|
443
|
+
}
|
444
444
|
}
|
445
|
-
const block = ast[1][1]
|
446
|
-
const temp = block.shift()
|
447
|
-
block.unshift(temp, identity(DEBUG.LOG), identity(DEBUG.ASSERT))
|
448
|
-
return compile(ast)
|
449
445
|
}
|