fez-lisp 1.2.49 → 1.2.53
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/compiler.js +6 -15
- package/src/interpreter.js +0 -54
- package/src/keywords.js +14 -12
- package/src/utils.js +123 -25
package/package.json
CHANGED
package/src/compiler.js
CHANGED
@@ -5,7 +5,8 @@ import {
|
|
5
5
|
KEYWORDS,
|
6
6
|
TYPE,
|
7
7
|
VALUE,
|
8
|
-
WORD
|
8
|
+
WORD,
|
9
|
+
SUGGAR
|
9
10
|
} from './keywords.js'
|
10
11
|
import { leaf, isLeaf } from './parser.js'
|
11
12
|
const deepRenameTco = (name, newName, tree) => {
|
@@ -176,9 +177,9 @@ const compile = (tree, Drill) => {
|
|
176
177
|
case KEYWORDS.DEFINE_VARIABLE: {
|
177
178
|
const n = Arguments[0][VALUE]
|
178
179
|
const prefix = n.split(':')[0]
|
179
|
-
if (prefix ===
|
180
|
+
if (prefix === SUGGAR.RECURSION) {
|
180
181
|
const name = lispToJavaScriptVariableName(n)
|
181
|
-
const newName =
|
182
|
+
const newName = `${SUGGAR.RECURSION}_${performance
|
182
183
|
.now()
|
183
184
|
.toString()
|
184
185
|
.replace('.', 7)}`
|
@@ -199,10 +200,10 @@ const compile = (tree, Drill) => {
|
|
199
200
|
)})=>{${vars}return ${evaluatedBody
|
200
201
|
.toString()
|
201
202
|
.trim()}}, ${newName})));`
|
202
|
-
} else if (prefix ===
|
203
|
+
} else if (prefix === SUGGAR.CACHE) {
|
203
204
|
// memoization here
|
204
205
|
const name = lispToJavaScriptVariableName(n)
|
205
|
-
const newName = name.substring(
|
206
|
+
const newName = name.substring(SUGGAR.CACHE.length + 1)
|
206
207
|
Drill.Variables.add(name)
|
207
208
|
const functionArgs = Arguments.at(-1).slice(1)
|
208
209
|
const body = functionArgs.pop()
|
@@ -312,16 +313,6 @@ const compile = (tree, Drill) => {
|
|
312
313
|
Drill
|
313
314
|
)}:${Arguments.length === 3 ? compile(Arguments[2], Drill) : 0});`
|
314
315
|
}
|
315
|
-
case KEYWORDS.CONDITION: {
|
316
|
-
let out = '('
|
317
|
-
for (let i = 0; i < Arguments.length; i += 2)
|
318
|
-
out += `${compile(Arguments[i], Drill)}?${compile(
|
319
|
-
Arguments[i + 1],
|
320
|
-
Drill
|
321
|
-
)}:`
|
322
|
-
out += '0);'
|
323
|
-
return out
|
324
|
-
}
|
325
316
|
case KEYWORDS.THROW: {
|
326
317
|
Drill.Helpers.add('__error')
|
327
318
|
return `__error(${compile(Arguments[0], Drill)})`
|
package/src/interpreter.js
CHANGED
@@ -171,37 +171,6 @@ export const keywords = {
|
|
171
171
|
? evaluate(args[2], env)
|
172
172
|
: 0
|
173
173
|
},
|
174
|
-
[KEYWORDS.CONDITION]: (args, env) => {
|
175
|
-
if (args.length < 2)
|
176
|
-
throw new RangeError(
|
177
|
-
`Invalid number of arguments for (${
|
178
|
-
KEYWORDS.CONDITION
|
179
|
-
}), expected (> 2 required) but got ${args.length} (${
|
180
|
-
KEYWORDS.CONDITION
|
181
|
-
} ${stringifyArgs(args)})`
|
182
|
-
)
|
183
|
-
if (args.length % 2 !== 0)
|
184
|
-
throw new RangeError(
|
185
|
-
`Invalid number of arguments for (${
|
186
|
-
KEYWORDS.CONDITION
|
187
|
-
}), expected even number of arguments but got ${args.length} (${
|
188
|
-
KEYWORDS.CONDITION
|
189
|
-
} ${stringifyArgs(args)})`
|
190
|
-
)
|
191
|
-
for (let i = 0; i < args.length; i += 2) {
|
192
|
-
const condition = evaluate(args[i], env)
|
193
|
-
if (condition !== FALSE && condition !== TRUE)
|
194
|
-
throw new TypeError(
|
195
|
-
`Condition of (${
|
196
|
-
KEYWORDS.CONDITION
|
197
|
-
}) must be ${TRUE} or ${FALSE} but got (${
|
198
|
-
KEYWORDS.CONDITION
|
199
|
-
} ${stringifyArgs(args)})`
|
200
|
-
)
|
201
|
-
if (condition) return evaluate(args[i + 1], env)
|
202
|
-
}
|
203
|
-
return 0
|
204
|
-
},
|
205
174
|
[KEYWORDS.ARRAY_TYPE]: (args, env) => {
|
206
175
|
return args.length ? args.map((x) => evaluate(x, env)) : []
|
207
176
|
},
|
@@ -651,29 +620,6 @@ export const keywords = {
|
|
651
620
|
)
|
652
621
|
return operands.reduce((acc, x) => acc >>> x)
|
653
622
|
},
|
654
|
-
// [KEYWORDS.PIPE]: (args, env) => {
|
655
|
-
// if (args.length < 1)
|
656
|
-
// throw new RangeError(
|
657
|
-
// `Invalid number of arguments to (${KEYWORDS.PIPE}) (>= 1 required). (${
|
658
|
-
// KEYWORDS.PIPE
|
659
|
-
// } ${stringifyArgs(args)})`
|
660
|
-
// )
|
661
|
-
// let inp = args[0]
|
662
|
-
// for (let i = 1; i < args.length; ++i) {
|
663
|
-
// if (!args[i].length || args[i][0][TYPE] !== APPLY)
|
664
|
-
// throw new TypeError(
|
665
|
-
// `Argument at position (${i}) of (${
|
666
|
-
// KEYWORDS.PIPE
|
667
|
-
// }) is not an invoked (${KEYWORDS.ANONYMOUS_FUNCTION}). (${
|
668
|
-
// KEYWORDS.PIPE
|
669
|
-
// } ${stringifyArgs(args)})`
|
670
|
-
// )
|
671
|
-
// const [first, ...rest] = args[i]
|
672
|
-
// const arr = [first, inp, ...rest]
|
673
|
-
// inp = arr
|
674
|
-
// }
|
675
|
-
// return evaluate(inp, env)
|
676
|
-
// },
|
677
623
|
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
678
624
|
if (args.length !== 1 && args.length !== 3)
|
679
625
|
throw new RangeError(
|
package/src/keywords.js
CHANGED
@@ -6,6 +6,19 @@ export const ATOM = 2
|
|
6
6
|
export const TRUE = 1
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
|
+
export const SUGGAR = {
|
10
|
+
// Syntactic suggars
|
11
|
+
PIPE: '|>',
|
12
|
+
NOT_EQUAL_1: '!=',
|
13
|
+
NOT_EQUAL_2: '<>',
|
14
|
+
UNLESS: 'unless',
|
15
|
+
LIST_TYPE: 'list',
|
16
|
+
POWER: '**',
|
17
|
+
INTEGER_DEVISION: '//',
|
18
|
+
CONDITION: 'cond',
|
19
|
+
RECURSION: 'recursive',
|
20
|
+
CACHE: 'memoized'
|
21
|
+
}
|
9
22
|
export const KEYWORDS = {
|
10
23
|
NUMBER_TYPE: 'number',
|
11
24
|
ARRAY_TYPE: 'array',
|
@@ -29,7 +42,6 @@ export const KEYWORDS = {
|
|
29
42
|
BLOCK: 'do',
|
30
43
|
ANONYMOUS_FUNCTION: 'lambda',
|
31
44
|
IF: 'if',
|
32
|
-
CONDITION: 'cond',
|
33
45
|
NOT: 'not',
|
34
46
|
EQUAL: '=',
|
35
47
|
LESS_THAN: '<',
|
@@ -49,17 +61,7 @@ export const KEYWORDS = {
|
|
49
61
|
LOG_CHAR: 'log-char!',
|
50
62
|
CLEAR_CONSOLE: 'clear!',
|
51
63
|
|
52
|
-
THROW: 'throw'
|
53
|
-
|
54
|
-
// Syntactic suggars
|
55
|
-
PIPE: '|>',
|
56
|
-
NOT_EQUAL_1: '!=',
|
57
|
-
NOT_EQUAL_2: '<>',
|
58
|
-
UNLESS: 'unless',
|
59
|
-
LIST_TYPE: 'list',
|
60
|
-
|
61
|
-
RECURSION: 'recursive',
|
62
|
-
CACHE: 'memoized'
|
64
|
+
THROW: 'throw'
|
63
65
|
}
|
64
66
|
|
65
67
|
export const TYPES = {
|
package/src/utils.js
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
2
|
import { comp } from './compiler.js'
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
APPLY,
|
5
|
+
ATOM,
|
6
|
+
FALSE,
|
7
|
+
KEYWORDS,
|
8
|
+
SUGGAR,
|
9
|
+
TRUE,
|
10
|
+
TYPE,
|
11
|
+
VALUE,
|
12
|
+
WORD
|
13
|
+
} from './keywords.js'
|
4
14
|
import { evaluate, run } from './evaluator.js'
|
5
15
|
import { AST, isLeaf, LISP } from './parser.js'
|
6
16
|
export const logError = (error) =>
|
@@ -102,8 +112,8 @@ export const isForbiddenVariableName = (name) => {
|
|
102
112
|
switch (name) {
|
103
113
|
case '_':
|
104
114
|
case KEYWORDS.DEFINE_VARIABLE:
|
105
|
-
case
|
106
|
-
case
|
115
|
+
case SUGGAR.RECURSION:
|
116
|
+
case SUGGAR.CACHE:
|
107
117
|
return true
|
108
118
|
default:
|
109
119
|
return !isNaN(name[0])
|
@@ -385,8 +395,8 @@ export const deSuggar = (ast) => {
|
|
385
395
|
prev[TYPE] === APPLY &&
|
386
396
|
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
387
397
|
) {
|
388
|
-
exp[0][
|
389
|
-
exp[0][
|
398
|
+
exp[0][VALUE] = KEYWORDS.CALL_FUNCTION
|
399
|
+
exp[0][TYPE] = APPLY
|
390
400
|
exp.length = 1
|
391
401
|
exp[1] = [
|
392
402
|
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
@@ -410,13 +420,13 @@ export const deSuggar = (ast) => {
|
|
410
420
|
// }
|
411
421
|
// }
|
412
422
|
// break
|
413
|
-
case
|
423
|
+
case SUGGAR.PIPE:
|
414
424
|
{
|
415
425
|
if (rest.length < 1)
|
416
426
|
throw new RangeError(
|
417
427
|
`Invalid number of arguments to (${
|
418
|
-
|
419
|
-
}) (>= 1 required). (${
|
428
|
+
SUGGAR.PIPE
|
429
|
+
}) (>= 1 required). (${SUGGAR.PIPE} ${stringifyArgs(
|
420
430
|
rest
|
421
431
|
)})`
|
422
432
|
)
|
@@ -426,10 +436,10 @@ export const deSuggar = (ast) => {
|
|
426
436
|
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
427
437
|
throw new TypeError(
|
428
438
|
`Argument at position (${i}) of (${
|
429
|
-
|
439
|
+
SUGGAR.PIPE
|
430
440
|
}) is not an invoked (${
|
431
441
|
KEYWORDS.ANONYMOUS_FUNCTION
|
432
|
-
}). (${
|
442
|
+
}). (${SUGGAR.PIPE} ${stringifyArgs(rest)})`
|
433
443
|
)
|
434
444
|
inp = [rest[i].shift(), inp, ...rest[i]]
|
435
445
|
}
|
@@ -437,51 +447,139 @@ export const deSuggar = (ast) => {
|
|
437
447
|
deSuggar(exp)
|
438
448
|
}
|
439
449
|
break
|
440
|
-
case
|
450
|
+
case SUGGAR.CONDITION:
|
451
|
+
{
|
452
|
+
if (rest.length < 2)
|
453
|
+
throw new RangeError(
|
454
|
+
`Invalid number of arguments for (${
|
455
|
+
SUGGAR.CONDITION
|
456
|
+
}), expected (> 2 required) but got ${rest.length} (${
|
457
|
+
SUGGAR.CONDITION
|
458
|
+
} ${stringifyArgs(rest)})`
|
459
|
+
)
|
460
|
+
if (rest.length % 2 !== 0)
|
461
|
+
throw new RangeError(
|
462
|
+
`Invalid number of arguments for (${
|
463
|
+
SUGGAR.CONDITION
|
464
|
+
}), expected even number of arguments but got ${
|
465
|
+
rest.length
|
466
|
+
} (${SUGGAR.CONDITION} ${stringifyArgs(rest)})`
|
467
|
+
)
|
468
|
+
exp.length = 0
|
469
|
+
let temp = exp
|
470
|
+
for (let i = 0; i < rest.length; i += 2) {
|
471
|
+
if (i === rest.length - 2) {
|
472
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest.at(-1))
|
473
|
+
} else {
|
474
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest[i + 1], [])
|
475
|
+
temp = temp.at(-1)
|
476
|
+
}
|
477
|
+
}
|
478
|
+
deSuggar(exp)
|
479
|
+
}
|
480
|
+
break
|
481
|
+
case SUGGAR.LIST_TYPE:
|
441
482
|
{
|
442
483
|
exp.length = 0
|
443
484
|
let temp = exp
|
444
485
|
for (const item of rest) {
|
445
|
-
temp.push([
|
486
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE], item, [])
|
446
487
|
temp = temp.at(-1)
|
447
488
|
}
|
448
|
-
temp.push([
|
489
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE])
|
449
490
|
deSuggar(exp)
|
450
491
|
}
|
451
492
|
break
|
493
|
+
case SUGGAR.INTEGER_DEVISION:
|
494
|
+
{
|
495
|
+
if (rest.some((x) => x[TYPE] === APPLY))
|
496
|
+
throw new TypeError(
|
497
|
+
`Arguments of (${
|
498
|
+
SUGGAR.INTEGER_DEVISION
|
499
|
+
}), must be (or atom word) (hint use (math:floor (${
|
500
|
+
KEYWORDS.DIVISION
|
501
|
+
} a b)) instead) (${
|
502
|
+
SUGGAR.INTEGER_DEVISION
|
503
|
+
} ${stringifyArgs(rest)})`
|
504
|
+
)
|
505
|
+
else {
|
506
|
+
exp.length = 1
|
507
|
+
exp[0] = [APPLY, KEYWORDS.BITWISE_OR]
|
508
|
+
exp.push([[APPLY, KEYWORDS.DIVISION], ...rest])
|
509
|
+
exp.push([ATOM, FALSE])
|
510
|
+
}
|
511
|
+
}
|
512
|
+
break
|
513
|
+
case SUGGAR.POWER:
|
514
|
+
{
|
515
|
+
if (rest.length !== 2)
|
516
|
+
throw new RangeError(
|
517
|
+
`Invalid number of arguments for (${
|
518
|
+
SUGGAR.POWER
|
519
|
+
}), expected (= 2) but got ${rest.length} (${
|
520
|
+
SUGGAR.POWER
|
521
|
+
} ${stringifyArgs(rest)})`
|
522
|
+
)
|
523
|
+
const isExponentAtom = exp[1][TYPE] === ATOM
|
524
|
+
const isPowerAtom = exp[2][TYPE] === ATOM
|
525
|
+
const isExponentWord = exp[1][TYPE] === WORD
|
526
|
+
if ((isExponentWord || isExponentAtom) && isPowerAtom) {
|
527
|
+
exp[0][VALUE] = KEYWORDS.MULTIPLICATION
|
528
|
+
const exponent = exp[1]
|
529
|
+
const power = exp[2][VALUE]
|
530
|
+
exp.length = 1
|
531
|
+
if (isExponentAtom) {
|
532
|
+
exp.push(exponent, [ATOM, exponent[VALUE] ** (power - 1)])
|
533
|
+
} else if (isExponentWord) {
|
534
|
+
exp.push(
|
535
|
+
...Array.from({ length: power })
|
536
|
+
.fill(0)
|
537
|
+
.map(() => [exponent[TYPE], exponent[VALUE]])
|
538
|
+
)
|
539
|
+
}
|
540
|
+
} else
|
541
|
+
throw new TypeError(
|
542
|
+
`Second Arguments of (${
|
543
|
+
SUGGAR.POWER
|
544
|
+
}), must be (atom) (hint use math:power instead) (${
|
545
|
+
SUGGAR.POWER
|
546
|
+
} ${stringifyArgs(rest)})`
|
547
|
+
)
|
548
|
+
}
|
549
|
+
break
|
452
550
|
case KEYWORDS.MULTIPLICATION:
|
453
551
|
if (!rest.length) {
|
454
|
-
exp[0][
|
455
|
-
exp[0][
|
552
|
+
exp[0][TYPE] = ATOM
|
553
|
+
exp[0][VALUE] = TRUE
|
456
554
|
}
|
457
555
|
break
|
458
556
|
case KEYWORDS.ADDITION:
|
459
557
|
case KEYWORDS.DIVISION:
|
460
558
|
if (!rest.length) {
|
461
|
-
exp[0][
|
462
|
-
exp[0][
|
559
|
+
exp[0][TYPE] = ATOM
|
560
|
+
exp[0][VALUE] = FALSE
|
463
561
|
}
|
464
562
|
break
|
465
|
-
case
|
563
|
+
case SUGGAR.UNLESS:
|
466
564
|
{
|
467
565
|
if (rest.length > 3 || rest.length < 2)
|
468
566
|
throw new RangeError(
|
469
567
|
`Invalid number of arguments for (${
|
470
|
-
|
568
|
+
SUGGAR.UNLESS
|
471
569
|
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
472
|
-
|
570
|
+
SUGGAR.UNLESS
|
473
571
|
} ${stringifyArgs(rest)})`
|
474
572
|
)
|
475
|
-
exp[0][
|
573
|
+
exp[0][VALUE] = KEYWORDS.IF
|
476
574
|
const temp = exp[2]
|
477
|
-
exp[2] = exp[3] ?? [ATOM,
|
575
|
+
exp[2] = exp[3] ?? [ATOM, FALSE]
|
478
576
|
exp[3] = temp
|
479
577
|
}
|
480
578
|
deSuggar(exp)
|
481
579
|
break
|
482
580
|
|
483
|
-
case
|
484
|
-
case
|
581
|
+
case SUGGAR.NOT_EQUAL_1:
|
582
|
+
case SUGGAR.NOT_EQUAL_2:
|
485
583
|
{
|
486
584
|
if (rest.length > 3 || rest.length < 2)
|
487
585
|
throw new RangeError(
|
@@ -491,7 +589,7 @@ export const deSuggar = (ast) => {
|
|
491
589
|
exp[0][1]
|
492
590
|
} ${stringifyArgs(rest)})`
|
493
591
|
)
|
494
|
-
exp[0][
|
592
|
+
exp[0][VALUE] = KEYWORDS.NOT
|
495
593
|
exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
|
496
594
|
exp.length = 2
|
497
595
|
deSuggar(exp)
|