fez-lisp 1.2.15 → 1.2.17
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 +5 -4
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/interpreter.js +0 -136
- package/src/keywords.js +10 -5
- package/src/utils.js +155 -2
package/package.json
CHANGED
package/src/interpreter.js
CHANGED
@@ -799,140 +799,4 @@ const keywords = {
|
|
799
799
|
}
|
800
800
|
keywords[KEYWORDS.NOT_COMPILED_BLOCK] = keywords[KEYWORDS.BLOCK]
|
801
801
|
|
802
|
-
export const deSuggar = (ast) => {
|
803
|
-
if (ast.length === 0)
|
804
|
-
throw new SyntaxError(
|
805
|
-
`Top level ${KEYWORDS.NUMBER_TYPE} need to be wrapped in a (${KEYWORDS.IDENTITY})`
|
806
|
-
)
|
807
|
-
// for (const node of ast)
|
808
|
-
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
809
|
-
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
810
|
-
let prev = undefined
|
811
|
-
const evaluate = (exp) => {
|
812
|
-
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
813
|
-
if (first != undefined) {
|
814
|
-
switch (first[TYPE]) {
|
815
|
-
case WORD:
|
816
|
-
break
|
817
|
-
case ATOM:
|
818
|
-
break
|
819
|
-
case APPLY:
|
820
|
-
{
|
821
|
-
switch (first[VALUE]) {
|
822
|
-
case KEYWORDS.BLOCK:
|
823
|
-
{
|
824
|
-
if (
|
825
|
-
prev == undefined ||
|
826
|
-
(prev &&
|
827
|
-
prev[TYPE] === APPLY &&
|
828
|
-
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
829
|
-
) {
|
830
|
-
exp[0][1] = KEYWORDS.CALL_FUNCTION
|
831
|
-
exp[0][0] = APPLY
|
832
|
-
exp.length = 1
|
833
|
-
exp[1] = [
|
834
|
-
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
835
|
-
[[APPLY, KEYWORDS.BLOCK], ...rest]
|
836
|
-
]
|
837
|
-
deSuggar(exp)
|
838
|
-
}
|
839
|
-
}
|
840
|
-
break
|
841
|
-
// case KEYWORDS.DEFINE_VARIABLE:
|
842
|
-
// {
|
843
|
-
// if (
|
844
|
-
// rest[1] &&
|
845
|
-
// rest[1][0] &&
|
846
|
-
// rest[1][0][TYPE] === APPLY &&
|
847
|
-
// rest[1][0][VALUE] === KEYWORDS.BLOCK
|
848
|
-
// ) {
|
849
|
-
// throw new SyntaxError(
|
850
|
-
// `Can't use (${KEYWORDS.BLOCK}) in (${KEYWORDS.DEFINE_VARIABLE})`
|
851
|
-
// )
|
852
|
-
// }
|
853
|
-
// }
|
854
|
-
// break
|
855
|
-
case KEYWORDS.PIPE:
|
856
|
-
{
|
857
|
-
if (rest.length < 1)
|
858
|
-
throw new RangeError(
|
859
|
-
`Invalid number of arguments to (${
|
860
|
-
KEYWORDS.PIPE
|
861
|
-
}) (>= 1 required). (${KEYWORDS.PIPE} ${stringifyArgs(
|
862
|
-
rest
|
863
|
-
)})`
|
864
|
-
)
|
865
|
-
let inp = rest[0]
|
866
|
-
exp.length = 0
|
867
|
-
for (let i = 1; i < rest.length; ++i) {
|
868
|
-
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
869
|
-
throw new TypeError(
|
870
|
-
`Argument at position (${i}) of (${
|
871
|
-
KEYWORDS.PIPE
|
872
|
-
}) is not an invoked (${
|
873
|
-
KEYWORDS.ANONYMOUS_FUNCTION
|
874
|
-
}). (${KEYWORDS.PIPE} ${stringifyArgs(rest)})`
|
875
|
-
)
|
876
|
-
inp = [rest[i].shift(), inp, ...rest[i]]
|
877
|
-
}
|
878
|
-
for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
|
879
|
-
deSuggar(exp)
|
880
|
-
}
|
881
|
-
break
|
882
|
-
case KEYWORDS.LIST_TYPE:
|
883
|
-
{
|
884
|
-
exp.length = 0
|
885
|
-
let temp = exp
|
886
|
-
for (const item of rest) {
|
887
|
-
temp.push([0, KEYWORDS.ARRAY_TYPE], item, [])
|
888
|
-
temp = temp.at(-1)
|
889
|
-
}
|
890
|
-
temp.push([0, 'array'])
|
891
|
-
deSuggar(exp)
|
892
|
-
}
|
893
|
-
break
|
894
|
-
case KEYWORDS.MULTIPLICATION:
|
895
|
-
if (!rest.length) {
|
896
|
-
exp[0][0] = 2
|
897
|
-
exp[0][1] = 1
|
898
|
-
}
|
899
|
-
break
|
900
|
-
case KEYWORDS.ADDITION:
|
901
|
-
case KEYWORDS.DIVISION:
|
902
|
-
if (!rest.length) {
|
903
|
-
exp[0][0] = 2
|
904
|
-
exp[0][1] = 0
|
905
|
-
}
|
906
|
-
break
|
907
|
-
case KEYWORDS.UNLESS:
|
908
|
-
{
|
909
|
-
if (rest.length > 3 || rest.length < 2)
|
910
|
-
throw new RangeError(
|
911
|
-
`Invalid number of arguments for (${
|
912
|
-
KEYWORDS.UNLESS
|
913
|
-
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
914
|
-
KEYWORDS.UNLESS
|
915
|
-
} ${stringifyArgs(rest)})`
|
916
|
-
)
|
917
|
-
exp[0][1] = KEYWORDS.IF
|
918
|
-
const temp = exp[2]
|
919
|
-
exp[2] = exp[3] ?? [ATOM, 0]
|
920
|
-
exp[3] = temp
|
921
|
-
}
|
922
|
-
break
|
923
|
-
}
|
924
|
-
prev = first
|
925
|
-
}
|
926
|
-
break
|
927
|
-
default:
|
928
|
-
for (const e of exp) evaluate(e)
|
929
|
-
break
|
930
|
-
}
|
931
|
-
for (const r of rest) evaluate(r)
|
932
|
-
}
|
933
|
-
}
|
934
|
-
evaluate(ast)
|
935
|
-
return ast
|
936
|
-
}
|
937
|
-
|
938
802
|
export { keywords }
|
package/src/keywords.js
CHANGED
@@ -7,9 +7,7 @@ export const TRUE = 1
|
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
9
|
export const KEYWORDS = {
|
10
|
-
RECURSION: 'rec',
|
11
10
|
NUMBER_TYPE: 'number',
|
12
|
-
LIST_TYPE: 'list',
|
13
11
|
ARRAY_TYPE: 'array',
|
14
12
|
IDENTITY: 'identity',
|
15
13
|
ARRAY_LENGTH: 'length',
|
@@ -31,7 +29,6 @@ export const KEYWORDS = {
|
|
31
29
|
BLOCK: 'do',
|
32
30
|
ANONYMOUS_FUNCTION: 'lambda',
|
33
31
|
IF: 'if',
|
34
|
-
UNLESS: 'unless',
|
35
32
|
CONDITION: 'cond',
|
36
33
|
NOT: 'not',
|
37
34
|
EQUAL: '=',
|
@@ -41,9 +38,9 @@ export const KEYWORDS = {
|
|
41
38
|
LESS_THAN_OR_EQUAL: '<=',
|
42
39
|
AND: 'and',
|
43
40
|
OR: 'or',
|
41
|
+
|
44
42
|
CALL_FUNCTION: 'apply',
|
45
43
|
DEFINE_VARIABLE: 'let',
|
46
|
-
PIPE: '|>',
|
47
44
|
|
48
45
|
SET_ARRAY: 'set!',
|
49
46
|
|
@@ -51,7 +48,15 @@ export const KEYWORDS = {
|
|
51
48
|
LOG: 'log!',
|
52
49
|
LOG_STRING: 'log-string!',
|
53
50
|
LOG_CHAR: 'log-char!',
|
54
|
-
CLEAR_CONSOLE: 'clear!'
|
51
|
+
CLEAR_CONSOLE: 'clear!',
|
52
|
+
|
53
|
+
// Syntactic suggars
|
54
|
+
PIPE: '|>',
|
55
|
+
NOT_EQUAL_1: '!=',
|
56
|
+
NOT_EQUAL_2: '<>',
|
57
|
+
UNLESS: 'unless',
|
58
|
+
LIST_TYPE: 'list',
|
59
|
+
RECURSION: 'rec'
|
55
60
|
}
|
56
61
|
|
57
62
|
export const TYPES = {
|
package/src/utils.js
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
2
|
import { comp } from './compiler.js'
|
3
3
|
import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
|
4
|
-
import { run } from './evaluator.js'
|
4
|
+
import { evaluate, run } from './evaluator.js'
|
5
5
|
import { AST, isLeaf, LISP } from './parser.js'
|
6
|
-
import { deSuggar } from './interpreter.js'
|
7
6
|
export const logError = (error) =>
|
8
7
|
console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
|
9
8
|
export const logSuccess = (output) => console.log(output, '\x1b[0m')
|
@@ -228,6 +227,8 @@ export const dfs = (tree, callback) => {
|
|
228
227
|
else callback(tree)
|
229
228
|
}
|
230
229
|
export const deepClone = (ast) => AST.parse(AST.stringify(ast))
|
230
|
+
export const interpret = (ast, keywords) =>
|
231
|
+
ast.reduce((_, x) => evaluate(x, keywords), 0)
|
231
232
|
export const fez = (source, options = {}) => {
|
232
233
|
const env = Object.create(null)
|
233
234
|
try {
|
@@ -355,3 +356,155 @@ export const js = (source, deps) => {
|
|
355
356
|
])
|
356
357
|
return `${top}${program}`
|
357
358
|
}
|
359
|
+
|
360
|
+
export const deSuggar = (ast) => {
|
361
|
+
if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
|
362
|
+
// for (const node of ast)
|
363
|
+
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
364
|
+
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
365
|
+
let prev = undefined
|
366
|
+
const evaluate = (exp) => {
|
367
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
368
|
+
if (first != undefined) {
|
369
|
+
switch (first[TYPE]) {
|
370
|
+
case WORD:
|
371
|
+
break
|
372
|
+
case ATOM:
|
373
|
+
break
|
374
|
+
case APPLY:
|
375
|
+
{
|
376
|
+
switch (first[VALUE]) {
|
377
|
+
case KEYWORDS.BLOCK:
|
378
|
+
{
|
379
|
+
if (
|
380
|
+
prev == undefined ||
|
381
|
+
(prev &&
|
382
|
+
prev[TYPE] === APPLY &&
|
383
|
+
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
384
|
+
) {
|
385
|
+
exp[0][1] = KEYWORDS.CALL_FUNCTION
|
386
|
+
exp[0][0] = APPLY
|
387
|
+
exp.length = 1
|
388
|
+
exp[1] = [
|
389
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
390
|
+
[[APPLY, KEYWORDS.BLOCK], ...rest]
|
391
|
+
]
|
392
|
+
deSuggar(exp)
|
393
|
+
}
|
394
|
+
}
|
395
|
+
break
|
396
|
+
// case KEYWORDS.DEFINE_VARIABLE:
|
397
|
+
// {
|
398
|
+
// if (
|
399
|
+
// rest[1] &&
|
400
|
+
// rest[1][0] &&
|
401
|
+
// rest[1][0][TYPE] === APPLY &&
|
402
|
+
// rest[1][0][VALUE] === KEYWORDS.BLOCK
|
403
|
+
// ) {
|
404
|
+
// throw new SyntaxError(
|
405
|
+
// `Can't use (${KEYWORDS.BLOCK}) in (${KEYWORDS.DEFINE_VARIABLE})`
|
406
|
+
// )
|
407
|
+
// }
|
408
|
+
// }
|
409
|
+
// break
|
410
|
+
case KEYWORDS.PIPE:
|
411
|
+
{
|
412
|
+
if (rest.length < 1)
|
413
|
+
throw new RangeError(
|
414
|
+
`Invalid number of arguments to (${
|
415
|
+
KEYWORDS.PIPE
|
416
|
+
}) (>= 1 required). (${KEYWORDS.PIPE} ${stringifyArgs(
|
417
|
+
rest
|
418
|
+
)})`
|
419
|
+
)
|
420
|
+
let inp = rest[0]
|
421
|
+
exp.length = 0
|
422
|
+
for (let i = 1; i < rest.length; ++i) {
|
423
|
+
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
424
|
+
throw new TypeError(
|
425
|
+
`Argument at position (${i}) of (${
|
426
|
+
KEYWORDS.PIPE
|
427
|
+
}) is not an invoked (${
|
428
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
429
|
+
}). (${KEYWORDS.PIPE} ${stringifyArgs(rest)})`
|
430
|
+
)
|
431
|
+
inp = [rest[i].shift(), inp, ...rest[i]]
|
432
|
+
}
|
433
|
+
for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
|
434
|
+
deSuggar(exp)
|
435
|
+
}
|
436
|
+
break
|
437
|
+
case KEYWORDS.LIST_TYPE:
|
438
|
+
{
|
439
|
+
exp.length = 0
|
440
|
+
let temp = exp
|
441
|
+
for (const item of rest) {
|
442
|
+
temp.push([0, KEYWORDS.ARRAY_TYPE], item, [])
|
443
|
+
temp = temp.at(-1)
|
444
|
+
}
|
445
|
+
temp.push([0, 'array'])
|
446
|
+
deSuggar(exp)
|
447
|
+
}
|
448
|
+
break
|
449
|
+
case KEYWORDS.MULTIPLICATION:
|
450
|
+
if (!rest.length) {
|
451
|
+
exp[0][0] = 2
|
452
|
+
exp[0][1] = 1
|
453
|
+
}
|
454
|
+
break
|
455
|
+
case KEYWORDS.ADDITION:
|
456
|
+
case KEYWORDS.DIVISION:
|
457
|
+
if (!rest.length) {
|
458
|
+
exp[0][0] = 2
|
459
|
+
exp[0][1] = 0
|
460
|
+
}
|
461
|
+
break
|
462
|
+
case KEYWORDS.UNLESS:
|
463
|
+
{
|
464
|
+
if (rest.length > 3 || rest.length < 2)
|
465
|
+
throw new RangeError(
|
466
|
+
`Invalid number of arguments for (${
|
467
|
+
KEYWORDS.UNLESS
|
468
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
469
|
+
KEYWORDS.UNLESS
|
470
|
+
} ${stringifyArgs(rest)})`
|
471
|
+
)
|
472
|
+
exp[0][1] = KEYWORDS.IF
|
473
|
+
const temp = exp[2]
|
474
|
+
exp[2] = exp[3] ?? [ATOM, 0]
|
475
|
+
exp[3] = temp
|
476
|
+
}
|
477
|
+
deSuggar(exp)
|
478
|
+
break
|
479
|
+
|
480
|
+
case KEYWORDS.NOT_EQUAL_1:
|
481
|
+
case KEYWORDS.NOT_EQUAL_2:
|
482
|
+
{
|
483
|
+
if (rest.length > 3 || rest.length < 2)
|
484
|
+
throw new RangeError(
|
485
|
+
`Invalid number of arguments for (${
|
486
|
+
exp[0][1]
|
487
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
488
|
+
exp[0][1]
|
489
|
+
} ${stringifyArgs(rest)})`
|
490
|
+
)
|
491
|
+
exp[0][1] = KEYWORDS.NOT
|
492
|
+
exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
|
493
|
+
exp.length = 2
|
494
|
+
deSuggar(exp)
|
495
|
+
}
|
496
|
+
break
|
497
|
+
}
|
498
|
+
prev = first
|
499
|
+
}
|
500
|
+
break
|
501
|
+
default:
|
502
|
+
for (const e of exp) evaluate(e)
|
503
|
+
break
|
504
|
+
}
|
505
|
+
for (const r of rest) evaluate(r)
|
506
|
+
}
|
507
|
+
}
|
508
|
+
evaluate(ast)
|
509
|
+
return ast
|
510
|
+
}
|