fez-lisp 1.4.3 → 1.4.5
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 +10 -4
- package/src/keywords.js +4 -2
- package/src/utils.js +140 -111
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) => {
|
@@ -758,7 +759,11 @@ export const keywords = {
|
|
758
759
|
name ? ` ${name}` : ''
|
759
760
|
}) are provided (expects ${params.length} but got ${
|
760
761
|
props.length
|
761
|
-
})\n\n
|
762
|
+
})\n\n${
|
763
|
+
name
|
764
|
+
? `(${name} ${stringifyArgs(params)})`
|
765
|
+
: `(${KEYWORDS.ANONYMOUS_FUNCTION} ${stringifyArgs(params)})`
|
766
|
+
}`
|
762
767
|
)
|
763
768
|
const localEnv = Object.create(env)
|
764
769
|
// localEnv[KEYWORDS.BLOCK] = block[KEYWORDS.BLOCK]
|
@@ -769,7 +774,8 @@ export const keywords = {
|
|
769
774
|
writable: true
|
770
775
|
})
|
771
776
|
}
|
772
|
-
if (name
|
777
|
+
if (name && Array.isArray(env[DEBUG.CALLSTACK]))
|
778
|
+
env[DEBUG.CALLSTACK].push(name)
|
773
779
|
return evaluate(body, localEnv)
|
774
780
|
}
|
775
781
|
},
|
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
@@ -317,133 +317,162 @@ const identity = (name) => [
|
|
317
317
|
[1, 'x']
|
318
318
|
]
|
319
319
|
]
|
320
|
-
export const
|
320
|
+
// export const result = (ast) => {
|
321
|
+
// try {
|
322
|
+
// return { output: evaluate(ast, keywords), error: null }
|
323
|
+
// } catch (error) {
|
324
|
+
// const isMaxCallStack =
|
325
|
+
// error.message.includes('Maximum call stack size exceeded') ||
|
326
|
+
// error.message.includes('too much recursion')
|
327
|
+
// return {
|
328
|
+
// output: null,
|
329
|
+
// error: {
|
330
|
+
// stack: [...callStack],
|
331
|
+
// message: isMaxCallStack
|
332
|
+
// ? error
|
333
|
+
// : `${error}\n${callStack
|
334
|
+
// .reverse()
|
335
|
+
// .map((x, i) => `${Array(i + 2).join(' ')}(${x} ...)`)
|
336
|
+
// .join('\n')}`
|
337
|
+
// }
|
338
|
+
// }
|
339
|
+
// }
|
340
|
+
// }
|
321
341
|
export const debug = (ast) => {
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
342
|
+
const debugEnv = {
|
343
|
+
...keywords,
|
344
|
+
[DEBUG.CALLSTACK]: [KEYWORDS.BLOCK],
|
345
|
+
[DEBUG.LOG]: (args, env) => {
|
346
|
+
if (args.length !== 1 && args.length !== 2)
|
347
|
+
throw new RangeError(
|
348
|
+
`Invalid number of arguments to (${DEBUG.LOG}) (or (= 1) (= 2)) (${
|
349
|
+
DEBUG.LOG
|
350
|
+
} ${stringifyArgs(args)})`
|
351
|
+
)
|
352
|
+
const expression = evaluate(args[0], env)
|
353
|
+
if (args.length === 2) {
|
354
|
+
const option = evaluate(args[1], env)
|
355
|
+
if (!Array.isArray(option)) {
|
356
|
+
throw new TypeError(
|
357
|
+
`Second argument of (${DEBUG.LOG}) must be an (${
|
358
|
+
RUNTIME_TYPES.ARRAY
|
359
|
+
}) but got (${expression}) (${DEBUG.LOG} ${stringifyArgs(args)})`
|
333
360
|
)
|
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('')
|
361
|
+
}
|
362
|
+
const type = option.map((x) => String.fromCharCode(x)).join('')
|
363
|
+
switch (type) {
|
364
|
+
case 'string':
|
365
|
+
case 'str':
|
366
|
+
{
|
367
|
+
if (!Array.isArray(expression))
|
368
|
+
throw new TypeError(
|
369
|
+
`Argument of (${DEBUG.LOG}) must be an (${
|
370
|
+
RUNTIME_TYPES.ARRAY
|
371
|
+
}) in the case ${type} but got (${expression}) (${
|
372
|
+
DEBUG.LOG
|
373
|
+
} ${stringifyArgs(args)})`
|
359
374
|
)
|
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)})`
|
375
|
+
console.log(
|
376
|
+
expression.map((x) => String.fromCharCode(x)).join('')
|
385
377
|
)
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
378
|
+
}
|
379
|
+
break
|
380
|
+
case 'char':
|
381
|
+
case 'ch':
|
382
|
+
{
|
383
|
+
if (typeof expression !== 'number')
|
384
|
+
throw new TypeError(
|
385
|
+
`Argument argument of (${DEBUG.LOG}) must be a (${
|
386
|
+
RUNTIME_TYPES.NUMBER
|
387
|
+
}) in the case ${type} but got (${expression}) (${
|
388
|
+
DEBUG.LOG
|
389
|
+
} ${stringifyArgs(args)})`
|
390
|
+
)
|
391
|
+
console.log(String.fromCharCode(expression))
|
392
|
+
}
|
393
|
+
|
394
|
+
break
|
395
|
+
case '*':
|
396
|
+
console.log(expression)
|
397
|
+
break
|
398
|
+
default:
|
399
|
+
throw new TypeError(
|
400
|
+
`Invalid number of option to (${
|
401
|
+
DEBUG.LOG
|
402
|
+
}) got ${option} ${stringifyArgs(args)})`
|
403
|
+
)
|
404
|
+
}
|
405
|
+
} else console.log(expression)
|
406
|
+
return expression
|
407
|
+
},
|
408
|
+
[DEBUG.ASSERT]: (args, env) => {
|
409
|
+
if (args.length < 2)
|
410
|
+
throw new RangeError(
|
411
|
+
`Invalid number of arguments for (${
|
412
|
+
DEBUG.ASSERT
|
413
|
+
}), expected (> 2 required) but got ${args.length} (${
|
414
|
+
DEBUG.ASSERT
|
415
|
+
} ${stringifyArgs(args)})`
|
416
|
+
)
|
417
|
+
if (args.length % 2 !== 0)
|
418
|
+
throw new RangeError(
|
419
|
+
`Invalid number of arguments for (${
|
420
|
+
DEBUG.ASSERT
|
421
|
+
}), expected even number of arguments but got ${args.length} (${
|
422
|
+
DEBUG.ASSERT
|
423
|
+
} ${stringifyArgs(args)})`
|
424
|
+
)
|
425
|
+
for (let i = 0; i < args.length; i += 2) {
|
426
|
+
const condition = evaluate(args[i], env)
|
427
|
+
if (condition !== FALSE && condition !== TRUE)
|
428
|
+
throw new TypeError(
|
429
|
+
`Condition of (${
|
402
430
|
DEBUG.ASSERT
|
403
|
-
})
|
431
|
+
}) must be ${TRUE} or ${FALSE} but got (${
|
404
432
|
DEBUG.ASSERT
|
405
433
|
} ${stringifyArgs(args)})`
|
406
434
|
)
|
407
|
-
|
408
|
-
const
|
409
|
-
if (
|
435
|
+
if (condition) {
|
436
|
+
const error = args[i + 1]
|
437
|
+
if (error[0][TYPE] === APPLY && error[0][VALUE] === KEYWORDS.ERROR)
|
438
|
+
return evaluate(error, env)
|
439
|
+
else
|
410
440
|
throw new TypeError(
|
411
|
-
`
|
412
|
-
|
413
|
-
})
|
414
|
-
DEBUG.ASSERT
|
415
|
-
} ${stringifyArgs(args)})`
|
441
|
+
`Concequence of (${DEBUG.ASSERT}) must be (${
|
442
|
+
KEYWORDS.ERROR
|
443
|
+
}) but got (${DEBUG.ASSERT} ${stringifyArgs(args)})`
|
416
444
|
)
|
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
445
|
}
|
429
|
-
return 0
|
430
446
|
}
|
447
|
+
return 0
|
448
|
+
}
|
449
|
+
}
|
450
|
+
try {
|
451
|
+
const evaluated = evaluate(ast, debugEnv)
|
452
|
+
const block = ast[1][1]
|
453
|
+
const temp = block.shift()
|
454
|
+
block.unshift(temp, identity(DEBUG.LOG), identity(DEBUG.ASSERT))
|
455
|
+
return {
|
456
|
+
evaluated,
|
457
|
+
compiled: compile(ast),
|
458
|
+
error: null
|
431
459
|
}
|
432
|
-
evaluate(ast, debugEnv)
|
433
460
|
} catch (error) {
|
434
461
|
const isMaxCallStack =
|
435
462
|
error.message.includes('Maximum call stack size exceeded') ||
|
436
463
|
error.message.includes('too much recursion')
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
464
|
+
return {
|
465
|
+
compiled: null,
|
466
|
+
evaluated: null,
|
467
|
+
error: {
|
468
|
+
message: isMaxCallStack
|
469
|
+
? error.message
|
470
|
+
: `${error.message}\n${debugEnv[DEBUG.CALLSTACK]
|
471
|
+
.reverse()
|
472
|
+
.map((x, i) => `${Array(i + 2).join(' ')}(${x} ...)`)
|
473
|
+
.join('\n')}`,
|
474
|
+
stack: debugEnv[DEBUG.CALLSTACK]
|
475
|
+
}
|
476
|
+
}
|
444
477
|
}
|
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
478
|
}
|