fez-lisp 1.4.10 → 1.4.12
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/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/compiler.js +32 -29
- package/src/interpreter.js +6 -0
- package/src/keywords.js +2 -2
- package/src/macros.js +116 -0
- package/src/utils.js +3 -2
package/package.json
CHANGED
package/src/compiler.js
CHANGED
@@ -7,18 +7,15 @@ import {
|
|
7
7
|
VALUE,
|
8
8
|
WORD
|
9
9
|
} from './keywords.js'
|
10
|
+
import { OPTIMIZATIONS } from './macros.js'
|
10
11
|
import { leaf, isLeaf, AST } from './parser.js'
|
11
|
-
export const OPTIMIZATIONS = {
|
12
|
-
RECURSION: 'recursive',
|
13
|
-
CACHE: 'memoized'
|
14
|
-
}
|
15
12
|
const deepRename = (name, newName, tree) => {
|
16
13
|
if (!isLeaf(tree))
|
17
14
|
for (const leaf of tree) {
|
18
15
|
// Figure out a non mutable solution so
|
19
16
|
// I can get rid of deep clone AST.parse(AST.stringify(ast))
|
20
17
|
if (leaf[VALUE] === name) leaf[VALUE] = newName
|
21
|
-
deepRename(name, newName, leaf)
|
18
|
+
else deepRename(name, newName, leaf)
|
22
19
|
}
|
23
20
|
}
|
24
21
|
const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
|
@@ -181,30 +178,30 @@ const comp = (tree, Drill) => {
|
|
181
178
|
case KEYWORDS.DEFINE_VARIABLE: {
|
182
179
|
const n = tail[0][VALUE]
|
183
180
|
const prefix = n.split(':')[0]
|
184
|
-
if (prefix === OPTIMIZATIONS.RECURSION) {
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
181
|
+
// if (prefix === OPTIMIZATIONS.RECURSION) {
|
182
|
+
// const name = lispToJavaScriptVariableName(n)
|
183
|
+
// const newName = `${OPTIMIZATIONS.RECURSION}_${performance
|
184
|
+
// .now()
|
185
|
+
// .toString()
|
186
|
+
// .replace('.', 7)}`
|
187
|
+
// Drill.Variables.add(name)
|
188
|
+
// Drill.Variables.add(newName)
|
189
|
+
// Drill.Helpers.add('__tco')
|
190
|
+
// const functionArgs = tail.at(-1).slice(1)
|
191
|
+
// const body = functionArgs.pop()
|
192
|
+
// const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
|
193
|
+
// deepRename(n, `()=>${newName}`, body)
|
194
|
+
// const evaluatedBody = comp(body, FunctionDrill)
|
195
|
+
// const vars = FunctionDrill.Variables.size
|
196
|
+
// ? `var ${[...FunctionDrill.Variables].join(',')};`
|
197
|
+
// : ''
|
198
|
+
// return `(${name}=(__tco(${newName}=(${parseArgs(
|
199
|
+
// functionArgs,
|
200
|
+
// Drill
|
201
|
+
// )})=>{${vars}return ${evaluatedBody.toString().trim()}})));`
|
202
|
+
// } else
|
203
|
+
|
204
|
+
if (prefix === OPTIMIZATIONS.CACHE) {
|
208
205
|
// memoization here
|
209
206
|
const name = lispToJavaScriptVariableName(n)
|
210
207
|
const newName = name.substring(OPTIMIZATIONS.CACHE.length + 1)
|
@@ -308,6 +305,12 @@ const comp = (tree, Drill) => {
|
|
308
305
|
tail.length === 3 ? comp(tail[2], Drill) : 0
|
309
306
|
});`
|
310
307
|
}
|
308
|
+
case KEYWORDS.LOOP: {
|
309
|
+
return `(()=>{while(${comp(tail[0], Drill)}){${comp(
|
310
|
+
tail[1],
|
311
|
+
Drill
|
312
|
+
)}}return 0})();`
|
313
|
+
}
|
311
314
|
case KEYWORDS.ERROR: {
|
312
315
|
Drill.Helpers.add('__error')
|
313
316
|
return `__error(${compile(tail[0], Drill)})`
|
package/src/interpreter.js
CHANGED
@@ -13,6 +13,12 @@ import { evaluate } from './evaluator.js'
|
|
13
13
|
import { isForbiddenVariableName, stringifyArgs } from './utils.js'
|
14
14
|
import { LISP } from './parser.js'
|
15
15
|
export const keywords = {
|
16
|
+
[KEYWORDS.LOOP]: (args, env) => {
|
17
|
+
if (args.length != 2)
|
18
|
+
throw new RangeError(`Wrong number of args to ${KEYWORDS.LOOP}`)
|
19
|
+
while (evaluate(args[0], env) !== FALSE) evaluate(args[1], env)
|
20
|
+
return FALSE
|
21
|
+
},
|
16
22
|
[KEYWORDS.ADDITION]: (args, env) => {
|
17
23
|
if (args.length !== 2)
|
18
24
|
throw new RangeError(
|
package/src/keywords.js
CHANGED
@@ -7,6 +7,7 @@ export const TRUE = 1
|
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
9
|
export const KEYWORDS = {
|
10
|
+
LOOP: 'loop',
|
10
11
|
CREATE_ARRAY: 'array',
|
11
12
|
ARRAY_LENGTH: 'length',
|
12
13
|
IS_ATOM: 'atom?',
|
@@ -40,7 +41,7 @@ export const KEYWORDS = {
|
|
40
41
|
DEFINE_VARIABLE: 'let',
|
41
42
|
|
42
43
|
SET_ARRAY: 'set!',
|
43
|
-
ERROR: 'throw'
|
44
|
+
ERROR: 'throw'
|
44
45
|
}
|
45
46
|
|
46
47
|
export const TYPES = {
|
@@ -56,7 +57,6 @@ export const DEBUG = {
|
|
56
57
|
LOG: 'log',
|
57
58
|
ASSERT: 'assert',
|
58
59
|
CALLSTACK: '(CALLSTACK)' // so that you can't use it in the code
|
59
|
-
|
60
60
|
}
|
61
61
|
|
62
62
|
export const SPECIAL_FORMS_SET = new Set(Object.values(KEYWORDS))
|
package/src/macros.js
CHANGED
@@ -29,6 +29,19 @@ export const SUGGAR = {
|
|
29
29
|
INTEGER_DEVISION: '//',
|
30
30
|
CONDITION: 'cond'
|
31
31
|
}
|
32
|
+
export const OPTIMIZATIONS = {
|
33
|
+
RECURSION: 'recursive',
|
34
|
+
CACHE: 'memoized'
|
35
|
+
}
|
36
|
+
const deepTransform = (predicate, transform, tree) => {
|
37
|
+
if (!isLeaf(tree))
|
38
|
+
for (const leaf of tree) {
|
39
|
+
// Figure out a non mutable solution so
|
40
|
+
// I can get rid of deep clone AST.parse(AST.stringify(ast))
|
41
|
+
if (predicate(leaf)) transform(leaf)
|
42
|
+
else deepTransform(predicate, transform, leaf)
|
43
|
+
}
|
44
|
+
}
|
32
45
|
export const deSuggarAst = (ast, scope) => {
|
33
46
|
if (scope === undefined) scope = ast
|
34
47
|
if (ast.length === 0) throw new SyntaxError(`No expressions...`)
|
@@ -489,6 +502,109 @@ export const deSuggarAst = (ast, scope) => {
|
|
489
502
|
exp.iron = true
|
490
503
|
exp.push(newScope)
|
491
504
|
deSuggarAst(scope)
|
505
|
+
} else {
|
506
|
+
const last = exp.at(-1)
|
507
|
+
if (
|
508
|
+
!isLeaf(last) &&
|
509
|
+
Array.isArray(last) &&
|
510
|
+
last[0] &&
|
511
|
+
last[0][TYPE] === APPLY &&
|
512
|
+
last[0][VALUE] === KEYWORDS.ANONYMOUS_FUNCTION
|
513
|
+
) {
|
514
|
+
const name = exp[1][VALUE]
|
515
|
+
const prefix = name.split(':')[0]
|
516
|
+
if (prefix === OPTIMIZATIONS.RECURSION) {
|
517
|
+
const args = last.slice(1, -1)
|
518
|
+
const newName = `*${performance
|
519
|
+
.now()
|
520
|
+
.toString()
|
521
|
+
.replace('.', 0)}*`
|
522
|
+
deepTransform(
|
523
|
+
(leaf) =>
|
524
|
+
Array.isArray(leaf) &&
|
525
|
+
leaf[0] &&
|
526
|
+
leaf[0][TYPE] === APPLY &&
|
527
|
+
leaf[0][VALUE] === name,
|
528
|
+
(leaf) => {
|
529
|
+
const copy = [...leaf]
|
530
|
+
leaf.length = 0
|
531
|
+
copy[0][VALUE] = newName
|
532
|
+
leaf.push(
|
533
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
534
|
+
copy
|
535
|
+
)
|
536
|
+
},
|
537
|
+
last
|
538
|
+
)
|
539
|
+
exp[exp.length - 1] = [
|
540
|
+
[APPLY, KEYWORDS.CALL_FUNCTION],
|
541
|
+
[
|
542
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
543
|
+
[
|
544
|
+
[APPLY, KEYWORDS.BLOCK],
|
545
|
+
[
|
546
|
+
[APPLY, KEYWORDS.DEFINE_VARIABLE],
|
547
|
+
[WORD, newName],
|
548
|
+
last
|
549
|
+
],
|
550
|
+
[
|
551
|
+
[APPLY, KEYWORDS.CALL_FUNCTION],
|
552
|
+
[WORD, newName],
|
553
|
+
[
|
554
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
555
|
+
[WORD, '*fn*'],
|
556
|
+
[
|
557
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
558
|
+
...args,
|
559
|
+
[
|
560
|
+
[APPLY, KEYWORDS.BLOCK],
|
561
|
+
[
|
562
|
+
[APPLY, KEYWORDS.DEFINE_VARIABLE],
|
563
|
+
[WORD, '*res*'],
|
564
|
+
[
|
565
|
+
[APPLY, KEYWORDS.CREATE_ARRAY],
|
566
|
+
[[APPLY, '*fn*'], ...args]
|
567
|
+
]
|
568
|
+
],
|
569
|
+
[
|
570
|
+
[APPLY, KEYWORDS.LOOP],
|
571
|
+
[
|
572
|
+
[APPLY, KEYWORDS.IS_LAMBDA],
|
573
|
+
[
|
574
|
+
[APPLY, KEYWORDS.GET_ARRAY],
|
575
|
+
[WORD, '*res*'],
|
576
|
+
[ATOM, 0]
|
577
|
+
]
|
578
|
+
],
|
579
|
+
[
|
580
|
+
[APPLY, KEYWORDS.SET_ARRAY],
|
581
|
+
[WORD, '*res*'],
|
582
|
+
[ATOM, 0],
|
583
|
+
[
|
584
|
+
[APPLY, KEYWORDS.CALL_FUNCTION],
|
585
|
+
[
|
586
|
+
[APPLY, KEYWORDS.GET_ARRAY],
|
587
|
+
[WORD, '*res*'],
|
588
|
+
[ATOM, 0]
|
589
|
+
]
|
590
|
+
]
|
591
|
+
]
|
592
|
+
],
|
593
|
+
[
|
594
|
+
[APPLY, KEYWORDS.GET_ARRAY],
|
595
|
+
[WORD, '*res*'],
|
596
|
+
[ATOM, 0]
|
597
|
+
]
|
598
|
+
]
|
599
|
+
]
|
600
|
+
]
|
601
|
+
]
|
602
|
+
]
|
603
|
+
]
|
604
|
+
]
|
605
|
+
deSuggarAst(exp[exp.length - 1])
|
606
|
+
}
|
607
|
+
}
|
492
608
|
}
|
493
609
|
}
|
494
610
|
break
|
package/src/utils.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
|
-
import { compile
|
2
|
+
import { compile } from './compiler.js'
|
3
3
|
import {
|
4
4
|
APPLY,
|
5
5
|
ATOM,
|
@@ -17,7 +17,8 @@ import { isLeaf, LISP } from './parser.js'
|
|
17
17
|
import {
|
18
18
|
deSuggarAst,
|
19
19
|
deSuggarSource,
|
20
|
-
handleUnbalancedQuotes
|
20
|
+
handleUnbalancedQuotes,
|
21
|
+
OPTIMIZATIONS
|
21
22
|
} from './macros.js'
|
22
23
|
import { keywords } from './interpreter.js'
|
23
24
|
export const logError = (error) =>
|