@rohal12/spindle 0.52.6 → 0.52.7
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/dist/pkg/format.js +1 -1
- package/dist/pkg/headless.js +54 -32
- package/dist/pkg/story-variables.js +54 -32
- package/package.json +1 -1
- package/src/js-lexer.ts +80 -45
package/dist/pkg/headless.js
CHANGED
|
@@ -946,6 +946,13 @@ var UNCLOSED = -1;
|
|
|
946
946
|
var MALFORMED = -2;
|
|
947
947
|
/** A frame result: the scan stops at index `s` inside it (`STOPPED - s`). */
|
|
948
948
|
var STOPPED = -3;
|
|
949
|
+
/**
|
|
950
|
+
* The result of a frame that closes at `end`. `bodyStarted`: the code inside
|
|
951
|
+
* started a function or class body. That body replaced the one to come, and
|
|
952
|
+
* is gone once the frame is closed, so after the frame no body is to come —
|
|
953
|
+
* a scan that skips the frame must know, as must the frames around it.
|
|
954
|
+
*/
|
|
955
|
+
var frameEnd = (end, bodyStarted) => end * 2 + +bodyStarted;
|
|
949
956
|
/** The closers whose search for their frame a frame result may depend on. */
|
|
950
957
|
var CLOSERS = [
|
|
951
958
|
")",
|
|
@@ -1178,14 +1185,30 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1178
1185
|
]] : void 0;
|
|
1179
1186
|
/**
|
|
1180
1187
|
* How many function or class bodies to come the scan has started, and with
|
|
1181
|
-
* `parens`, that count when each open frame opened. Code in a
|
|
1182
|
-
*
|
|
1183
|
-
* follow once the frame is closed: none is to come then.
|
|
1188
|
+
* `braces` or `parens`, that count when each open frame opened. Code in a
|
|
1189
|
+
* frame that starts one replaces the one to come, and the new one can't
|
|
1190
|
+
* follow once the frame is closed: none is to come then. A scan skipping
|
|
1191
|
+
* the frame learns this from its result (`frameEnd`, `resume`), and counts
|
|
1192
|
+
* the body as started too, for the frames around it to record.
|
|
1184
1193
|
*/
|
|
1185
1194
|
let bodyStarts = 0;
|
|
1186
|
-
const startsAt = parens ? [0] : void 0;
|
|
1195
|
+
const startsAt = braces || parens ? [0] : void 0;
|
|
1187
1196
|
/** A `(…)` or `[…]` frame result found in `parens`, to skip to. */
|
|
1188
1197
|
let parenTo;
|
|
1198
|
+
/** The result of the frame at index `k`, closing at `i`. */
|
|
1199
|
+
const closedAt = (k) => frameEnd(i, startsAt !== void 0 && bodyStarts !== startsAt[k]);
|
|
1200
|
+
/**
|
|
1201
|
+
* Go on after a frame an earlier scan lexed, whose result (`frameEnd`) is
|
|
1202
|
+
* `result`: returns the index it closes at. A body started in it is gone
|
|
1203
|
+
* after it, with the one it replaced.
|
|
1204
|
+
*/
|
|
1205
|
+
function resume(result) {
|
|
1206
|
+
if (result % 2 === 1) {
|
|
1207
|
+
pendingBody = void 0;
|
|
1208
|
+
bodyStarts++;
|
|
1209
|
+
}
|
|
1210
|
+
return Math.floor(result / 2);
|
|
1211
|
+
}
|
|
1189
1212
|
/** The closer `c` looked for its frame and found index `k`. */
|
|
1190
1213
|
function lookedFor(c, k) {
|
|
1191
1214
|
if (!lowest) return;
|
|
@@ -1333,8 +1356,8 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1333
1356
|
if (lowest) {
|
|
1334
1357
|
for (let j = frames.length - 1; j >= k; j--) foldLowest(j);
|
|
1335
1358
|
lowest.length = k;
|
|
1336
|
-
startsAt.length = k;
|
|
1337
1359
|
}
|
|
1360
|
+
if (startsAt) startsAt.length = k;
|
|
1338
1361
|
frames.length = k;
|
|
1339
1362
|
if (stackIds) stackIds.length = k;
|
|
1340
1363
|
for (const list of Object.values(closers)) while (list.length && list[list.length - 1] >= k) list.pop();
|
|
@@ -1411,6 +1434,18 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1411
1434
|
stmtNext = f.kind === "block";
|
|
1412
1435
|
keyNext = f.kind === "object" || f.kind === "class";
|
|
1413
1436
|
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Go past frame `f` if an earlier scan lexed it: to just after it, or to
|
|
1439
|
+
* the end of the source when it never closes. Whether it did, or
|
|
1440
|
+
* MALFORMED when that scan found the code malformed.
|
|
1441
|
+
*/
|
|
1442
|
+
function skipLexed(f) {
|
|
1443
|
+
const end = known(f);
|
|
1444
|
+
if (end === void 0) return false;
|
|
1445
|
+
if (end === MALFORMED) return MALFORMED;
|
|
1446
|
+
i = end === UNCLOSED ? src.length : resume(end) + 1;
|
|
1447
|
+
return true;
|
|
1448
|
+
}
|
|
1414
1449
|
function openBrace() {
|
|
1415
1450
|
let f;
|
|
1416
1451
|
if (pendingBody?.depth === frames.length) {
|
|
@@ -1454,10 +1489,10 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1454
1489
|
return;
|
|
1455
1490
|
}
|
|
1456
1491
|
const closed = frames[k];
|
|
1492
|
+
const result = closedAt(k);
|
|
1457
1493
|
if (lowest && c !== "}") {
|
|
1458
1494
|
for (let j = frames.length - 1; j > k; j--) foldLowest(j);
|
|
1459
|
-
|
|
1460
|
-
recordParen(k, i * 2 + +bodyStarted);
|
|
1495
|
+
recordParen(k, result);
|
|
1461
1496
|
}
|
|
1462
1497
|
truncate(k);
|
|
1463
1498
|
if (c === "]" && !ctx.lookahead) ctx.brackets.set(closed.open, i);
|
|
@@ -1466,7 +1501,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1466
1501
|
stmtNext = closed.header;
|
|
1467
1502
|
} else if (c === "]") endPunct(c, false);
|
|
1468
1503
|
else {
|
|
1469
|
-
record(closed,
|
|
1504
|
+
record(closed, result);
|
|
1470
1505
|
afterBrace(closed);
|
|
1471
1506
|
}
|
|
1472
1507
|
}
|
|
@@ -1568,23 +1603,16 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1568
1603
|
} else if (ch === "`") {
|
|
1569
1604
|
literal(ch, i);
|
|
1570
1605
|
if (frames.length === 1) return i + 1;
|
|
1571
|
-
record(top(),
|
|
1606
|
+
record(top(), closedAt(frames.length - 1));
|
|
1572
1607
|
i++;
|
|
1573
1608
|
truncate(frames.length - 1);
|
|
1574
1609
|
endOperand();
|
|
1575
1610
|
} else if (ch === "$" && src.charAt(i + 1) === "{") {
|
|
1576
1611
|
const f = frame("expr", "}", i);
|
|
1577
1612
|
f.interpolation = true;
|
|
1578
|
-
const
|
|
1579
|
-
if (
|
|
1580
|
-
if (
|
|
1581
|
-
i = src.length;
|
|
1582
|
-
break;
|
|
1583
|
-
}
|
|
1584
|
-
if (end !== void 0) {
|
|
1585
|
-
i = end + 1;
|
|
1586
|
-
continue;
|
|
1587
|
-
}
|
|
1613
|
+
const skipped = skipLexed(f);
|
|
1614
|
+
if (skipped === MALFORMED) return malformed();
|
|
1615
|
+
if (skipped) continue;
|
|
1588
1616
|
literal("${", i);
|
|
1589
1617
|
i += 2;
|
|
1590
1618
|
nesting++;
|
|
@@ -1611,14 +1639,9 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1611
1639
|
if (ch === "`") {
|
|
1612
1640
|
endWord();
|
|
1613
1641
|
const f = frame("template", "`", i);
|
|
1614
|
-
const
|
|
1615
|
-
if (
|
|
1616
|
-
if (
|
|
1617
|
-
i = src.length;
|
|
1618
|
-
break;
|
|
1619
|
-
}
|
|
1620
|
-
if (end !== void 0) {
|
|
1621
|
-
i = end + 1;
|
|
1642
|
+
const skipped = skipLexed(f);
|
|
1643
|
+
if (skipped === MALFORMED) return malformed();
|
|
1644
|
+
if (skipped) {
|
|
1622
1645
|
endOperand();
|
|
1623
1646
|
continue;
|
|
1624
1647
|
}
|
|
@@ -1637,7 +1660,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1637
1660
|
lookedFor("}", k);
|
|
1638
1661
|
if (frames[k].interpolation) {
|
|
1639
1662
|
endWord();
|
|
1640
|
-
record(frames[k],
|
|
1663
|
+
record(frames[k], closedAt(k));
|
|
1641
1664
|
truncate(k);
|
|
1642
1665
|
nesting--;
|
|
1643
1666
|
literal(ch, i);
|
|
@@ -1719,7 +1742,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1719
1742
|
continue;
|
|
1720
1743
|
}
|
|
1721
1744
|
}
|
|
1722
|
-
if (ch === outer.closer && frames.length === 1) break;
|
|
1745
|
+
if (ch === outer.closer && (frames.length === 1 || ch === "]" && innermost("]") === 0 && innermost("}") === 0)) break;
|
|
1723
1746
|
trackCode(ch);
|
|
1724
1747
|
if (skipTo) {
|
|
1725
1748
|
const { frame: skipped, end } = skipTo;
|
|
@@ -1729,7 +1752,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1729
1752
|
i = src.length;
|
|
1730
1753
|
break;
|
|
1731
1754
|
}
|
|
1732
|
-
i = end;
|
|
1755
|
+
i = resume(end);
|
|
1733
1756
|
afterBrace(skipped);
|
|
1734
1757
|
i++;
|
|
1735
1758
|
continue;
|
|
@@ -1747,8 +1770,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1747
1770
|
strict.stopped = true;
|
|
1748
1771
|
return STOPPED - result;
|
|
1749
1772
|
}
|
|
1750
|
-
i =
|
|
1751
|
-
if (result % 2 === 1) pendingBody = void 0;
|
|
1773
|
+
i = resume(result);
|
|
1752
1774
|
if (skipped.closer === ")") {
|
|
1753
1775
|
endPunct(")", skipped.header);
|
|
1754
1776
|
stmtNext = skipped.header;
|
|
@@ -248,6 +248,13 @@ var UNCLOSED = -1;
|
|
|
248
248
|
var MALFORMED = -2;
|
|
249
249
|
/** A frame result: the scan stops at index `s` inside it (`STOPPED - s`). */
|
|
250
250
|
var STOPPED = -3;
|
|
251
|
+
/**
|
|
252
|
+
* The result of a frame that closes at `end`. `bodyStarted`: the code inside
|
|
253
|
+
* started a function or class body. That body replaced the one to come, and
|
|
254
|
+
* is gone once the frame is closed, so after the frame no body is to come —
|
|
255
|
+
* a scan that skips the frame must know, as must the frames around it.
|
|
256
|
+
*/
|
|
257
|
+
var frameEnd = (end, bodyStarted) => end * 2 + +bodyStarted;
|
|
251
258
|
/** The closers whose search for their frame a frame result may depend on. */
|
|
252
259
|
var CLOSERS = [
|
|
253
260
|
")",
|
|
@@ -480,14 +487,30 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
480
487
|
]] : void 0;
|
|
481
488
|
/**
|
|
482
489
|
* How many function or class bodies to come the scan has started, and with
|
|
483
|
-
* `parens`, that count when each open frame opened. Code in a
|
|
484
|
-
*
|
|
485
|
-
* follow once the frame is closed: none is to come then.
|
|
490
|
+
* `braces` or `parens`, that count when each open frame opened. Code in a
|
|
491
|
+
* frame that starts one replaces the one to come, and the new one can't
|
|
492
|
+
* follow once the frame is closed: none is to come then. A scan skipping
|
|
493
|
+
* the frame learns this from its result (`frameEnd`, `resume`), and counts
|
|
494
|
+
* the body as started too, for the frames around it to record.
|
|
486
495
|
*/
|
|
487
496
|
let bodyStarts = 0;
|
|
488
|
-
const startsAt = parens ? [0] : void 0;
|
|
497
|
+
const startsAt = braces || parens ? [0] : void 0;
|
|
489
498
|
/** A `(…)` or `[…]` frame result found in `parens`, to skip to. */
|
|
490
499
|
let parenTo;
|
|
500
|
+
/** The result of the frame at index `k`, closing at `i`. */
|
|
501
|
+
const closedAt = (k) => frameEnd(i, startsAt !== void 0 && bodyStarts !== startsAt[k]);
|
|
502
|
+
/**
|
|
503
|
+
* Go on after a frame an earlier scan lexed, whose result (`frameEnd`) is
|
|
504
|
+
* `result`: returns the index it closes at. A body started in it is gone
|
|
505
|
+
* after it, with the one it replaced.
|
|
506
|
+
*/
|
|
507
|
+
function resume(result) {
|
|
508
|
+
if (result % 2 === 1) {
|
|
509
|
+
pendingBody = void 0;
|
|
510
|
+
bodyStarts++;
|
|
511
|
+
}
|
|
512
|
+
return Math.floor(result / 2);
|
|
513
|
+
}
|
|
491
514
|
/** The closer `c` looked for its frame and found index `k`. */
|
|
492
515
|
function lookedFor(c, k) {
|
|
493
516
|
if (!lowest) return;
|
|
@@ -635,8 +658,8 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
635
658
|
if (lowest) {
|
|
636
659
|
for (let j = frames.length - 1; j >= k; j--) foldLowest(j);
|
|
637
660
|
lowest.length = k;
|
|
638
|
-
startsAt.length = k;
|
|
639
661
|
}
|
|
662
|
+
if (startsAt) startsAt.length = k;
|
|
640
663
|
frames.length = k;
|
|
641
664
|
if (stackIds) stackIds.length = k;
|
|
642
665
|
for (const list of Object.values(closers)) while (list.length && list[list.length - 1] >= k) list.pop();
|
|
@@ -713,6 +736,18 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
713
736
|
stmtNext = f.kind === "block";
|
|
714
737
|
keyNext = f.kind === "object" || f.kind === "class";
|
|
715
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* Go past frame `f` if an earlier scan lexed it: to just after it, or to
|
|
741
|
+
* the end of the source when it never closes. Whether it did, or
|
|
742
|
+
* MALFORMED when that scan found the code malformed.
|
|
743
|
+
*/
|
|
744
|
+
function skipLexed(f) {
|
|
745
|
+
const end = known(f);
|
|
746
|
+
if (end === void 0) return false;
|
|
747
|
+
if (end === MALFORMED) return MALFORMED;
|
|
748
|
+
i = end === UNCLOSED ? src.length : resume(end) + 1;
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
716
751
|
function openBrace() {
|
|
717
752
|
let f;
|
|
718
753
|
if (pendingBody?.depth === frames.length) {
|
|
@@ -756,10 +791,10 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
756
791
|
return;
|
|
757
792
|
}
|
|
758
793
|
const closed = frames[k];
|
|
794
|
+
const result = closedAt(k);
|
|
759
795
|
if (lowest && c !== "}") {
|
|
760
796
|
for (let j = frames.length - 1; j > k; j--) foldLowest(j);
|
|
761
|
-
|
|
762
|
-
recordParen(k, i * 2 + +bodyStarted);
|
|
797
|
+
recordParen(k, result);
|
|
763
798
|
}
|
|
764
799
|
truncate(k);
|
|
765
800
|
if (c === "]" && !ctx.lookahead) ctx.brackets.set(closed.open, i);
|
|
@@ -768,7 +803,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
768
803
|
stmtNext = closed.header;
|
|
769
804
|
} else if (c === "]") endPunct(c, false);
|
|
770
805
|
else {
|
|
771
|
-
record(closed,
|
|
806
|
+
record(closed, result);
|
|
772
807
|
afterBrace(closed);
|
|
773
808
|
}
|
|
774
809
|
}
|
|
@@ -870,23 +905,16 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
870
905
|
} else if (ch === "`") {
|
|
871
906
|
literal(ch, i);
|
|
872
907
|
if (frames.length === 1) return i + 1;
|
|
873
|
-
record(top(),
|
|
908
|
+
record(top(), closedAt(frames.length - 1));
|
|
874
909
|
i++;
|
|
875
910
|
truncate(frames.length - 1);
|
|
876
911
|
endOperand();
|
|
877
912
|
} else if (ch === "$" && src.charAt(i + 1) === "{") {
|
|
878
913
|
const f = frame("expr", "}", i);
|
|
879
914
|
f.interpolation = true;
|
|
880
|
-
const
|
|
881
|
-
if (
|
|
882
|
-
if (
|
|
883
|
-
i = src.length;
|
|
884
|
-
break;
|
|
885
|
-
}
|
|
886
|
-
if (end !== void 0) {
|
|
887
|
-
i = end + 1;
|
|
888
|
-
continue;
|
|
889
|
-
}
|
|
915
|
+
const skipped = skipLexed(f);
|
|
916
|
+
if (skipped === MALFORMED) return malformed();
|
|
917
|
+
if (skipped) continue;
|
|
890
918
|
literal("${", i);
|
|
891
919
|
i += 2;
|
|
892
920
|
nesting++;
|
|
@@ -913,14 +941,9 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
913
941
|
if (ch === "`") {
|
|
914
942
|
endWord();
|
|
915
943
|
const f = frame("template", "`", i);
|
|
916
|
-
const
|
|
917
|
-
if (
|
|
918
|
-
if (
|
|
919
|
-
i = src.length;
|
|
920
|
-
break;
|
|
921
|
-
}
|
|
922
|
-
if (end !== void 0) {
|
|
923
|
-
i = end + 1;
|
|
944
|
+
const skipped = skipLexed(f);
|
|
945
|
+
if (skipped === MALFORMED) return malformed();
|
|
946
|
+
if (skipped) {
|
|
924
947
|
endOperand();
|
|
925
948
|
continue;
|
|
926
949
|
}
|
|
@@ -939,7 +962,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
939
962
|
lookedFor("}", k);
|
|
940
963
|
if (frames[k].interpolation) {
|
|
941
964
|
endWord();
|
|
942
|
-
record(frames[k],
|
|
965
|
+
record(frames[k], closedAt(k));
|
|
943
966
|
truncate(k);
|
|
944
967
|
nesting--;
|
|
945
968
|
literal(ch, i);
|
|
@@ -1021,7 +1044,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1021
1044
|
continue;
|
|
1022
1045
|
}
|
|
1023
1046
|
}
|
|
1024
|
-
if (ch === outer.closer && frames.length === 1) break;
|
|
1047
|
+
if (ch === outer.closer && (frames.length === 1 || ch === "]" && innermost("]") === 0 && innermost("}") === 0)) break;
|
|
1025
1048
|
trackCode(ch);
|
|
1026
1049
|
if (skipTo) {
|
|
1027
1050
|
const { frame: skipped, end } = skipTo;
|
|
@@ -1031,7 +1054,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1031
1054
|
i = src.length;
|
|
1032
1055
|
break;
|
|
1033
1056
|
}
|
|
1034
|
-
i = end;
|
|
1057
|
+
i = resume(end);
|
|
1035
1058
|
afterBrace(skipped);
|
|
1036
1059
|
i++;
|
|
1037
1060
|
continue;
|
|
@@ -1049,8 +1072,7 @@ function scan(src, handlers, start, nesting, outer, ctx) {
|
|
|
1049
1072
|
strict.stopped = true;
|
|
1050
1073
|
return STOPPED - result;
|
|
1051
1074
|
}
|
|
1052
|
-
i =
|
|
1053
|
-
if (result % 2 === 1) pendingBody = void 0;
|
|
1075
|
+
i = resume(result);
|
|
1054
1076
|
if (skipped.closer === ")") {
|
|
1055
1077
|
endPunct(")", skipped.header);
|
|
1056
1078
|
stmtNext = skipped.header;
|
package/package.json
CHANGED
package/src/js-lexer.ts
CHANGED
|
@@ -397,6 +397,14 @@ const MALFORMED = -2;
|
|
|
397
397
|
/** A frame result: the scan stops at index `s` inside it (`STOPPED - s`). */
|
|
398
398
|
const STOPPED = -3;
|
|
399
399
|
|
|
400
|
+
/**
|
|
401
|
+
* The result of a frame that closes at `end`. `bodyStarted`: the code inside
|
|
402
|
+
* started a function or class body. That body replaced the one to come, and
|
|
403
|
+
* is gone once the frame is closed, so after the frame no body is to come —
|
|
404
|
+
* a scan that skips the frame must know, as must the frames around it.
|
|
405
|
+
*/
|
|
406
|
+
const frameEnd = (end: number, bodyStarted: boolean) => end * 2 + +bodyStarted;
|
|
407
|
+
|
|
400
408
|
/** The closers whose search for their frame a frame result may depend on. */
|
|
401
409
|
const CLOSERS = [')', ']', '}'] as const;
|
|
402
410
|
const FRAME_KINDS: readonly Frame['kind'][] = [
|
|
@@ -423,11 +431,13 @@ function frameKey(f: Frame): number | undefined {
|
|
|
423
431
|
*/
|
|
424
432
|
export interface JsScanCache {
|
|
425
433
|
/**
|
|
426
|
-
* Where each `{…}` frame, template literal or `${…}` interpolation closes
|
|
427
|
-
*
|
|
428
|
-
* code inside
|
|
429
|
-
*
|
|
430
|
-
*
|
|
434
|
+
* Where each `{…}` frame, template literal or `${…}` interpolation closes,
|
|
435
|
+
* as a frame result (`frameEnd`): its `}` or closing backtick, and whether
|
|
436
|
+
* the code inside started a function or class body; or `UNCLOSED` or
|
|
437
|
+
* `MALFORMED`. The code inside such a frame lexes the same whatever
|
|
438
|
+
* surrounds it, given where it opens and its kind — a stray `)` or `]`
|
|
439
|
+
* inside never closes it — so a later scan entering the same frame skips
|
|
440
|
+
* to its end.
|
|
431
441
|
*/
|
|
432
442
|
braces: Map<number, number>;
|
|
433
443
|
/** Look-ahead bracket matches (`ScanContext.brackets`). */
|
|
@@ -457,10 +467,8 @@ export interface JsScanCache {
|
|
|
457
467
|
stacks: Map<string, number>;
|
|
458
468
|
/**
|
|
459
469
|
* How `(…)` and `[…]` frames end, by the kind of scan and then by the frame
|
|
460
|
-
* (`parenKey`):
|
|
461
|
-
*
|
|
462
|
-
* can't follow once the frame is closed), `end * 2` otherwise, or
|
|
463
|
-
* `UNCLOSED`, `MALFORMED` or `STOPPED - s`. Unlike braces, a stray closer inside may close a frame
|
|
470
|
+
* (`parenKey`): a frame result (`frameEnd`), or `UNCLOSED`, `MALFORMED` or
|
|
471
|
+
* `STOPPED - s`. Unlike braces, a stray closer inside may close a frame
|
|
464
472
|
* around them, so the code inside lexes the same only around frames for
|
|
465
473
|
* which the closers it met find nothing to close; the key says which
|
|
466
474
|
* closers met none.
|
|
@@ -748,15 +756,34 @@ function scan(
|
|
|
748
756
|
: undefined;
|
|
749
757
|
/**
|
|
750
758
|
* How many function or class bodies to come the scan has started, and with
|
|
751
|
-
* `parens`, that count when each open frame opened. Code in a
|
|
752
|
-
*
|
|
753
|
-
* follow once the frame is closed: none is to come then.
|
|
759
|
+
* `braces` or `parens`, that count when each open frame opened. Code in a
|
|
760
|
+
* frame that starts one replaces the one to come, and the new one can't
|
|
761
|
+
* follow once the frame is closed: none is to come then. A scan skipping
|
|
762
|
+
* the frame learns this from its result (`frameEnd`, `resume`), and counts
|
|
763
|
+
* the body as started too, for the frames around it to record.
|
|
754
764
|
*/
|
|
755
765
|
let bodyStarts = 0;
|
|
756
|
-
const startsAt: number[] | undefined = parens ? [0] : undefined;
|
|
766
|
+
const startsAt: number[] | undefined = braces || parens ? [0] : undefined;
|
|
757
767
|
/** A `(…)` or `[…]` frame result found in `parens`, to skip to. */
|
|
758
768
|
let parenTo: { frame: Frame; result: number } | undefined;
|
|
759
769
|
|
|
770
|
+
/** The result of the frame at index `k`, closing at `i`. */
|
|
771
|
+
const closedAt = (k: number) =>
|
|
772
|
+
frameEnd(i, startsAt !== undefined && bodyStarts !== startsAt[k]);
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Go on after a frame an earlier scan lexed, whose result (`frameEnd`) is
|
|
776
|
+
* `result`: returns the index it closes at. A body started in it is gone
|
|
777
|
+
* after it, with the one it replaced.
|
|
778
|
+
*/
|
|
779
|
+
function resume(result: number): number {
|
|
780
|
+
if (result % 2 === 1) {
|
|
781
|
+
pendingBody = undefined;
|
|
782
|
+
bodyStarts++;
|
|
783
|
+
}
|
|
784
|
+
return Math.floor(result / 2);
|
|
785
|
+
}
|
|
786
|
+
|
|
760
787
|
/** The closer `c` looked for its frame and found index `k`. */
|
|
761
788
|
function lookedFor(c: (typeof CLOSERS)[number], k: number) {
|
|
762
789
|
if (!lowest) return;
|
|
@@ -933,8 +960,8 @@ function scan(
|
|
|
933
960
|
if (lowest) {
|
|
934
961
|
for (let j = frames.length - 1; j >= k; j--) foldLowest(j);
|
|
935
962
|
lowest.length = k;
|
|
936
|
-
startsAt!.length = k;
|
|
937
963
|
}
|
|
964
|
+
if (startsAt) startsAt.length = k;
|
|
938
965
|
frames.length = k;
|
|
939
966
|
if (stackIds) stackIds.length = k;
|
|
940
967
|
for (const list of Object.values(closers)) {
|
|
@@ -1034,6 +1061,19 @@ function scan(
|
|
|
1034
1061
|
keyNext = f.kind === 'object' || f.kind === 'class';
|
|
1035
1062
|
}
|
|
1036
1063
|
|
|
1064
|
+
/**
|
|
1065
|
+
* Go past frame `f` if an earlier scan lexed it: to just after it, or to
|
|
1066
|
+
* the end of the source when it never closes. Whether it did, or
|
|
1067
|
+
* MALFORMED when that scan found the code malformed.
|
|
1068
|
+
*/
|
|
1069
|
+
function skipLexed(f: Frame): boolean | typeof MALFORMED {
|
|
1070
|
+
const end = known(f);
|
|
1071
|
+
if (end === undefined) return false;
|
|
1072
|
+
if (end === MALFORMED) return MALFORMED;
|
|
1073
|
+
i = end === UNCLOSED ? src.length : resume(end) + 1;
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1037
1077
|
function openBrace() {
|
|
1038
1078
|
let f: Frame;
|
|
1039
1079
|
if (pendingBody?.depth === frames.length) {
|
|
@@ -1079,12 +1119,11 @@ function scan(
|
|
|
1079
1119
|
return;
|
|
1080
1120
|
}
|
|
1081
1121
|
const closed = frames[k]!;
|
|
1122
|
+
const result = closedAt(k);
|
|
1082
1123
|
if (lowest && c !== '}') {
|
|
1083
1124
|
// Its entry complete, with those of the frames still open in it
|
|
1084
1125
|
for (let j = frames.length - 1; j > k; j--) foldLowest(j);
|
|
1085
|
-
|
|
1086
|
-
const bodyStarted = bodyStarts !== startsAt![k];
|
|
1087
|
-
recordParen(k, i * 2 + +bodyStarted);
|
|
1126
|
+
recordParen(k, result);
|
|
1088
1127
|
}
|
|
1089
1128
|
truncate(k);
|
|
1090
1129
|
if (c === ']' && !ctx.lookahead) ctx.brackets.set(closed.open, i);
|
|
@@ -1095,7 +1134,7 @@ function scan(
|
|
|
1095
1134
|
} else if (c === ']') {
|
|
1096
1135
|
endPunct(c, false);
|
|
1097
1136
|
} else {
|
|
1098
|
-
record(closed,
|
|
1137
|
+
record(closed, result);
|
|
1099
1138
|
afterBrace(closed);
|
|
1100
1139
|
}
|
|
1101
1140
|
}
|
|
@@ -1214,24 +1253,17 @@ function scan(
|
|
|
1214
1253
|
} else if (ch === '`') {
|
|
1215
1254
|
literal(ch, i);
|
|
1216
1255
|
if (frames.length === 1) return i + 1; // the end of `lexTemplate`
|
|
1217
|
-
record(top(),
|
|
1256
|
+
record(top(), closedAt(frames.length - 1));
|
|
1218
1257
|
i++;
|
|
1219
1258
|
truncate(frames.length - 1);
|
|
1220
1259
|
endOperand();
|
|
1221
1260
|
} else if (ch === '$' && src.charAt(i + 1) === '{') {
|
|
1222
1261
|
const f = frame('expr', '}', i);
|
|
1223
1262
|
f.interpolation = true;
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
if (
|
|
1227
|
-
|
|
1228
|
-
break;
|
|
1229
|
-
}
|
|
1230
|
-
if (end !== undefined) {
|
|
1231
|
-
// An interpolation an earlier scan lexed: on with the text after it
|
|
1232
|
-
i = end + 1;
|
|
1233
|
-
continue;
|
|
1234
|
-
}
|
|
1263
|
+
// An interpolation an earlier scan lexed: on with the text after it
|
|
1264
|
+
const skipped = skipLexed(f);
|
|
1265
|
+
if (skipped === MALFORMED) return malformed();
|
|
1266
|
+
if (skipped) continue;
|
|
1235
1267
|
literal('${', i);
|
|
1236
1268
|
i += 2;
|
|
1237
1269
|
nesting++;
|
|
@@ -1261,15 +1293,10 @@ function scan(
|
|
|
1261
1293
|
if (ch === '`') {
|
|
1262
1294
|
endWord();
|
|
1263
1295
|
const f = frame('template', '`', i);
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
if (
|
|
1267
|
-
|
|
1268
|
-
break;
|
|
1269
|
-
}
|
|
1270
|
-
if (end !== undefined) {
|
|
1271
|
-
// A template literal an earlier scan lexed: on after it
|
|
1272
|
-
i = end + 1;
|
|
1296
|
+
// A template literal an earlier scan lexed: on after it
|
|
1297
|
+
const skipped = skipLexed(f);
|
|
1298
|
+
if (skipped === MALFORMED) return malformed();
|
|
1299
|
+
if (skipped) {
|
|
1273
1300
|
endOperand();
|
|
1274
1301
|
continue;
|
|
1275
1302
|
}
|
|
@@ -1291,7 +1318,7 @@ function scan(
|
|
|
1291
1318
|
lookedFor('}', k);
|
|
1292
1319
|
if (frames[k]!.interpolation) {
|
|
1293
1320
|
endWord();
|
|
1294
|
-
record(frames[k]!,
|
|
1321
|
+
record(frames[k]!, closedAt(k));
|
|
1295
1322
|
truncate(k);
|
|
1296
1323
|
nesting--;
|
|
1297
1324
|
literal(ch, i);
|
|
@@ -1399,8 +1426,17 @@ function scan(
|
|
|
1399
1426
|
}
|
|
1400
1427
|
}
|
|
1401
1428
|
|
|
1402
|
-
// End of an `outer` square bracket
|
|
1403
|
-
|
|
1429
|
+
// End of an `outer` square bracket: a `]` that closes no `[` within the
|
|
1430
|
+
// innermost braces closes it, over any `(` left open in it, as it closes
|
|
1431
|
+
// a nested `[`. Look-ahead scans record where the `[`s nested in theirs
|
|
1432
|
+
// end (`ctx.brackets`), so one starting at a `[` must find the same.
|
|
1433
|
+
if (
|
|
1434
|
+
ch === outer.closer &&
|
|
1435
|
+
(frames.length === 1 ||
|
|
1436
|
+
(ch === ']' && innermost(']') === 0 && innermost('}') === 0))
|
|
1437
|
+
) {
|
|
1438
|
+
break;
|
|
1439
|
+
}
|
|
1404
1440
|
|
|
1405
1441
|
// Regular code character
|
|
1406
1442
|
trackCode(ch);
|
|
@@ -1413,7 +1449,7 @@ function scan(
|
|
|
1413
1449
|
i = src.length;
|
|
1414
1450
|
break;
|
|
1415
1451
|
}
|
|
1416
|
-
i = end;
|
|
1452
|
+
i = resume(end);
|
|
1417
1453
|
afterBrace(skipped);
|
|
1418
1454
|
i++;
|
|
1419
1455
|
continue;
|
|
@@ -1432,8 +1468,7 @@ function scan(
|
|
|
1432
1468
|
strict!.stopped = true;
|
|
1433
1469
|
return STOPPED - result;
|
|
1434
1470
|
}
|
|
1435
|
-
i =
|
|
1436
|
-
if (result % 2 === 1) pendingBody = undefined;
|
|
1471
|
+
i = resume(result);
|
|
1437
1472
|
if (skipped.closer === ')') {
|
|
1438
1473
|
endPunct(')', skipped.header);
|
|
1439
1474
|
stmtNext = skipped.header;
|