fez-lisp 1.2.15 → 1.2.16
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/package.json +1 -1
- package/src/interpreter.js +0 -136
- package/src/keywords.js +10 -5
- package/src/utils.js +152 -1
package/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<img width="64" src="./logo.svg"/>
|
5
5
|
</p>
|
6
6
|
|
7
|
-
|
7
|
+
```lisp
|
8
8
|
(let fizz-buzz (lambda n
|
9
9
|
(cond
|
10
10
|
(= (mod n 15) 0) "Fizz Buzz"
|
@@ -34,7 +34,7 @@
|
|
34
34
|
(math:range 1 100)
|
35
35
|
(array:map fizz-buzz)
|
36
36
|
(log!))
|
37
|
-
|
37
|
+
```
|
38
38
|
|
39
39
|
```lisp
|
40
40
|
; https://adventofcode.com/2020/day/1
|
@@ -242,10 +242,11 @@ console.log(fez(tree(`(+ (|> 1 (+ 2) (* 3) (- 1)) (- (* (+ 1 2) 3) 1))`)))
|
|
242
242
|
```
|
243
243
|
|
244
244
|
```lisp
|
245
|
-
; all keywords
|
245
|
+
; Build-in all keywords
|
246
246
|
(/) (+) (*) (-) (=) (<) (>) (>=) (<=) (&) (~) (|) (^) (<<) (>>) (>>>)
|
247
247
|
(mod) (let) (if) (not) (and) (or) (cond) (atom?) (lambda?)
|
248
248
|
(length) (do) (array) (set!) (get) (lambda) (apply) (void)
|
249
249
|
(log!) (log-string!) (log-char!) (clear!)
|
250
|
-
|
250
|
+
; Syntactic suggar keywords
|
251
|
+
(|>) (list) (unless) (array?) (truthy?) (falsy?) (evaluate) '() `() (!=) (<>)
|
251
252
|
```
|
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
@@ -3,7 +3,6 @@ import { comp } from './compiler.js'
|
|
3
3
|
import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
|
4
4
|
import { 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')
|
@@ -355,3 +354,155 @@ export const js = (source, deps) => {
|
|
355
354
|
])
|
356
355
|
return `${top}${program}`
|
357
356
|
}
|
357
|
+
|
358
|
+
export const deSuggar = (ast) => {
|
359
|
+
if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
|
360
|
+
// for (const node of ast)
|
361
|
+
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
362
|
+
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
363
|
+
let prev = undefined
|
364
|
+
const evaluate = (exp) => {
|
365
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
366
|
+
if (first != undefined) {
|
367
|
+
switch (first[TYPE]) {
|
368
|
+
case WORD:
|
369
|
+
break
|
370
|
+
case ATOM:
|
371
|
+
break
|
372
|
+
case APPLY:
|
373
|
+
{
|
374
|
+
switch (first[VALUE]) {
|
375
|
+
case KEYWORDS.BLOCK:
|
376
|
+
{
|
377
|
+
if (
|
378
|
+
prev == undefined ||
|
379
|
+
(prev &&
|
380
|
+
prev[TYPE] === APPLY &&
|
381
|
+
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
382
|
+
) {
|
383
|
+
exp[0][1] = KEYWORDS.CALL_FUNCTION
|
384
|
+
exp[0][0] = APPLY
|
385
|
+
exp.length = 1
|
386
|
+
exp[1] = [
|
387
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
388
|
+
[[APPLY, KEYWORDS.BLOCK], ...rest]
|
389
|
+
]
|
390
|
+
deSuggar(exp)
|
391
|
+
}
|
392
|
+
}
|
393
|
+
break
|
394
|
+
// case KEYWORDS.DEFINE_VARIABLE:
|
395
|
+
// {
|
396
|
+
// if (
|
397
|
+
// rest[1] &&
|
398
|
+
// rest[1][0] &&
|
399
|
+
// rest[1][0][TYPE] === APPLY &&
|
400
|
+
// rest[1][0][VALUE] === KEYWORDS.BLOCK
|
401
|
+
// ) {
|
402
|
+
// throw new SyntaxError(
|
403
|
+
// `Can't use (${KEYWORDS.BLOCK}) in (${KEYWORDS.DEFINE_VARIABLE})`
|
404
|
+
// )
|
405
|
+
// }
|
406
|
+
// }
|
407
|
+
// break
|
408
|
+
case KEYWORDS.PIPE:
|
409
|
+
{
|
410
|
+
if (rest.length < 1)
|
411
|
+
throw new RangeError(
|
412
|
+
`Invalid number of arguments to (${
|
413
|
+
KEYWORDS.PIPE
|
414
|
+
}) (>= 1 required). (${KEYWORDS.PIPE} ${stringifyArgs(
|
415
|
+
rest
|
416
|
+
)})`
|
417
|
+
)
|
418
|
+
let inp = rest[0]
|
419
|
+
exp.length = 0
|
420
|
+
for (let i = 1; i < rest.length; ++i) {
|
421
|
+
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
422
|
+
throw new TypeError(
|
423
|
+
`Argument at position (${i}) of (${
|
424
|
+
KEYWORDS.PIPE
|
425
|
+
}) is not an invoked (${
|
426
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
427
|
+
}). (${KEYWORDS.PIPE} ${stringifyArgs(rest)})`
|
428
|
+
)
|
429
|
+
inp = [rest[i].shift(), inp, ...rest[i]]
|
430
|
+
}
|
431
|
+
for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
|
432
|
+
deSuggar(exp)
|
433
|
+
}
|
434
|
+
break
|
435
|
+
case KEYWORDS.LIST_TYPE:
|
436
|
+
{
|
437
|
+
exp.length = 0
|
438
|
+
let temp = exp
|
439
|
+
for (const item of rest) {
|
440
|
+
temp.push([0, KEYWORDS.ARRAY_TYPE], item, [])
|
441
|
+
temp = temp.at(-1)
|
442
|
+
}
|
443
|
+
temp.push([0, 'array'])
|
444
|
+
deSuggar(exp)
|
445
|
+
}
|
446
|
+
break
|
447
|
+
case KEYWORDS.MULTIPLICATION:
|
448
|
+
if (!rest.length) {
|
449
|
+
exp[0][0] = 2
|
450
|
+
exp[0][1] = 1
|
451
|
+
}
|
452
|
+
break
|
453
|
+
case KEYWORDS.ADDITION:
|
454
|
+
case KEYWORDS.DIVISION:
|
455
|
+
if (!rest.length) {
|
456
|
+
exp[0][0] = 2
|
457
|
+
exp[0][1] = 0
|
458
|
+
}
|
459
|
+
break
|
460
|
+
case KEYWORDS.UNLESS:
|
461
|
+
{
|
462
|
+
if (rest.length > 3 || rest.length < 2)
|
463
|
+
throw new RangeError(
|
464
|
+
`Invalid number of arguments for (${
|
465
|
+
KEYWORDS.UNLESS
|
466
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
467
|
+
KEYWORDS.UNLESS
|
468
|
+
} ${stringifyArgs(rest)})`
|
469
|
+
)
|
470
|
+
exp[0][1] = KEYWORDS.IF
|
471
|
+
const temp = exp[2]
|
472
|
+
exp[2] = exp[3] ?? [ATOM, 0]
|
473
|
+
exp[3] = temp
|
474
|
+
}
|
475
|
+
deSuggar(exp)
|
476
|
+
break
|
477
|
+
|
478
|
+
case KEYWORDS.NOT_EQUAL_1:
|
479
|
+
case KEYWORDS.NOT_EQUAL_2:
|
480
|
+
{
|
481
|
+
if (rest.length > 3 || rest.length < 2)
|
482
|
+
throw new RangeError(
|
483
|
+
`Invalid number of arguments for (${
|
484
|
+
exp[0][1]
|
485
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
486
|
+
exp[0][1]
|
487
|
+
} ${stringifyArgs(rest)})`
|
488
|
+
)
|
489
|
+
exp[0][1] = KEYWORDS.NOT
|
490
|
+
exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
|
491
|
+
exp.length = 2
|
492
|
+
deSuggar(exp)
|
493
|
+
}
|
494
|
+
break
|
495
|
+
}
|
496
|
+
prev = first
|
497
|
+
}
|
498
|
+
break
|
499
|
+
default:
|
500
|
+
for (const e of exp) evaluate(e)
|
501
|
+
break
|
502
|
+
}
|
503
|
+
for (const r of rest) evaluate(r)
|
504
|
+
}
|
505
|
+
}
|
506
|
+
evaluate(ast)
|
507
|
+
return ast
|
508
|
+
}
|