fez-lisp 1.5.201 → 1.6.0
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 +228 -14
- package/lib/baked/macros.js +3 -3
- package/lib/baked/std.js +1 -1
- package/package.json +2 -2
- package/src/check.js +45 -41
- package/src/enhance.js +2 -2
- package/src/utils.js +1 -1
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "fez-lisp",
|
3
3
|
"description": "Lisp interpreted & compiled to JavaScript",
|
4
4
|
"author": "AT290690",
|
5
|
-
"version": "1.
|
5
|
+
"version": "1.6.0",
|
6
6
|
"type": "module",
|
7
7
|
"main": "index.js",
|
8
8
|
"keywords": [
|
@@ -25,7 +25,7 @@
|
|
25
25
|
"build": "node ./lib/builder.js",
|
26
26
|
"fez": "node main.js",
|
27
27
|
"test": "mocha",
|
28
|
-
"examples": "node ./examples/2015/test.spec.js && node ./examples/2019/test.spec.js && node ./examples/2020/test.spec.js && node ./examples/2021/test.spec.js && node ./examples/2023/test.spec.js && node ./examples/2024/test.spec.js"
|
28
|
+
"examples": "node ./examples/leetcode/test.spec.js && node ./examples/2015/test.spec.js && node ./examples/2019/test.spec.js && node ./examples/2020/test.spec.js && node ./examples/2021/test.spec.js && node ./examples/2023/test.spec.js && node ./examples/2024/test.spec.js"
|
29
29
|
},
|
30
30
|
"license": "MIT",
|
31
31
|
"devDependencies": {
|
package/src/check.js
CHANGED
@@ -215,7 +215,7 @@ export const setTypeToCollection = (stats) =>
|
|
215
215
|
(stats[TYPE_PROP][0] = COLLECTION)
|
216
216
|
export const setTypeToAbstraction = (stats) =>
|
217
217
|
(isUnknownType(stats) || isAnyType(stats)) && (stats[TYPE_PROP][0] = APPLY)
|
218
|
-
export const
|
218
|
+
export const setReturnToAbstraction = (stats) =>
|
219
219
|
isUnknownReturn(stats) && (stats[RETURNS][0] = APPLY)
|
220
220
|
export const setTypeRef = (stats, value) =>
|
221
221
|
(isUnknownType(stats) || isAnyType(stats)) &&
|
@@ -238,8 +238,8 @@ export const setTypeToReturnRef = (stats, value) => {
|
|
238
238
|
// Change main type if unknown
|
239
239
|
if (isUnknownType(stats) || isAnyType(stats))
|
240
240
|
if (!isUnknownReturn(value)) stats[TYPE_PROP][0] = value[RETURNS][0]
|
241
|
-
// TODO: Figure out what we do if generic
|
242
|
-
//
|
241
|
+
// TODO: Figure out what we do if generic things get inferred
|
242
|
+
// change sub type if it doesn't have
|
243
243
|
if (!hasSubType(stats) || getSubType(stats).has(UNKNOWN))
|
244
244
|
if (hasSubReturn(value)) stats[TYPE_PROP][1] = value[RETURNS][1]
|
245
245
|
}
|
@@ -263,13 +263,13 @@ export const setType = (stats, value) =>
|
|
263
263
|
((stats[TYPE_PROP][0] = value[TYPE_PROP][0]),
|
264
264
|
value[TYPE_PROP][1] && (stats[TYPE_PROP][1] = value[TYPE_PROP][1]))
|
265
265
|
export const setSubType = (stats, value) =>
|
266
|
-
// makes no
|
266
|
+
// makes no sense to protect this for now
|
267
267
|
hasSubType(value) && (stats[TYPE_PROP][1] = value[TYPE_PROP][1])
|
268
268
|
export const setPropToSubType = (stats, prop, value) =>
|
269
|
-
// makes no
|
269
|
+
// makes no sense to protect this for now
|
270
270
|
hasSubType(value) && (stats[prop][1] = value[TYPE_PROP][1])
|
271
271
|
export const setPropToSubReturn = (stats, prop, value) =>
|
272
|
-
// makes no
|
272
|
+
// makes no sense to protect this for now
|
273
273
|
hasSubReturn(value) && (stats[prop][1] = value[RETURNS][1])
|
274
274
|
export const setTypeToReturn = (stats, value) =>
|
275
275
|
(isUnknownType(stats) || isAnyType(stats)) &&
|
@@ -297,11 +297,18 @@ export const getSubType = (stats) => stats && stats[TYPE_PROP][1]
|
|
297
297
|
export const hasSubType = (stats) => stats && stats[TYPE_PROP][1] instanceof Set
|
298
298
|
export const getSubReturn = (stats) => stats && stats[RETURNS][1]
|
299
299
|
export const hasSubReturn = (stats) => stats && stats[RETURNS][1] instanceof Set
|
300
|
-
export const
|
300
|
+
export const isUnknownSubReturn = (stats) =>
|
301
301
|
!hasSubReturn(stats) ||
|
302
|
-
(stats
|
303
|
-
|
302
|
+
(stats &&
|
303
|
+
stats[RETURNS] &&
|
304
|
+
stats[RETURNS][1] &&
|
305
|
+
stats[RETURNS][1].size === 1 &&
|
306
|
+
stats[RETURNS][1].has(UNKNOWN))
|
307
|
+
export const isUnknownSubType = (stats) =>
|
304
308
|
hasSubType(stats) &&
|
309
|
+
stats &&
|
310
|
+
stats[TYPE_PROP] &&
|
311
|
+
stats[TYPE_PROP][1] &&
|
305
312
|
stats[TYPE_PROP][1].size === 1 &&
|
306
313
|
stats[TYPE_PROP][1].has(UNKNOWN)
|
307
314
|
export const isAtomType = (stats) =>
|
@@ -315,7 +322,7 @@ export const equalTypes = (a, b) => {
|
|
315
322
|
if (!isSameType) return false
|
316
323
|
return true
|
317
324
|
}
|
318
|
-
const
|
325
|
+
const isRedefinedInLambda = (env, name, exp) => {
|
319
326
|
if (exp.slice(1, -1).some((x) => x[VALUE] === name)) return true
|
320
327
|
else if (
|
321
328
|
exp
|
@@ -424,12 +431,12 @@ const checkPredicateNameDeep = (name, exp, rest, returns) => {
|
|
424
431
|
[WORD, name],
|
425
432
|
isLeaf(fn)
|
426
433
|
? fn // when apply is a word (let x? (lambda (apply [] array:empty!)))
|
427
|
-
: drillReturnType(fn, (r) => r[VALUE] === KEYWORDS.CALL_FUNCTION) // when apply is an
|
434
|
+
: drillReturnType(fn, (r) => r[VALUE] === KEYWORDS.CALL_FUNCTION) // when apply is an anonymous lambda // (let fn? (lambda x (apply x (lambda x (array:empty! [])))))
|
428
435
|
])
|
429
436
|
}
|
430
437
|
return checkPredicateName(exp, [[WORD, name], returns])
|
431
438
|
}
|
432
|
-
const
|
439
|
+
const fillUnknownArgs = (n) =>
|
433
440
|
Array.from({ length: n })
|
434
441
|
.fill(null)
|
435
442
|
.map(() => ({
|
@@ -510,7 +517,7 @@ const IfApplyBranch = ({
|
|
510
517
|
setPropToAbstraction(ref[STATS], prop)
|
511
518
|
ref[STATS][RETURNS] = [UNKNOWN]
|
512
519
|
ref[STATS][ARG_COUNT] = re.length - 2
|
513
|
-
ref[STATS][ARGUMENTS] =
|
520
|
+
ref[STATS][ARGUMENTS] = fillUnknownArgs(re.length - 2)
|
514
521
|
break
|
515
522
|
case KEYWORDS.CALL_FUNCTION:
|
516
523
|
if (re.at(-1)[TYPE] === WORD) {
|
@@ -538,14 +545,14 @@ const IfApplyBranch = ({
|
|
538
545
|
const ifExpression = ({ re, env, ref, prop, stack, exp, check }) => {
|
539
546
|
if (re[0][TYPE] === ATOM || re[1][TYPE] === ATOM)
|
540
547
|
return setPropToAtom(ref[STATS], prop)
|
541
|
-
// TODO check that both
|
548
|
+
// TODO check that both branches are predicates if one is
|
542
549
|
else {
|
543
550
|
const conc = isLeaf(re[0]) ? re[0] : re[0][0]
|
544
551
|
const alt = isLeaf(re[1]) ? re[1] : re[1][0]
|
545
552
|
const concequent = env[conc[VALUE]]
|
546
553
|
const alternative = env[alt[VALUE]]
|
547
554
|
// TODO make this more simple - it's so many different things just because types are functions or not
|
548
|
-
// WHY not
|
555
|
+
// WHY not consider making return types for everything
|
549
556
|
if (concequent)
|
550
557
|
if (conc[TYPE] === WORD) {
|
551
558
|
return setPropToTypeRef(ref[STATS], prop, concequent[STATS])
|
@@ -869,8 +876,7 @@ const resolveReturnType = ({
|
|
869
876
|
return true
|
870
877
|
} else if (!env[returns[VALUE]]) return false
|
871
878
|
else if (getType(env[returns[VALUE]][STATS]) === APPLY) {
|
872
|
-
if (returns[TYPE] === WORD)
|
873
|
-
setReturnToAbbstraction(env[name][STATS])
|
879
|
+
if (returns[TYPE] === WORD) setReturnToAbstraction(env[name][STATS])
|
874
880
|
else {
|
875
881
|
// ALWAYS APPLY
|
876
882
|
// rest.at(-1)[0][TYPE] === APPLY
|
@@ -897,9 +903,9 @@ const resolveReturnType = ({
|
|
897
903
|
case KEYWORDS.ANONYMOUS_FUNCTION:
|
898
904
|
{
|
899
905
|
// TODO figure out a better way to do this
|
900
|
-
// This is
|
906
|
+
// This is initialization of identity or any other
|
901
907
|
// function that returns it's argument
|
902
|
-
//
|
908
|
+
// Redefine the variable but since it's an error doing that
|
903
909
|
// Delete it first
|
904
910
|
delete env[name]
|
905
911
|
check(
|
@@ -914,7 +920,7 @@ const resolveReturnType = ({
|
|
914
920
|
// const n = genericReturn.length
|
915
921
|
// setTypeToAbstraction(env[name][STATS])
|
916
922
|
// env[name][STATS][ARG_COUNT] = n - 2
|
917
|
-
// env[name][STATS][ARGUMENTS] =
|
923
|
+
// env[name][STATS][ARGUMENTS] = fillUnknownArgs(
|
918
924
|
// n - 2
|
919
925
|
// )
|
920
926
|
// checkReturnType({
|
@@ -1055,7 +1061,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1055
1061
|
)
|
1056
1062
|
// TODO delete this maybe
|
1057
1063
|
// #C2
|
1058
|
-
// It will not be
|
1064
|
+
// It will not be possible to know return type
|
1059
1065
|
const match1 = () => {
|
1060
1066
|
const actual = local[lambdaName]
|
1061
1067
|
const expected = args[i]
|
@@ -1165,7 +1171,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1165
1171
|
case WORD:
|
1166
1172
|
if (!isSpecial)
|
1167
1173
|
stagger(stack, 'append', [first, env], () => {
|
1168
|
-
// Figure out how to determine if
|
1174
|
+
// Figure out how to determine if variable is define after it's used
|
1169
1175
|
if (env[first[VALUE]] === undefined) {
|
1170
1176
|
throw new TypeError(
|
1171
1177
|
`Trying to access undefined variable ${first[VALUE]} (check #11)`
|
@@ -1188,11 +1194,11 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1188
1194
|
exp
|
1189
1195
|
)})`
|
1190
1196
|
)
|
1191
|
-
// TODO check
|
1197
|
+
// TODO check let define types
|
1192
1198
|
const name = rest[0][VALUE]
|
1193
1199
|
if (env.hasOwnProperty(name))
|
1194
1200
|
throw new ReferenceError(
|
1195
|
-
`Attempting to
|
1201
|
+
`Attempting to redeclare (${name}) which was previously declared in this scope (${stringifyArgs(
|
1196
1202
|
exp
|
1197
1203
|
)})`
|
1198
1204
|
)
|
@@ -1247,7 +1253,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1247
1253
|
retried: 0,
|
1248
1254
|
counter: 0,
|
1249
1255
|
[ARG_COUNT]: n - 2,
|
1250
|
-
[ARGUMENTS]:
|
1256
|
+
[ARGUMENTS]: fillUnknownArgs(n - 2),
|
1251
1257
|
[RETURNS]: [UNKNOWN]
|
1252
1258
|
}
|
1253
1259
|
}
|
@@ -1287,7 +1293,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1287
1293
|
})
|
1288
1294
|
}
|
1289
1295
|
resolve()
|
1290
|
-
if (
|
1296
|
+
if (isUnknownSubType(env[name][STATS]))
|
1291
1297
|
stagger(stack, 'prepend', exp, () => {
|
1292
1298
|
once(env[name][STATS], exp, stack, () => {
|
1293
1299
|
setPropToCollection(env[name][STATS], TYPE_PROP)
|
@@ -1303,12 +1309,12 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1303
1309
|
checkPredicateName(exp, rest)
|
1304
1310
|
const isLeafNode = isLeaf(rightHand)
|
1305
1311
|
if (isLeafNode && rightHand[TYPE] === WORD) {
|
1306
|
-
// TODO make sure this prevents the
|
1312
|
+
// TODO make sure this prevents the assignment all together
|
1307
1313
|
if (env[rest[1][VALUE]] === undefined)
|
1308
1314
|
throw new TypeError(
|
1309
1315
|
`Trying to access undefined variable ${rest[1][VALUE]} (check #22)`
|
1310
1316
|
)
|
1311
|
-
// Used to be
|
1317
|
+
// Used to be checking if it's an assignment to a special form
|
1312
1318
|
// but this should not cause problems
|
1313
1319
|
// env[name] = SPECIAL_FORMS_SET.has(rest[1][VALUE])
|
1314
1320
|
// ? structuredClone(env[rest[1][VALUE]])
|
@@ -1412,7 +1418,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1412
1418
|
if (isLeaf(returns)) {
|
1413
1419
|
// TODO figure out what we do here
|
1414
1420
|
// this here is a variable WORD
|
1415
|
-
// so return type of that function is that
|
1421
|
+
// so return type of that function is that variable type
|
1416
1422
|
switch (returns[TYPE]) {
|
1417
1423
|
case ATOM:
|
1418
1424
|
setReturnToAtom(ref[STATS])
|
@@ -1469,9 +1475,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1469
1475
|
// default:
|
1470
1476
|
// if (copy[ret[VALUE]]) {
|
1471
1477
|
// if (isUnknownReturn(copy[ret[VALUE]][STATS])) {
|
1472
|
-
// once(ref[STATS], [
|
1473
|
-
// setReturnRef(ref[STATS], copy[ret[VALUE]][STATS])
|
1474
|
-
// })
|
1478
|
+
// once(ref[STATS], copy[ret[VALUE]][STATS])
|
1475
1479
|
// } else setReturnRef(ref[STATS], copy[ret[VALUE]][STATS])
|
1476
1480
|
// } else
|
1477
1481
|
// stagger(stack, 'append', [ret, copy], () => {
|
@@ -1541,13 +1545,13 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1541
1545
|
}) (${stringifyArgs(exp)}) (check #12)`
|
1542
1546
|
)
|
1543
1547
|
else if (!env[first[VALUE]][STATS][ARG_COUNT]) {
|
1544
|
-
// TODO recursively take return type of
|
1548
|
+
// TODO recursively take return type of application
|
1545
1549
|
// FN ASSIGMENT
|
1546
|
-
// ASSIGMENT of
|
1550
|
+
// ASSIGMENT of parameters of lambda that are a lambda
|
1547
1551
|
// minus one to remove the body
|
1548
1552
|
env[first[VALUE]][STATS][TYPE_PROP] = [APPLY]
|
1549
1553
|
env[first[VALUE]][STATS][ARG_COUNT] = rest.length
|
1550
|
-
env[first[VALUE]][STATS][ARGUMENTS] =
|
1554
|
+
env[first[VALUE]][STATS][ARGUMENTS] = fillUnknownArgs(
|
1551
1555
|
rest.length
|
1552
1556
|
)
|
1553
1557
|
for (let i = 0; i < rest.length; ++i) {
|
@@ -1569,7 +1573,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1569
1573
|
// case ATOM:
|
1570
1574
|
// break
|
1571
1575
|
case APPLY:
|
1572
|
-
// passing arg
|
1576
|
+
// passing arg as a APPLICATION
|
1573
1577
|
if (isUnknownType(arg[i][STATS]))
|
1574
1578
|
setTypeToReturnRef(
|
1575
1579
|
arg[i][STATS],
|
@@ -1577,7 +1581,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1577
1581
|
)
|
1578
1582
|
break
|
1579
1583
|
case WORD:
|
1580
|
-
// if param is a word we
|
1584
|
+
// if param is a word we associate them by reference
|
1581
1585
|
// if (isUnknownType(arg[i][STATS]))
|
1582
1586
|
setStatsRef(arg[i], env[ARG[VALUE]])
|
1583
1587
|
break
|
@@ -1611,7 +1615,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1611
1615
|
env[name][STATS][ARG_COUNT] !==
|
1612
1616
|
args[i][STATS][ARG_COUNT]
|
1613
1617
|
) {
|
1614
|
-
// TODO: Investigate why there used to be
|
1618
|
+
// TODO: Investigate why there used to be an error called #111 with this condition if (args[i][STATS][ARG_COUNT] === undefined)
|
1615
1619
|
if (getType(args[i][STATS]) === APPLY)
|
1616
1620
|
throw new TypeError(
|
1617
1621
|
`Incorrect number of arguments for (${
|
@@ -1749,7 +1753,7 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1749
1753
|
setType(env[name][STATS], args[i][STATS])
|
1750
1754
|
else if (isUnknownType(env[name][STATS])) {
|
1751
1755
|
// REFF ASSIGMENT
|
1752
|
-
// EXPLAIN: Not
|
1756
|
+
// EXPLAIN: Not assigning ref fixes this overwriting
|
1753
1757
|
// (let sum (lambda testxs (+ (get testxs 0) (get testxs 1))))
|
1754
1758
|
// (let range (math:range 1 10))
|
1755
1759
|
// (sum range)
|
@@ -1795,10 +1799,10 @@ export const typeCheck = (ast, ctx = SPECIAL_FORM_TYPES) => {
|
|
1795
1799
|
setReturnRef(env[name][STATS], args[i][STATS])
|
1796
1800
|
break
|
1797
1801
|
default:
|
1798
|
-
// TODO fix this
|
1802
|
+
// TODO fix this assignment
|
1799
1803
|
// It turns out it's not possible to determine return type of function here
|
1800
1804
|
// what if it's a global function used elsewhere where the return type mwould be different
|
1801
|
-
// THIS
|
1805
|
+
// THIS will give lambda return types but refactor is needed still
|
1802
1806
|
setReturnRef(env[name][STATS], args[i][STATS])
|
1803
1807
|
break
|
1804
1808
|
}
|
package/src/enhance.js
CHANGED
@@ -11,7 +11,7 @@ import {
|
|
11
11
|
import { getPrefix, shake, wrapInBlock } from './utils.js'
|
12
12
|
import std from '../lib/baked/std.js'
|
13
13
|
export const OPTIMIZATIONS = {
|
14
|
-
|
14
|
+
TAILCALL: 'tail-call',
|
15
15
|
CACHE: 'memoized'
|
16
16
|
}
|
17
17
|
export const OPTIMIZED_PREFIX = 'optimized-lambda::'
|
@@ -53,7 +53,7 @@ const opt = (ast) => {
|
|
53
53
|
) {
|
54
54
|
const name = exp[1][VALUE]
|
55
55
|
const prefix = getPrefix(name)
|
56
|
-
if (prefix === OPTIMIZATIONS.
|
56
|
+
if (prefix === OPTIMIZATIONS.TAILCALL) {
|
57
57
|
const args = last.slice(1, -1)
|
58
58
|
const newName = `${OPTIMIZED_PREFIX}${name}::*${performance
|
59
59
|
.now()
|
package/src/utils.js
CHANGED
@@ -119,7 +119,7 @@ const KEYWORDS_SET = Object.values(KEYWORDS).reduce((a, b) => {
|
|
119
119
|
export const isForbiddenVariableName = (name) => {
|
120
120
|
switch (name) {
|
121
121
|
case '_':
|
122
|
-
case OPTIMIZATIONS.
|
122
|
+
case OPTIMIZATIONS.TAILCALL:
|
123
123
|
case OPTIMIZATIONS.CACHE:
|
124
124
|
return true
|
125
125
|
default:
|