@rohal12/spindle 0.57.0 → 0.59.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/dist/pkg/format.js +1 -1
- package/dist/pkg/headless.js +773 -493
- package/dist/pkg/macro-registry.json +29 -11
- package/dist/pkg/story-variables.js +804 -268
- package/dist/pkg/tooling.js +39 -2
- package/package.json +1 -1
- package/src/code-check.ts +215 -51
- package/src/components/macros/Button.tsx +3 -1
- package/src/components/macros/Checkbox.tsx +4 -1
- package/src/components/macros/Dialog.tsx +1 -1
- package/src/components/macros/Include.tsx +2 -6
- package/src/components/macros/MacroLink.tsx +4 -1
- package/src/components/macros/Meter.tsx +1 -1
- package/src/components/macros/Radiobutton.tsx +2 -2
- package/src/components/macros/Watch.tsx +11 -6
- package/src/components/macros/include-args.ts +23 -0
- package/src/components/macros/input-macro.ts +4 -1
- package/src/components/macros/macro-args.ts +119 -46
- package/src/components/macros/option-utils.ts +10 -4
- package/src/expression.ts +4 -54
- package/src/index.tsx +2 -4
- package/src/js-lexer.ts +19 -0
- package/src/markup/grammar-shared.ts +8 -0
- package/src/markup/parse.ts +107 -2
- package/src/markup/spindle.d.peggy.ts +5 -2
- package/src/markup/spindle.peggy +27 -16
- package/src/markup/tokens.ts +3 -0
- package/src/markup/validate.ts +22 -12
- package/src/registry.ts +70 -6
- package/src/story-variables.ts +52 -10
- package/src/tooling.ts +63 -5
- package/src/transform.ts +65 -0
- package/src/widgets/widget-def.ts +2 -2
- package/types/index.d.ts +23 -0
- package/types/tooling.d.ts +337 -0
|
@@ -1,12 +1,39 @@
|
|
|
1
|
-
//#region src/markup/
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var SIGIL_SCOPES$1 = {
|
|
1
|
+
//#region src/markup/tokens.ts
|
|
2
|
+
/** Variable sigils: story ($), temporary (_), local (@), transient (%). */
|
|
3
|
+
var SIGIL_SCOPES = {
|
|
5
4
|
$: "variable",
|
|
6
5
|
_: "temporary",
|
|
7
6
|
"@": "local",
|
|
8
7
|
"%": "transient"
|
|
9
8
|
};
|
|
9
|
+
Object.fromEntries(Object.entries(SIGIL_SCOPES).map(([sigil, scope]) => [scope, sigil]));
|
|
10
|
+
var SIGIL_CHARS = new Set(Object.keys(SIGIL_SCOPES));
|
|
11
|
+
/** Whether `c` is a variable sigil. */
|
|
12
|
+
function isSigil(c) {
|
|
13
|
+
return c !== void 0 && SIGIL_CHARS.has(c);
|
|
14
|
+
}
|
|
15
|
+
/** Copy the selectors set in `from` onto `target`, and return it. */
|
|
16
|
+
function withSelectors$1(target, from) {
|
|
17
|
+
if (from.className) target.className = from.className;
|
|
18
|
+
if (from.id) target.id = from.id;
|
|
19
|
+
return target;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/markup/grammar-shared.ts
|
|
23
|
+
/**
|
|
24
|
+
* What the grammar (spindle.peggy) imports from the rest of Spindle, so that
|
|
25
|
+
* it holds no second copy: the generated parser imports this module's
|
|
26
|
+
* default export as `shared` (see scripts/peggy.ts).
|
|
27
|
+
*/
|
|
28
|
+
var grammar_shared_default = {
|
|
29
|
+
SIGIL_SCOPES,
|
|
30
|
+
withSelectors: withSelectors$1
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/markup/spindle.peggy
|
|
34
|
+
var LB = String.fromCharCode(123);
|
|
35
|
+
var RB = String.fromCharCode(125);
|
|
36
|
+
var { SIGIL_SCOPES: SIGIL_SCOPES$1, withSelectors } = grammar_shared_default;
|
|
10
37
|
var VOID_TAGS = new Set([
|
|
11
38
|
"area",
|
|
12
39
|
"base",
|
|
@@ -41,34 +68,46 @@ var LINK_SEPARATORS = [
|
|
|
41
68
|
["<-", true]
|
|
42
69
|
];
|
|
43
70
|
var NO_SELECTORS = {};
|
|
44
|
-
|
|
71
|
+
/**
|
|
72
|
+
* A link's display text and target, from its text `inner` (at `at` in the
|
|
73
|
+
* input), with where the target is written.
|
|
74
|
+
*/
|
|
75
|
+
function parseLink(inner, at) {
|
|
76
|
+
/** The trimmed text from `start` to `end` of inner, and where it is. */
|
|
77
|
+
const part = (start, end) => {
|
|
78
|
+
const raw = inner.slice(start, end);
|
|
79
|
+
const value = raw.trim();
|
|
80
|
+
const from = at + start + (value ? raw.indexOf(value) : 0);
|
|
81
|
+
return {
|
|
82
|
+
value,
|
|
83
|
+
start: from,
|
|
84
|
+
end: from + value.length
|
|
85
|
+
};
|
|
86
|
+
};
|
|
45
87
|
for (const [separator, targetFirst] of LINK_SEPARATORS) {
|
|
46
88
|
const idx = inner.indexOf(separator);
|
|
47
89
|
if (idx === -1) continue;
|
|
48
|
-
const before =
|
|
49
|
-
const after =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
90
|
+
const before = part(0, idx);
|
|
91
|
+
const after = part(idx + separator.length, inner.length);
|
|
92
|
+
const [display, target] = targetFirst ? [after, before] : [before, after];
|
|
93
|
+
return {
|
|
94
|
+
display: display.value,
|
|
95
|
+
target: target.value,
|
|
96
|
+
targetStart: target.start,
|
|
97
|
+
targetEnd: target.end
|
|
56
98
|
};
|
|
57
99
|
}
|
|
58
|
-
const
|
|
100
|
+
const whole = part(0, inner.length);
|
|
59
101
|
return {
|
|
60
|
-
display:
|
|
61
|
-
target:
|
|
102
|
+
display: whole.value,
|
|
103
|
+
target: whole.value,
|
|
104
|
+
targetStart: whole.start,
|
|
105
|
+
targetEnd: whole.end
|
|
62
106
|
};
|
|
63
107
|
}
|
|
64
108
|
function quoteArg(value) {
|
|
65
109
|
return "\"" + value.replace(/[\\"]/g, "\\$&") + "\"";
|
|
66
110
|
}
|
|
67
|
-
function withSelectors(target, from) {
|
|
68
|
-
if (from.className) target.className = from.className;
|
|
69
|
-
if (from.id) target.id = from.id;
|
|
70
|
-
return target;
|
|
71
|
-
}
|
|
72
111
|
function selectorsOf(parts) {
|
|
73
112
|
const classes = [];
|
|
74
113
|
let id = "";
|
|
@@ -145,6 +184,8 @@ function tokenOf(x) {
|
|
|
145
184
|
type: "link",
|
|
146
185
|
display: x.display,
|
|
147
186
|
target: x.target,
|
|
187
|
+
targetStart: x.targetStart,
|
|
188
|
+
targetEnd: x.targetEnd,
|
|
148
189
|
...span
|
|
149
190
|
}, x);
|
|
150
191
|
case "variable": return withSelectors({
|
|
@@ -288,7 +329,8 @@ function peg$parse(input, options) {
|
|
|
288
329
|
const peg$source = options.grammarSource;
|
|
289
330
|
const peg$startRuleFunctions = {
|
|
290
331
|
Markup: peg$parseMarkup,
|
|
291
|
-
Tokens: peg$parseTokens
|
|
332
|
+
Tokens: peg$parseTokens,
|
|
333
|
+
SelectorsPrefix: peg$parseSelectorsPrefix
|
|
292
334
|
};
|
|
293
335
|
let peg$startRuleFunction = peg$parseMarkup;
|
|
294
336
|
const peg$c0 = "\\";
|
|
@@ -675,8 +717,8 @@ function peg$parse(input, options) {
|
|
|
675
717
|
function peg$f48() {
|
|
676
718
|
return !textMode;
|
|
677
719
|
}
|
|
678
|
-
function peg$f49(s, inner) {
|
|
679
|
-
return withSelectors(lexeme("link", location(), parseLink(inner)), s);
|
|
720
|
+
function peg$f49(s, at, inner) {
|
|
721
|
+
return withSelectors(lexeme("link", location(), parseLink(inner, at)), s);
|
|
680
722
|
}
|
|
681
723
|
function peg$f50() {
|
|
682
724
|
return !textMode;
|
|
@@ -690,40 +732,46 @@ function peg$parse(input, options) {
|
|
|
690
732
|
function peg$f53() {
|
|
691
733
|
return NO_SELECTORS;
|
|
692
734
|
}
|
|
693
|
-
function peg$f54(
|
|
735
|
+
function peg$f54(s, end) {
|
|
736
|
+
return {
|
|
737
|
+
...s,
|
|
738
|
+
end
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function peg$f55(prefix, name) {
|
|
694
742
|
return [prefix, name];
|
|
695
743
|
}
|
|
696
|
-
function peg$
|
|
744
|
+
function peg$f56(s, sigil, name) {
|
|
697
745
|
return withSelectors(lexeme("variable", location(), {
|
|
698
746
|
name,
|
|
699
747
|
scope: SIGIL_SCOPES$1[sigil]
|
|
700
748
|
}), s);
|
|
701
749
|
}
|
|
702
|
-
function peg$
|
|
750
|
+
function peg$f57(s, expression) {
|
|
703
751
|
return withSelectors(lexeme("expression", location(), { expression }), s);
|
|
704
752
|
}
|
|
705
|
-
function peg$
|
|
753
|
+
function peg$f58() {
|
|
706
754
|
fail("Unclosed " + LB + input.substr(location().end.offset, 1) + "…: no } ends it", location());
|
|
707
755
|
}
|
|
708
|
-
function peg$
|
|
756
|
+
function peg$f59(start) {
|
|
709
757
|
return skipTo(hooks.closeBrace(input, start, start));
|
|
710
758
|
}
|
|
711
|
-
function peg$
|
|
759
|
+
function peg$f60(start) {
|
|
712
760
|
return skipEnd - offset();
|
|
713
761
|
}
|
|
714
|
-
function peg$
|
|
762
|
+
function peg$f61(start, code) {
|
|
715
763
|
return code;
|
|
716
764
|
}
|
|
717
|
-
function peg$
|
|
765
|
+
function peg$f62(s, start, close, name, nameEnd) {
|
|
718
766
|
return skipTo(hooks.closeBrace(input, nameEnd, start));
|
|
719
767
|
}
|
|
720
|
-
function peg$
|
|
768
|
+
function peg$f63(s, start, close, name, nameEnd) {
|
|
721
769
|
return skipEnd - offset();
|
|
722
770
|
}
|
|
723
|
-
function peg$
|
|
771
|
+
function peg$f64(s, start, close, name, nameEnd, args) {
|
|
724
772
|
return args;
|
|
725
773
|
}
|
|
726
|
-
function peg$
|
|
774
|
+
function peg$f65(s, start, close, name, nameEnd, tail) {
|
|
727
775
|
const loc = location();
|
|
728
776
|
const lower = name.toLowerCase();
|
|
729
777
|
const tag = lexeme("macro", loc, {
|
|
@@ -744,19 +792,19 @@ function peg$parse(input, options) {
|
|
|
744
792
|
}
|
|
745
793
|
return tag;
|
|
746
794
|
}
|
|
747
|
-
function peg$
|
|
795
|
+
function peg$f66(s) {
|
|
748
796
|
fail("A closing tag starts with a letter after " + LB + "/", location());
|
|
749
797
|
}
|
|
750
|
-
function peg$
|
|
798
|
+
function peg$f67() {
|
|
751
799
|
return !textMode;
|
|
752
800
|
}
|
|
753
|
-
function peg$
|
|
801
|
+
function peg$f68(tag) {
|
|
754
802
|
return VOID_TAGS.has(tag.lower);
|
|
755
803
|
}
|
|
756
|
-
function peg$
|
|
804
|
+
function peg$f69(tag) {
|
|
757
805
|
return null;
|
|
758
806
|
}
|
|
759
|
-
function peg$
|
|
807
|
+
function peg$f70(tag, list, slash) {
|
|
760
808
|
const lower = tag.toLowerCase();
|
|
761
809
|
return lexeme("html", location(), {
|
|
762
810
|
tag,
|
|
@@ -766,7 +814,7 @@ function peg$parse(input, options) {
|
|
|
766
814
|
isSelfClose: !!slash || VOID_TAGS.has(lower)
|
|
767
815
|
});
|
|
768
816
|
}
|
|
769
|
-
function peg$
|
|
817
|
+
function peg$f71(tag, bad) {
|
|
770
818
|
const c = input.charAt(bad);
|
|
771
819
|
if (!c) fail("Unclosed tag <" + tag + ": no > ends it (write < for a < that starts no tag)", location());
|
|
772
820
|
const at = {
|
|
@@ -784,7 +832,7 @@ function peg$parse(input, options) {
|
|
|
784
832
|
};
|
|
785
833
|
fail("Unexpected " + JSON.stringify(c) + " in the tag <" + tag + ">: expected an attribute, or > to end the tag", at);
|
|
786
834
|
}
|
|
787
|
-
function peg$
|
|
835
|
+
function peg$f72(tag) {
|
|
788
836
|
return lexeme("html", location(), {
|
|
789
837
|
tag,
|
|
790
838
|
lower: tag.toLowerCase(),
|
|
@@ -793,44 +841,44 @@ function peg$parse(input, options) {
|
|
|
793
841
|
isSelfClose: false
|
|
794
842
|
});
|
|
795
843
|
}
|
|
796
|
-
function peg$
|
|
844
|
+
function peg$f73(name) {
|
|
797
845
|
attrCode = hooks.isCodeAttribute(name);
|
|
798
846
|
return true;
|
|
799
847
|
}
|
|
800
|
-
function peg$
|
|
848
|
+
function peg$f74(name, v) {
|
|
801
849
|
return v;
|
|
802
850
|
}
|
|
803
|
-
function peg$
|
|
851
|
+
function peg$f75(name, value) {
|
|
804
852
|
return [name, value ?? ""];
|
|
805
853
|
}
|
|
806
|
-
function peg$
|
|
854
|
+
function peg$f76(v) {
|
|
807
855
|
return v;
|
|
808
856
|
}
|
|
809
|
-
function peg$
|
|
857
|
+
function peg$f77(v) {
|
|
810
858
|
return v;
|
|
811
859
|
}
|
|
812
|
-
function peg$
|
|
860
|
+
function peg$f78(q) {
|
|
813
861
|
fail("Unclosed attribute value: the " + q + " here has no closing " + q, location());
|
|
814
862
|
}
|
|
815
|
-
function peg$
|
|
863
|
+
function peg$f79() {
|
|
816
864
|
return !attrCode;
|
|
817
865
|
}
|
|
818
|
-
function peg$
|
|
866
|
+
function peg$f80(bs) {
|
|
819
867
|
return bs.length % 2 === 1;
|
|
820
868
|
}
|
|
821
|
-
function peg$
|
|
869
|
+
function peg$f81() {
|
|
822
870
|
return !attrCode;
|
|
823
871
|
}
|
|
824
|
-
function peg$
|
|
872
|
+
function peg$f82(start, nameEnd) {
|
|
825
873
|
return skipTo(hooks.closeBrace(input, nameEnd, start));
|
|
826
874
|
}
|
|
827
|
-
function peg$
|
|
875
|
+
function peg$f83(start, nameEnd) {
|
|
828
876
|
return skipEnd - offset();
|
|
829
877
|
}
|
|
830
|
-
function peg$
|
|
878
|
+
function peg$f84() {
|
|
831
879
|
return !attrCode;
|
|
832
880
|
}
|
|
833
|
-
function peg$
|
|
881
|
+
function peg$f85() {
|
|
834
882
|
return attrCode;
|
|
835
883
|
}
|
|
836
884
|
let peg$currPos = options.peg$currPos | 0;
|
|
@@ -1719,7 +1767,7 @@ function peg$parse(input, options) {
|
|
|
1719
1767
|
return s0;
|
|
1720
1768
|
}
|
|
1721
1769
|
function peg$parseLinkLexeme() {
|
|
1722
|
-
let s0, s1, s2, s3, s4, s5;
|
|
1770
|
+
let s0, s1, s2, s3, s4, s5, s6;
|
|
1723
1771
|
s0 = peg$currPos;
|
|
1724
1772
|
peg$savedPos = peg$currPos;
|
|
1725
1773
|
s1 = peg$f48();
|
|
@@ -1736,19 +1784,20 @@ function peg$parse(input, options) {
|
|
|
1736
1784
|
if (s2 !== peg$FAILED) {
|
|
1737
1785
|
s3 = peg$parseSelectors();
|
|
1738
1786
|
if (s3 !== peg$FAILED) {
|
|
1739
|
-
s4 = peg$
|
|
1740
|
-
s5 = peg$
|
|
1741
|
-
|
|
1787
|
+
s4 = peg$parseHere();
|
|
1788
|
+
s5 = peg$currPos;
|
|
1789
|
+
s6 = peg$parseLinkText();
|
|
1790
|
+
s5 = input.substring(s5, peg$currPos);
|
|
1742
1791
|
if (input.substr(peg$currPos, 2) === peg$c5) {
|
|
1743
|
-
|
|
1792
|
+
s6 = peg$c5;
|
|
1744
1793
|
peg$currPos += 2;
|
|
1745
1794
|
} else {
|
|
1746
|
-
|
|
1795
|
+
s6 = peg$FAILED;
|
|
1747
1796
|
if (peg$silentFails === 0) peg$fail(peg$e9);
|
|
1748
1797
|
}
|
|
1749
|
-
if (
|
|
1798
|
+
if (s6 !== peg$FAILED) {
|
|
1750
1799
|
peg$savedPos = s0;
|
|
1751
|
-
s0 = peg$f49(s3, s4);
|
|
1800
|
+
s0 = peg$f49(s3, s4, s5);
|
|
1752
1801
|
} else {
|
|
1753
1802
|
peg$currPos = s0;
|
|
1754
1803
|
s0 = peg$FAILED;
|
|
@@ -1919,6 +1968,38 @@ function peg$parse(input, options) {
|
|
|
1919
1968
|
}
|
|
1920
1969
|
return s0;
|
|
1921
1970
|
}
|
|
1971
|
+
function peg$parseSelectorsPrefix() {
|
|
1972
|
+
let s0, s1, s2, s3, s4;
|
|
1973
|
+
s0 = peg$currPos;
|
|
1974
|
+
s1 = peg$parseSelectors();
|
|
1975
|
+
if (s1 !== peg$FAILED) {
|
|
1976
|
+
s2 = peg$parseHere();
|
|
1977
|
+
s3 = [];
|
|
1978
|
+
if (input.length > peg$currPos) {
|
|
1979
|
+
s4 = input.charAt(peg$currPos);
|
|
1980
|
+
peg$currPos++;
|
|
1981
|
+
} else {
|
|
1982
|
+
s4 = peg$FAILED;
|
|
1983
|
+
if (peg$silentFails === 0) peg$fail(peg$e0);
|
|
1984
|
+
}
|
|
1985
|
+
while (s4 !== peg$FAILED) {
|
|
1986
|
+
s3.push(s4);
|
|
1987
|
+
if (input.length > peg$currPos) {
|
|
1988
|
+
s4 = input.charAt(peg$currPos);
|
|
1989
|
+
peg$currPos++;
|
|
1990
|
+
} else {
|
|
1991
|
+
s4 = peg$FAILED;
|
|
1992
|
+
if (peg$silentFails === 0) peg$fail(peg$e0);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
peg$savedPos = s0;
|
|
1996
|
+
s0 = peg$f54(s1, s2);
|
|
1997
|
+
} else {
|
|
1998
|
+
peg$currPos = s0;
|
|
1999
|
+
s0 = peg$FAILED;
|
|
2000
|
+
}
|
|
2001
|
+
return s0;
|
|
2002
|
+
}
|
|
1922
2003
|
function peg$parseSelectorPart() {
|
|
1923
2004
|
let s0, s1, s2, s3, s4;
|
|
1924
2005
|
s0 = peg$currPos;
|
|
@@ -1938,7 +2019,7 @@ function peg$parse(input, options) {
|
|
|
1938
2019
|
}
|
|
1939
2020
|
s2 = input.substring(s2, peg$currPos);
|
|
1940
2021
|
peg$savedPos = s0;
|
|
1941
|
-
s0 = peg$
|
|
2022
|
+
s0 = peg$f55(s1, s2);
|
|
1942
2023
|
} else {
|
|
1943
2024
|
peg$currPos = s0;
|
|
1944
2025
|
s0 = peg$FAILED;
|
|
@@ -2063,7 +2144,7 @@ function peg$parse(input, options) {
|
|
|
2063
2144
|
}
|
|
2064
2145
|
if (s5 !== peg$FAILED) {
|
|
2065
2146
|
peg$savedPos = s0;
|
|
2066
|
-
s0 = peg$
|
|
2147
|
+
s0 = peg$f56(s2, s3, s4);
|
|
2067
2148
|
} else {
|
|
2068
2149
|
peg$currPos = s0;
|
|
2069
2150
|
s0 = peg$FAILED;
|
|
@@ -2117,7 +2198,7 @@ function peg$parse(input, options) {
|
|
|
2117
2198
|
}
|
|
2118
2199
|
if (s5 !== peg$FAILED) {
|
|
2119
2200
|
peg$savedPos = s0;
|
|
2120
|
-
s0 = peg$
|
|
2201
|
+
s0 = peg$f57(s2, s4);
|
|
2121
2202
|
} else {
|
|
2122
2203
|
peg$currPos = s0;
|
|
2123
2204
|
s0 = peg$FAILED;
|
|
@@ -2165,7 +2246,7 @@ function peg$parse(input, options) {
|
|
|
2165
2246
|
} else s3 = peg$FAILED;
|
|
2166
2247
|
if (s3 !== peg$FAILED) {
|
|
2167
2248
|
peg$savedPos = s0;
|
|
2168
|
-
s0 = peg$
|
|
2249
|
+
s0 = peg$f58();
|
|
2169
2250
|
} else {
|
|
2170
2251
|
peg$currPos = s0;
|
|
2171
2252
|
s0 = peg$FAILED;
|
|
@@ -2187,12 +2268,12 @@ function peg$parse(input, options) {
|
|
|
2187
2268
|
s0 = peg$currPos;
|
|
2188
2269
|
s1 = peg$parseHere();
|
|
2189
2270
|
peg$savedPos = peg$currPos;
|
|
2190
|
-
s2 = peg$
|
|
2271
|
+
s2 = peg$f59(s1);
|
|
2191
2272
|
if (s2) s2 = void 0;
|
|
2192
2273
|
else s2 = peg$FAILED;
|
|
2193
2274
|
if (s2 !== peg$FAILED) {
|
|
2194
2275
|
s3 = peg$currPos;
|
|
2195
|
-
s4 = peg$
|
|
2276
|
+
s4 = peg$f60(s1);
|
|
2196
2277
|
s5 = peg$currPos;
|
|
2197
2278
|
s6 = [];
|
|
2198
2279
|
if (s6.length >= (s4 | 0)) s7 = peg$FAILED;
|
|
@@ -2223,7 +2304,7 @@ function peg$parse(input, options) {
|
|
|
2223
2304
|
else s3 = s4;
|
|
2224
2305
|
if (s3 !== peg$FAILED) {
|
|
2225
2306
|
peg$savedPos = s0;
|
|
2226
|
-
s0 = peg$
|
|
2307
|
+
s0 = peg$f61(s1, s3);
|
|
2227
2308
|
} else {
|
|
2228
2309
|
peg$currPos = s0;
|
|
2229
2310
|
s0 = peg$FAILED;
|
|
@@ -2261,12 +2342,12 @@ function peg$parse(input, options) {
|
|
|
2261
2342
|
s6 = peg$parseHere();
|
|
2262
2343
|
s7 = peg$currPos;
|
|
2263
2344
|
peg$savedPos = peg$currPos;
|
|
2264
|
-
s8 = peg$
|
|
2345
|
+
s8 = peg$f62(s2, s3, s4, s5, s6);
|
|
2265
2346
|
if (s8) s8 = void 0;
|
|
2266
2347
|
else s8 = peg$FAILED;
|
|
2267
2348
|
if (s8 !== peg$FAILED) {
|
|
2268
2349
|
s9 = peg$currPos;
|
|
2269
|
-
s10 = peg$
|
|
2350
|
+
s10 = peg$f63(s2, s3, s4, s5, s6);
|
|
2270
2351
|
s11 = peg$currPos;
|
|
2271
2352
|
s12 = [];
|
|
2272
2353
|
if (s12.length >= (s10 | 0)) s13 = peg$FAILED;
|
|
@@ -2305,7 +2386,7 @@ function peg$parse(input, options) {
|
|
|
2305
2386
|
}
|
|
2306
2387
|
if (s10 !== peg$FAILED) {
|
|
2307
2388
|
peg$savedPos = s7;
|
|
2308
|
-
s7 = peg$
|
|
2389
|
+
s7 = peg$f64(s2, s3, s4, s5, s6, s9);
|
|
2309
2390
|
} else {
|
|
2310
2391
|
peg$currPos = s7;
|
|
2311
2392
|
s7 = peg$FAILED;
|
|
@@ -2320,7 +2401,7 @@ function peg$parse(input, options) {
|
|
|
2320
2401
|
}
|
|
2321
2402
|
if (s7 === peg$FAILED) s7 = null;
|
|
2322
2403
|
peg$savedPos = s0;
|
|
2323
|
-
s0 = peg$
|
|
2404
|
+
s0 = peg$f65(s2, s3, s4, s5, s6, s7);
|
|
2324
2405
|
} else {
|
|
2325
2406
|
peg$currPos = s0;
|
|
2326
2407
|
s0 = peg$FAILED;
|
|
@@ -2354,7 +2435,7 @@ function peg$parse(input, options) {
|
|
|
2354
2435
|
}
|
|
2355
2436
|
if (s3 !== peg$FAILED) {
|
|
2356
2437
|
peg$savedPos = s0;
|
|
2357
|
-
s0 = peg$
|
|
2438
|
+
s0 = peg$f66(s2);
|
|
2358
2439
|
} else {
|
|
2359
2440
|
peg$currPos = s0;
|
|
2360
2441
|
s0 = peg$FAILED;
|
|
@@ -2411,19 +2492,19 @@ function peg$parse(input, options) {
|
|
|
2411
2492
|
let s0, s1, s2, s3;
|
|
2412
2493
|
s0 = peg$currPos;
|
|
2413
2494
|
peg$savedPos = peg$currPos;
|
|
2414
|
-
s1 = peg$
|
|
2495
|
+
s1 = peg$f67();
|
|
2415
2496
|
if (s1) s1 = void 0;
|
|
2416
2497
|
else s1 = peg$FAILED;
|
|
2417
2498
|
if (s1 !== peg$FAILED) {
|
|
2418
2499
|
s2 = peg$parseHtmlClose();
|
|
2419
2500
|
if (s2 !== peg$FAILED) {
|
|
2420
2501
|
peg$savedPos = peg$currPos;
|
|
2421
|
-
s3 = peg$
|
|
2502
|
+
s3 = peg$f68(s2);
|
|
2422
2503
|
if (s3) s3 = void 0;
|
|
2423
2504
|
else s3 = peg$FAILED;
|
|
2424
2505
|
if (s3 !== peg$FAILED) {
|
|
2425
2506
|
peg$savedPos = s0;
|
|
2426
|
-
s0 = peg$
|
|
2507
|
+
s0 = peg$f69(s2);
|
|
2427
2508
|
} else {
|
|
2428
2509
|
peg$currPos = s0;
|
|
2429
2510
|
s0 = peg$FAILED;
|
|
@@ -2475,7 +2556,7 @@ function peg$parse(input, options) {
|
|
|
2475
2556
|
}
|
|
2476
2557
|
if (s6 !== peg$FAILED) {
|
|
2477
2558
|
peg$savedPos = s0;
|
|
2478
|
-
s0 = peg$
|
|
2559
|
+
s0 = peg$f70(s2, s3, s5);
|
|
2479
2560
|
} else {
|
|
2480
2561
|
peg$currPos = s0;
|
|
2481
2562
|
s0 = peg$FAILED;
|
|
@@ -2540,7 +2621,7 @@ function peg$parse(input, options) {
|
|
|
2540
2621
|
s5 = peg$parse_();
|
|
2541
2622
|
s6 = peg$parseHere();
|
|
2542
2623
|
peg$savedPos = s0;
|
|
2543
|
-
s0 = peg$
|
|
2624
|
+
s0 = peg$f71(s2, s6);
|
|
2544
2625
|
} else {
|
|
2545
2626
|
peg$currPos = s0;
|
|
2546
2627
|
s0 = peg$FAILED;
|
|
@@ -2579,7 +2660,7 @@ function peg$parse(input, options) {
|
|
|
2579
2660
|
}
|
|
2580
2661
|
if (s4 !== peg$FAILED) {
|
|
2581
2662
|
peg$savedPos = s0;
|
|
2582
|
-
s0 = peg$
|
|
2663
|
+
s0 = peg$f72(s2);
|
|
2583
2664
|
} else {
|
|
2584
2665
|
peg$currPos = s0;
|
|
2585
2666
|
s0 = peg$FAILED;
|
|
@@ -2713,7 +2794,7 @@ function peg$parse(input, options) {
|
|
|
2713
2794
|
else s2 = s3;
|
|
2714
2795
|
if (s2 !== peg$FAILED) {
|
|
2715
2796
|
peg$savedPos = peg$currPos;
|
|
2716
|
-
s3 = peg$
|
|
2797
|
+
s3 = peg$f73(s2);
|
|
2717
2798
|
if (s3) s3 = void 0;
|
|
2718
2799
|
else s3 = peg$FAILED;
|
|
2719
2800
|
if (s3 !== peg$FAILED) {
|
|
@@ -2731,7 +2812,7 @@ function peg$parse(input, options) {
|
|
|
2731
2812
|
s8 = peg$parseAttrValue();
|
|
2732
2813
|
if (s8 !== peg$FAILED) {
|
|
2733
2814
|
peg$savedPos = s4;
|
|
2734
|
-
s4 = peg$
|
|
2815
|
+
s4 = peg$f74(s2, s8);
|
|
2735
2816
|
} else {
|
|
2736
2817
|
peg$currPos = s4;
|
|
2737
2818
|
s4 = peg$FAILED;
|
|
@@ -2742,7 +2823,7 @@ function peg$parse(input, options) {
|
|
|
2742
2823
|
}
|
|
2743
2824
|
if (s4 === peg$FAILED) s4 = null;
|
|
2744
2825
|
peg$savedPos = s0;
|
|
2745
|
-
s0 = peg$
|
|
2826
|
+
s0 = peg$f75(s2, s4);
|
|
2746
2827
|
} else {
|
|
2747
2828
|
peg$currPos = s0;
|
|
2748
2829
|
s0 = peg$FAILED;
|
|
@@ -2797,7 +2878,7 @@ function peg$parse(input, options) {
|
|
|
2797
2878
|
}
|
|
2798
2879
|
if (s3 !== peg$FAILED) {
|
|
2799
2880
|
peg$savedPos = s0;
|
|
2800
|
-
s0 = peg$
|
|
2881
|
+
s0 = peg$f76(s2);
|
|
2801
2882
|
} else {
|
|
2802
2883
|
peg$currPos = s0;
|
|
2803
2884
|
s0 = peg$FAILED;
|
|
@@ -2849,7 +2930,7 @@ function peg$parse(input, options) {
|
|
|
2849
2930
|
}
|
|
2850
2931
|
if (s3 !== peg$FAILED) {
|
|
2851
2932
|
peg$savedPos = s0;
|
|
2852
|
-
s0 = peg$
|
|
2933
|
+
s0 = peg$f77(s2);
|
|
2853
2934
|
} else {
|
|
2854
2935
|
peg$currPos = s0;
|
|
2855
2936
|
s0 = peg$FAILED;
|
|
@@ -2868,7 +2949,7 @@ function peg$parse(input, options) {
|
|
|
2868
2949
|
}
|
|
2869
2950
|
if (s1 !== peg$FAILED) {
|
|
2870
2951
|
peg$savedPos = s0;
|
|
2871
|
-
s1 = peg$
|
|
2952
|
+
s1 = peg$f78(s1);
|
|
2872
2953
|
}
|
|
2873
2954
|
s0 = s1;
|
|
2874
2955
|
if (s0 === peg$FAILED) {
|
|
@@ -2899,7 +2980,7 @@ function peg$parse(input, options) {
|
|
|
2899
2980
|
let s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13;
|
|
2900
2981
|
s0 = peg$currPos;
|
|
2901
2982
|
peg$savedPos = peg$currPos;
|
|
2902
|
-
s1 = peg$
|
|
2983
|
+
s1 = peg$f79();
|
|
2903
2984
|
if (s1) s1 = void 0;
|
|
2904
2985
|
else s1 = peg$FAILED;
|
|
2905
2986
|
if (s1 !== peg$FAILED) {
|
|
@@ -2928,7 +3009,7 @@ function peg$parse(input, options) {
|
|
|
2928
3009
|
if (s2 !== peg$FAILED) {
|
|
2929
3010
|
s3 = peg$currPos;
|
|
2930
3011
|
peg$savedPos = peg$currPos;
|
|
2931
|
-
s4 = peg$
|
|
3012
|
+
s4 = peg$f80(s2);
|
|
2932
3013
|
if (s4) s4 = void 0;
|
|
2933
3014
|
else s4 = peg$FAILED;
|
|
2934
3015
|
if (s4 !== peg$FAILED) {
|
|
@@ -2967,7 +3048,7 @@ function peg$parse(input, options) {
|
|
|
2967
3048
|
if (s0 === peg$FAILED) {
|
|
2968
3049
|
s0 = peg$currPos;
|
|
2969
3050
|
peg$savedPos = peg$currPos;
|
|
2970
|
-
s1 = peg$
|
|
3051
|
+
s1 = peg$f81();
|
|
2971
3052
|
if (s1) s1 = void 0;
|
|
2972
3053
|
else s1 = peg$FAILED;
|
|
2973
3054
|
if (s1 !== peg$FAILED) {
|
|
@@ -2994,12 +3075,12 @@ function peg$parse(input, options) {
|
|
|
2994
3075
|
if (s6 !== peg$FAILED) {
|
|
2995
3076
|
s7 = peg$parseHere();
|
|
2996
3077
|
peg$savedPos = peg$currPos;
|
|
2997
|
-
s8 = peg$
|
|
3078
|
+
s8 = peg$f82(s4, s7);
|
|
2998
3079
|
if (s8) s8 = void 0;
|
|
2999
3080
|
else s8 = peg$FAILED;
|
|
3000
3081
|
if (s8 !== peg$FAILED) {
|
|
3001
3082
|
s9 = peg$currPos;
|
|
3002
|
-
s10 = peg$
|
|
3083
|
+
s10 = peg$f83(s4, s7);
|
|
3003
3084
|
s11 = peg$currPos;
|
|
3004
3085
|
s12 = [];
|
|
3005
3086
|
if (s12.length >= (s10 | 0)) s13 = peg$FAILED;
|
|
@@ -3081,7 +3162,7 @@ function peg$parse(input, options) {
|
|
|
3081
3162
|
if (s0 === peg$FAILED) {
|
|
3082
3163
|
s0 = peg$currPos;
|
|
3083
3164
|
peg$savedPos = peg$currPos;
|
|
3084
|
-
s1 = peg$
|
|
3165
|
+
s1 = peg$f84();
|
|
3085
3166
|
if (s1) s1 = void 0;
|
|
3086
3167
|
else s1 = peg$FAILED;
|
|
3087
3168
|
if (s1 !== peg$FAILED) {
|
|
@@ -3172,7 +3253,7 @@ function peg$parse(input, options) {
|
|
|
3172
3253
|
if (s0 === peg$FAILED) {
|
|
3173
3254
|
s0 = peg$currPos;
|
|
3174
3255
|
peg$savedPos = peg$currPos;
|
|
3175
|
-
s1 = peg$
|
|
3256
|
+
s1 = peg$f85();
|
|
3176
3257
|
if (s1) s1 = void 0;
|
|
3177
3258
|
else s1 = peg$FAILED;
|
|
3178
3259
|
if (s1 !== peg$FAILED) {
|
|
@@ -3403,21 +3484,6 @@ function isBlockMacro(name) {
|
|
|
3403
3484
|
return BLOCK_MACROS.has(name);
|
|
3404
3485
|
}
|
|
3405
3486
|
//#endregion
|
|
3406
|
-
//#region src/markup/tokens.ts
|
|
3407
|
-
/** Variable sigils: story ($), temporary (_), local (@), transient (%). */
|
|
3408
|
-
var SIGIL_SCOPES = {
|
|
3409
|
-
$: "variable",
|
|
3410
|
-
_: "temporary",
|
|
3411
|
-
"@": "local",
|
|
3412
|
-
"%": "transient"
|
|
3413
|
-
};
|
|
3414
|
-
Object.fromEntries(Object.entries(SIGIL_SCOPES).map(([sigil, scope]) => [scope, sigil]));
|
|
3415
|
-
var SIGIL_CHARS = new Set(Object.keys(SIGIL_SCOPES));
|
|
3416
|
-
/** Whether `c` is a variable sigil. */
|
|
3417
|
-
function isSigil(c) {
|
|
3418
|
-
return c !== void 0 && SIGIL_CHARS.has(c);
|
|
3419
|
-
}
|
|
3420
|
-
//#endregion
|
|
3421
3487
|
//#region node_modules/acorn/dist/acorn.mjs
|
|
3422
3488
|
var astralIdentifierCodes = [
|
|
3423
3489
|
509,
|
|
@@ -9239,6 +9305,14 @@ function* tokens(src, start, goal, options = {}) {
|
|
|
9239
9305
|
function lexJs(src, handlers, goal = "expression") {
|
|
9240
9306
|
return walkTokens(src, 0, handlers, 0, goal, false);
|
|
9241
9307
|
}
|
|
9308
|
+
/**
|
|
9309
|
+
* Lex the template literal opening at `start` (a backtick) as `lexJs` does,
|
|
9310
|
+
* its interpolations one nesting level deeper. Returns the index just past
|
|
9311
|
+
* its closing backtick, or `src.length` if it is unterminated.
|
|
9312
|
+
*/
|
|
9313
|
+
function lexTemplate(src, start, handlers = {}, nesting = 0) {
|
|
9314
|
+
return walkTokens(src, start, handlers, nesting, "expression", true);
|
|
9315
|
+
}
|
|
9242
9316
|
function walkTokens(src, from, handlers, nesting, goal, oneTemplate) {
|
|
9243
9317
|
const comments = [];
|
|
9244
9318
|
const onComment = (_block, _text, s, e) => {
|
|
@@ -9356,6 +9430,22 @@ function regexEnd(src, start) {
|
|
|
9356
9430
|
return -1;
|
|
9357
9431
|
}
|
|
9358
9432
|
/**
|
|
9433
|
+
* The value of `src` when it is exactly one `"…"` or `'…'` string literal,
|
|
9434
|
+
* as JavaScript reads it (`"\u0048all"` is `Hall`, `"\1"` is U+0001 in
|
|
9435
|
+
* the non-strict code passages run), otherwise `null`: other code, or a
|
|
9436
|
+
* literal that is not well-formed (`"\u{110000}"`).
|
|
9437
|
+
*/
|
|
9438
|
+
function stringLiteralValue(src) {
|
|
9439
|
+
if (src[0] !== "\"" && src[0] !== "'") return null;
|
|
9440
|
+
try {
|
|
9441
|
+
const p = tokenizerAt(src, 0, "expression");
|
|
9442
|
+
p.nextToken();
|
|
9443
|
+
return p.type === types$1.string && p.end === src.length ? p.value : null;
|
|
9444
|
+
} catch {
|
|
9445
|
+
return null;
|
|
9446
|
+
}
|
|
9447
|
+
}
|
|
9448
|
+
/**
|
|
9359
9449
|
* Scan the `"…"` or `'…'` string literal opening at `start`. `end` is the
|
|
9360
9450
|
* index just past its closing quote, or `src.length` when it is unterminated
|
|
9361
9451
|
* (`closed` false). A backslash escapes the character after it, so a quote
|
|
@@ -9591,6 +9681,98 @@ function parseMarkup(source, options = {}) {
|
|
|
9591
9681
|
function tokenizeMarkup(source, options = {}) {
|
|
9592
9682
|
return run(source, "Tokens", options);
|
|
9593
9683
|
}
|
|
9684
|
+
/**
|
|
9685
|
+
* The `.class#id` selectors that start `source` at `at`, with the index just
|
|
9686
|
+
* past them and the one space that may follow (`end`; `at` if there are
|
|
9687
|
+
* none). A selector name may hold `{$name}` interpolations, as in markup.
|
|
9688
|
+
*/
|
|
9689
|
+
function parseSelectors(source, at = 0) {
|
|
9690
|
+
const found = run(source.slice(at), "SelectorsPrefix", {});
|
|
9691
|
+
return {
|
|
9692
|
+
...found,
|
|
9693
|
+
end: at + found.end
|
|
9694
|
+
};
|
|
9695
|
+
}
|
|
9696
|
+
/**
|
|
9697
|
+
* `tokenizeMarkup` for half-typed markup: the tokens it can read, and every
|
|
9698
|
+
* error, instead of throwing at the first. At a malformed tag, the tokens
|
|
9699
|
+
* before it are kept, its first character is read as text and tokenizing
|
|
9700
|
+
* resumes after it, so what follows is read as if the tag had not been
|
|
9701
|
+
* started. Tokens and errors have offsets in `source`; for well-formed
|
|
9702
|
+
* markup the tokens are those of `tokenizeMarkup` and there are no errors.
|
|
9703
|
+
*/
|
|
9704
|
+
function tokenizeMarkupTolerant(source, options = {}) {
|
|
9705
|
+
const tokens = [];
|
|
9706
|
+
const errors = [];
|
|
9707
|
+
const lastBrace = source.lastIndexOf("}");
|
|
9708
|
+
const { closeBrace } = {
|
|
9709
|
+
...defaultHooks,
|
|
9710
|
+
...options.hooks
|
|
9711
|
+
};
|
|
9712
|
+
/** `text` shifted by `base`, tokenized: the tokens or the error. */
|
|
9713
|
+
const attempt = (text, base) => {
|
|
9714
|
+
const hooks = {
|
|
9715
|
+
...options.hooks,
|
|
9716
|
+
closeBrace: (input, codeStart, lenientStart) => base + Math.min(codeStart, lenientStart) > lastBrace ? -1 : closeBrace(input, codeStart, lenientStart)
|
|
9717
|
+
};
|
|
9718
|
+
try {
|
|
9719
|
+
return tokenizeMarkup(text, {
|
|
9720
|
+
...options,
|
|
9721
|
+
hooks
|
|
9722
|
+
}).map((token) => shift(token, base));
|
|
9723
|
+
} catch (error) {
|
|
9724
|
+
if (!(error instanceof MarkupError)) throw error;
|
|
9725
|
+
return error;
|
|
9726
|
+
}
|
|
9727
|
+
};
|
|
9728
|
+
let base = 0;
|
|
9729
|
+
while (base <= source.length) {
|
|
9730
|
+
const rest = source.slice(base);
|
|
9731
|
+
const result = attempt(rest, base);
|
|
9732
|
+
if (Array.isArray(result)) {
|
|
9733
|
+
tokens.push(...result);
|
|
9734
|
+
break;
|
|
9735
|
+
}
|
|
9736
|
+
const at = base + result.offset;
|
|
9737
|
+
const { line, column } = lineColumn(source, at);
|
|
9738
|
+
errors.push(new MarkupError(result.reason, at, line, column));
|
|
9739
|
+
let cut = result.offset;
|
|
9740
|
+
let prefix = attempt(rest.slice(0, cut), base);
|
|
9741
|
+
while (!Array.isArray(prefix)) {
|
|
9742
|
+
cut = prefix.offset;
|
|
9743
|
+
prefix = attempt(rest.slice(0, cut), base);
|
|
9744
|
+
}
|
|
9745
|
+
tokens.push(...prefix);
|
|
9746
|
+
if (cut < rest.length) {
|
|
9747
|
+
const start = base + cut;
|
|
9748
|
+
tokens.push({
|
|
9749
|
+
type: "text",
|
|
9750
|
+
value: rest[cut],
|
|
9751
|
+
start,
|
|
9752
|
+
end: start + 1
|
|
9753
|
+
});
|
|
9754
|
+
}
|
|
9755
|
+
base += cut + 1;
|
|
9756
|
+
}
|
|
9757
|
+
return {
|
|
9758
|
+
tokens,
|
|
9759
|
+
errors
|
|
9760
|
+
};
|
|
9761
|
+
}
|
|
9762
|
+
/** `token` with its offsets moved `by` on. */
|
|
9763
|
+
function shift(token, by) {
|
|
9764
|
+
if (by === 0) return token;
|
|
9765
|
+
const moved = {
|
|
9766
|
+
...token,
|
|
9767
|
+
start: token.start + by,
|
|
9768
|
+
end: token.end + by
|
|
9769
|
+
};
|
|
9770
|
+
if (moved.type === "link") {
|
|
9771
|
+
moved.targetStart += by;
|
|
9772
|
+
moved.targetEnd += by;
|
|
9773
|
+
}
|
|
9774
|
+
return moved;
|
|
9775
|
+
}
|
|
9594
9776
|
//#endregion
|
|
9595
9777
|
//#region src/utils/parse-delay.ts
|
|
9596
9778
|
/**
|
|
@@ -9618,6 +9800,15 @@ function errorMessage(err, fallback) {
|
|
|
9618
9800
|
return fallback ?? String(err);
|
|
9619
9801
|
}
|
|
9620
9802
|
//#endregion
|
|
9803
|
+
//#region src/runtime-errors.ts
|
|
9804
|
+
/**
|
|
9805
|
+
* The error for `name`, a passage name that names no passage, used in the
|
|
9806
|
+
* passage `from`.
|
|
9807
|
+
*/
|
|
9808
|
+
function noPassageError(name, from) {
|
|
9809
|
+
return /* @__PURE__ */ new Error(`No passage named ${JSON.stringify(name)} (in passage ${JSON.stringify(from)})`);
|
|
9810
|
+
}
|
|
9811
|
+
//#endregion
|
|
9621
9812
|
//#region src/components/macros/arg-utils.ts
|
|
9622
9813
|
/**
|
|
9623
9814
|
* Shared helpers for macros that split or read their raw argument text by
|
|
@@ -9720,6 +9911,67 @@ function topLevelIndices(src, isSeparator) {
|
|
|
9720
9911
|
} });
|
|
9721
9912
|
return indices;
|
|
9722
9913
|
}
|
|
9914
|
+
/**
|
|
9915
|
+
* Split `src` at every depth-0 separator (see `topLevelIndices`). Segments
|
|
9916
|
+
* are returned as written, untrimmed, and empty segments are kept, so
|
|
9917
|
+
* `n` separators always give `n + 1` segments.
|
|
9918
|
+
*/
|
|
9919
|
+
function splitTopLevel(src, isSeparator) {
|
|
9920
|
+
const segments = [];
|
|
9921
|
+
let from = 0;
|
|
9922
|
+
for (const i of topLevelIndices(src, isSeparator)) {
|
|
9923
|
+
segments.push(src.slice(from, i));
|
|
9924
|
+
from = i + 1;
|
|
9925
|
+
}
|
|
9926
|
+
segments.push(src.slice(from));
|
|
9927
|
+
return segments;
|
|
9928
|
+
}
|
|
9929
|
+
/**
|
|
9930
|
+
* Check whether a whitespace-delimited token looks like a standalone value
|
|
9931
|
+
* (not an operator or partial expression): it starts like a value and does
|
|
9932
|
+
* not end with an operator still waiting for its operand.
|
|
9933
|
+
*/
|
|
9934
|
+
function isStandaloneValue(token) {
|
|
9935
|
+
return startsLikeValue(token) && !endsWithOperator(token);
|
|
9936
|
+
}
|
|
9937
|
+
function startsLikeValue(token) {
|
|
9938
|
+
const first = token[0];
|
|
9939
|
+
if (first === "\"" || first === "'" || first === "`") return true;
|
|
9940
|
+
if (/^[$_@]\w|^%[A-Za-z_]/.test(token)) return true;
|
|
9941
|
+
if (/\d/.test(first)) return true;
|
|
9942
|
+
if ((first === "-" || first === "+") && token.length > 1 && /\d/.test(token[1])) return true;
|
|
9943
|
+
if (first === "(" || first === "[" || first === "{") return true;
|
|
9944
|
+
if (/^(?:true|false|null|undefined)(?![\w$])/.test(token)) return true;
|
|
9945
|
+
if (first === "!" && token.length > 1 && token[1] !== "=") return true;
|
|
9946
|
+
return false;
|
|
9947
|
+
}
|
|
9948
|
+
/**
|
|
9949
|
+
* Try to split a raw string on whitespace at depth 0 (respecting strings,
|
|
9950
|
+
* template literals, parentheses, brackets, and braces). Each resulting token
|
|
9951
|
+
* must pass `isStandaloneValue()` or the split is rejected and `null` is
|
|
9952
|
+
* returned.
|
|
9953
|
+
*/
|
|
9954
|
+
function trySplitOnWhitespace(raw) {
|
|
9955
|
+
const args = splitTopLevel(raw, isWhitespace).filter(Boolean);
|
|
9956
|
+
if (args.length < 2) return null;
|
|
9957
|
+
for (const arg of args) if (!isStandaloneValue(arg)) return null;
|
|
9958
|
+
return args;
|
|
9959
|
+
}
|
|
9960
|
+
/**
|
|
9961
|
+
* Split rawArgs by commas, respecting parentheses, brackets, braces, and
|
|
9962
|
+
* strings. When no top-level commas are present, also supports adjacent quoted
|
|
9963
|
+
* string literals separated by whitespace (e.g. `"Label" "target"`).
|
|
9964
|
+
*/
|
|
9965
|
+
function splitArgs(raw) {
|
|
9966
|
+
const args = splitTopLevel(raw, (ch) => ch === ",").map((a) => a.trim());
|
|
9967
|
+
const hasComma = args.length > 1;
|
|
9968
|
+
if (args[args.length - 1] === "") args.pop();
|
|
9969
|
+
if (!hasComma && args.length === 1) {
|
|
9970
|
+
const split = trySplitOnWhitespace(args[0]);
|
|
9971
|
+
if (split) return split;
|
|
9972
|
+
}
|
|
9973
|
+
return args;
|
|
9974
|
+
}
|
|
9723
9975
|
//#endregion
|
|
9724
9976
|
//#region src/components/macros/macro-args.ts
|
|
9725
9977
|
/**
|
|
@@ -9762,11 +10014,13 @@ function readText(src) {
|
|
|
9762
10014
|
*/
|
|
9763
10015
|
var MacroArgumentError = class extends Error {
|
|
9764
10016
|
constructor(param, src) {
|
|
9765
|
-
super(`The ${param.name} must be a quoted string ("…" or '…'), not ${src}`);
|
|
10017
|
+
super(`The ${param.name} must be a quoted string ("…" or '…'), ${src ? `not ${src}` : "but it has no value"}`);
|
|
9766
10018
|
this.name = "MacroArgumentError";
|
|
9767
10019
|
}
|
|
9768
10020
|
};
|
|
9769
|
-
|
|
10021
|
+
/** Read `src`, at `place`, as `param` says. */
|
|
10022
|
+
function readValue(param, src, place) {
|
|
10023
|
+
place.spans?.set(param, [place.at, place.at + src.length]);
|
|
9770
10024
|
switch (param.type) {
|
|
9771
10025
|
case "string": {
|
|
9772
10026
|
const value = readWholeQuoted(src);
|
|
@@ -9777,7 +10031,7 @@ function readValue(param, src) {
|
|
|
9777
10031
|
case "names": return src.split(",").map((name) => name.trim());
|
|
9778
10032
|
case "delay": return parseDelay(src);
|
|
9779
10033
|
case "number": return Number(src);
|
|
9780
|
-
case "options": return readOptions(param.parameters ?? [], src);
|
|
10034
|
+
case "options": return readOptions(param.parameters ?? [], src, place);
|
|
9781
10035
|
default: return src;
|
|
9782
10036
|
}
|
|
9783
10037
|
}
|
|
@@ -9787,6 +10041,7 @@ function unset(param) {
|
|
|
9787
10041
|
}
|
|
9788
10042
|
var WORD_RE = /\w+/y;
|
|
9789
10043
|
var DIGITS_RE = /\d+/y;
|
|
10044
|
+
var WORD_OR_PUNCT_RE = /\S+/y;
|
|
9790
10045
|
/** Match the sticky regex `re` at `pos`, returning the matched text. */
|
|
9791
10046
|
function matchAt(re, src, pos) {
|
|
9792
10047
|
re.lastIndex = pos;
|
|
@@ -9794,10 +10049,12 @@ function matchAt(re, src, pos) {
|
|
|
9794
10049
|
}
|
|
9795
10050
|
/**
|
|
9796
10051
|
* Read keyword options (`goto "X" priority 5 once`). A keyword takes the
|
|
9797
|
-
* quoted string or
|
|
9798
|
-
* declared, and their values, are
|
|
10052
|
+
* quoted string (with or without whitespace before it) or digit run after
|
|
10053
|
+
* it as its value; keywords that aren't declared, and their values, are
|
|
10054
|
+
* skipped. A keyword of a `string` parameter must be followed by a quoted
|
|
10055
|
+
* string.
|
|
9799
10056
|
*/
|
|
9800
|
-
function readOptions(params, src) {
|
|
10057
|
+
function readOptions(params, src, { at, spans }) {
|
|
9801
10058
|
const options = {};
|
|
9802
10059
|
let i = 0;
|
|
9803
10060
|
while (i < src.length) {
|
|
@@ -9810,18 +10067,18 @@ function readOptions(params, src) {
|
|
|
9810
10067
|
let val;
|
|
9811
10068
|
let j = i;
|
|
9812
10069
|
while (j < src.length && isWhitespace(src[j])) j++;
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
i = j + digits.length;
|
|
9822
|
-
}
|
|
10070
|
+
const quoted = readQuoted(src, j);
|
|
10071
|
+
const digits = quoted || j === i ? null : matchAt(DIGITS_RE, src, j);
|
|
10072
|
+
if (quoted) {
|
|
10073
|
+
val = quoted.value;
|
|
10074
|
+
i = quoted.end;
|
|
10075
|
+
} else if (digits) {
|
|
10076
|
+
val = digits;
|
|
10077
|
+
i = j + digits.length;
|
|
9823
10078
|
}
|
|
9824
10079
|
const param = params.find((p) => p.name === key);
|
|
10080
|
+
if (param && param.type !== "flag" && val !== void 0) spans?.set(param, [at + j, at + i]);
|
|
10081
|
+
if (param?.type === "string" && !quoted) throw new MacroArgumentError(param, matchAt(WORD_OR_PUNCT_RE, src, j) ?? "");
|
|
9825
10082
|
if (param?.type === "flag") options[key] = true;
|
|
9826
10083
|
else if (param?.type === "number") options[key] = Number(val ?? 0);
|
|
9827
10084
|
else if (param) options[key] = val;
|
|
@@ -9831,21 +10088,21 @@ function readOptions(params, src) {
|
|
|
9831
10088
|
/** `inline %name` reads a transient variable; `inline % 2` is modulo. */
|
|
9832
10089
|
var LEADING_OPERATOR_RE = /^(?:[-+*/&|^=<>?:,.]|%(?![A-Za-z_]))/;
|
|
9833
10090
|
/**
|
|
9834
|
-
*
|
|
9835
|
-
* `null` if it has no such flag. Next to a binary operator the
|
|
9836
|
-
* operand (`"a" + inline`), not a flag; the closing `/` of a
|
|
9837
|
-
* is not one.
|
|
10091
|
+
* Where the rest of `src` is without the flag `word` as its last or first
|
|
10092
|
+
* term, or `null` if it has no such flag. Next to a binary operator the
|
|
10093
|
+
* word is an operand (`"a" + inline`), not a flag; the closing `/` of a
|
|
10094
|
+
* regex literal is not one.
|
|
9838
10095
|
*/
|
|
9839
10096
|
function takeFlag(src, word) {
|
|
9840
10097
|
const spans = terms(src);
|
|
9841
10098
|
if (spans.length < 2) return null;
|
|
9842
10099
|
if (slice(src, spans[spans.length - 1]) === word) {
|
|
9843
|
-
const rest =
|
|
9844
|
-
if (!endsWithOperator(rest)) return rest;
|
|
10100
|
+
const rest = [0, spans[spans.length - 2][1]];
|
|
10101
|
+
if (!endsWithOperator(slice(src, rest))) return rest;
|
|
9845
10102
|
}
|
|
9846
10103
|
if (slice(src, spans[0]) === word) {
|
|
9847
|
-
const rest =
|
|
9848
|
-
if (!LEADING_OPERATOR_RE.test(rest)) return rest;
|
|
10104
|
+
const rest = [spans[1][0], src.length];
|
|
10105
|
+
if (!LEADING_OPERATOR_RE.test(slice(src, rest))) return rest;
|
|
9849
10106
|
}
|
|
9850
10107
|
return null;
|
|
9851
10108
|
}
|
|
@@ -9873,60 +10130,112 @@ function findSeparator(src, word) {
|
|
|
9873
10130
|
return null;
|
|
9874
10131
|
}
|
|
9875
10132
|
var takesRest = (param) => param.type === "expression" || param.type === "statements" || param.type === "passage" || param.type === "text";
|
|
9876
|
-
/**
|
|
9877
|
-
function
|
|
9878
|
-
|
|
10133
|
+
/** The text of `src` without the whitespace around it, and where it is. */
|
|
10134
|
+
function trimmed(src, at) {
|
|
10135
|
+
const text = src.trim();
|
|
10136
|
+
return [text, text ? at + src.indexOf(text) : at];
|
|
10137
|
+
}
|
|
10138
|
+
/**
|
|
10139
|
+
* Read the parameters of one group from `src` (`null`: no text), which is
|
|
10140
|
+
* at `at` in the raw arguments, into `args`.
|
|
10141
|
+
*/
|
|
10142
|
+
function readGroup(params, src, args, { at: srcAt, spans }) {
|
|
10143
|
+
let [text, at] = trimmed(src ?? "", srcAt);
|
|
9879
10144
|
for (const flag of params.filter((p) => p.type === "flag")) {
|
|
9880
10145
|
const rest = src === null ? null : takeFlag(text, flag.name);
|
|
9881
10146
|
args[flag.name] = rest !== null;
|
|
9882
|
-
text = rest
|
|
10147
|
+
if (rest) [text, at] = trimmed(slice(text, rest), at + rest[0]);
|
|
9883
10148
|
}
|
|
9884
10149
|
const positional = params.filter((p) => p.type !== "flag");
|
|
9885
10150
|
if (positional.length === 0) return;
|
|
9886
10151
|
const lastText = positional.findIndex((p, i) => takesRest(p) && !positional.slice(i + 1).some(takesRest));
|
|
9887
10152
|
const restIndex = lastText < 0 ? positional.length - 1 : lastText;
|
|
9888
|
-
|
|
10153
|
+
/** Read `param` from the text from `start` to `end`. */
|
|
10154
|
+
const read = (param, [start, end]) => start === end ? unset(param) : readValue(param, text.slice(start, end), {
|
|
10155
|
+
at: at + start,
|
|
10156
|
+
spans
|
|
10157
|
+
});
|
|
9889
10158
|
if (positional.length === 1) {
|
|
9890
|
-
args[positional[0].name] = read(positional[0], text);
|
|
10159
|
+
args[positional[0].name] = read(positional[0], [0, text.length]);
|
|
9891
10160
|
return;
|
|
9892
10161
|
}
|
|
9893
|
-
const
|
|
10162
|
+
const parts = terms(text);
|
|
9894
10163
|
let from = 0;
|
|
9895
|
-
let to =
|
|
9896
|
-
for (const param of positional.slice(0, restIndex)) args[param.name] = from < to ? read(param,
|
|
9897
|
-
const readTrailing = (param,
|
|
10164
|
+
let to = parts.length;
|
|
10165
|
+
for (const param of positional.slice(0, restIndex)) args[param.name] = from < to ? read(param, parts[from++]) : unset(param);
|
|
10166
|
+
const readTrailing = (param, span) => {
|
|
9898
10167
|
try {
|
|
9899
|
-
return read(param,
|
|
10168
|
+
return read(param, span);
|
|
9900
10169
|
} catch (error) {
|
|
9901
10170
|
if (error instanceof MacroArgumentError) return void 0;
|
|
9902
10171
|
throw error;
|
|
9903
10172
|
}
|
|
9904
10173
|
};
|
|
9905
10174
|
for (const param of positional.slice(restIndex + 1).reverse()) {
|
|
9906
|
-
const value = from < to && !endsWithOperator(text.slice(0,
|
|
10175
|
+
const value = from < to && !endsWithOperator(text.slice(0, parts[to - 1][0])) ? readTrailing(param, parts[to - 1]) : void 0;
|
|
9907
10176
|
args[param.name] = value ?? unset(param);
|
|
9908
10177
|
if (value !== void 0) to--;
|
|
10178
|
+
else spans?.delete(param);
|
|
9909
10179
|
}
|
|
9910
10180
|
const rest = positional[restIndex];
|
|
9911
|
-
args[rest.name] = from < to ? read(rest,
|
|
10181
|
+
args[rest.name] = from < to ? read(rest, [parts[from][0], parts[to - 1][1]]) : unset(rest);
|
|
9912
10182
|
}
|
|
9913
|
-
/**
|
|
9914
|
-
|
|
10183
|
+
/**
|
|
10184
|
+
* Read `rawArgs` into the declared `parameters` (see above). With `spans`,
|
|
10185
|
+
* note there where each argument set was read.
|
|
10186
|
+
*/
|
|
10187
|
+
function parseMacroArgs(rawArgs, parameters, spans) {
|
|
9915
10188
|
const args = {};
|
|
9916
|
-
let rest = rawArgs
|
|
10189
|
+
let [rest, restAt] = trimmed(rawArgs, 0);
|
|
9917
10190
|
let groupStart = 0;
|
|
9918
10191
|
parameters.forEach((separator, i) => {
|
|
9919
10192
|
if (separator.type !== "separator") return;
|
|
9920
10193
|
const at = rest === null ? null : findSeparator(rest, separator.name);
|
|
9921
10194
|
args[separator.name] = at !== null;
|
|
9922
10195
|
const before = at ? rest.slice(0, at[0]) : rest;
|
|
9923
|
-
readGroup(parameters.slice(groupStart, i), before, args
|
|
10196
|
+
readGroup(parameters.slice(groupStart, i), before, args, {
|
|
10197
|
+
at: restAt,
|
|
10198
|
+
spans
|
|
10199
|
+
});
|
|
9924
10200
|
rest = at ? rest.slice(at[1]) : null;
|
|
10201
|
+
if (at) restAt += at[1];
|
|
9925
10202
|
groupStart = i + 1;
|
|
9926
10203
|
});
|
|
9927
|
-
readGroup(parameters.slice(groupStart), rest, args
|
|
10204
|
+
readGroup(parameters.slice(groupStart), rest, args, {
|
|
10205
|
+
at: restAt,
|
|
10206
|
+
spans
|
|
10207
|
+
});
|
|
9928
10208
|
return args;
|
|
9929
10209
|
}
|
|
10210
|
+
/**
|
|
10211
|
+
* Read a `passage` argument as written (`{goto "Hall"}`, `{goto $room}`): a
|
|
10212
|
+
* string literal is the name JavaScript reads from it (`"\u0048all"` is
|
|
10213
|
+
* `Hall`), anything else (a literal that is not well-formed too) an
|
|
10214
|
+
* expression, whose value is the name when it
|
|
10215
|
+
* runs (see evaluatePassageName).
|
|
10216
|
+
*/
|
|
10217
|
+
function passageTarget(arg) {
|
|
10218
|
+
const expression = arg.trim();
|
|
10219
|
+
const name = stringLiteralValue(expression);
|
|
10220
|
+
return name === null ? {
|
|
10221
|
+
kind: "expression",
|
|
10222
|
+
expression
|
|
10223
|
+
} : {
|
|
10224
|
+
kind: "name",
|
|
10225
|
+
name
|
|
10226
|
+
};
|
|
10227
|
+
}
|
|
10228
|
+
/**
|
|
10229
|
+
* The passage a `passage` argument names (`{goto "Hall"}`, `{goto $room}`):
|
|
10230
|
+
* the value of its expression. Throws what evaluating it throws, and if no
|
|
10231
|
+
* passage of the story `state` holds has that name, an error naming it and
|
|
10232
|
+
* the current passage.
|
|
10233
|
+
*/
|
|
10234
|
+
function evaluatePassageName(expr, evaluate, { storyData, currentPassage }) {
|
|
10235
|
+
const name = String(evaluate(expr ?? ""));
|
|
10236
|
+
if (storyData && !storyData.passages.has(name)) throw noPassageError(name, currentPassage);
|
|
10237
|
+
return name;
|
|
10238
|
+
}
|
|
9930
10239
|
//#endregion
|
|
9931
10240
|
//#region src/widgets/widget-def.ts
|
|
9932
10241
|
/**
|
|
@@ -9937,10 +10246,12 @@ function parseMacroArgs(rawArgs, parameters) {
|
|
|
9937
10246
|
var WIDGET_PARAMETERS = [{
|
|
9938
10247
|
name: "name",
|
|
9939
10248
|
type: "text",
|
|
10249
|
+
holds: "text",
|
|
9940
10250
|
required: true
|
|
9941
10251
|
}, {
|
|
9942
10252
|
name: "parameters",
|
|
9943
|
-
type: "text"
|
|
10253
|
+
type: "text",
|
|
10254
|
+
holds: "text"
|
|
9944
10255
|
}];
|
|
9945
10256
|
/**
|
|
9946
10257
|
* The widget a {widget} definition's arguments declare: its name and its
|
|
@@ -9972,6 +10283,17 @@ function blockWidgetNames(passages) {
|
|
|
9972
10283
|
}
|
|
9973
10284
|
return names;
|
|
9974
10285
|
}
|
|
10286
|
+
/** The parameters of the built-in sub-macros that take arguments. */
|
|
10287
|
+
var SUB_MACRO_PARAMETERS = new Map([["option", [{
|
|
10288
|
+
name: "value",
|
|
10289
|
+
type: "string",
|
|
10290
|
+
holds: "text",
|
|
10291
|
+
required: true
|
|
10292
|
+
}]]]);
|
|
10293
|
+
/** The parameters of the built-in sub-macro `name`, if it takes any. */
|
|
10294
|
+
function subMacroParameters(name) {
|
|
10295
|
+
return SUB_MACRO_PARAMETERS.get(name.toLowerCase());
|
|
10296
|
+
}
|
|
9975
10297
|
//#endregion
|
|
9976
10298
|
//#region src/code-check.ts
|
|
9977
10299
|
/** While a pass runs: the code it parsed so far, by goal and source. */
|
|
@@ -10009,21 +10331,59 @@ var CONDITION_MACROS = new Set([
|
|
|
10009
10331
|
"elseif",
|
|
10010
10332
|
"case"
|
|
10011
10333
|
]);
|
|
10012
|
-
/** Code inside the quoted strings of a macro's arguments, by parameter. */
|
|
10013
|
-
var CODE_IN_STRINGS = { watch: {
|
|
10014
|
-
condition: "expression",
|
|
10015
|
-
run: "statements"
|
|
10016
|
-
} };
|
|
10017
|
-
/** The `string` parameters of a macro that name a passage. */
|
|
10018
|
-
var PASSAGE_STRINGS = { watch: ["goto", "dialog"] };
|
|
10019
10334
|
/** Block macros whose body is the name of a passage. */
|
|
10020
10335
|
var PASSAGE_BODIES = new Set(["dialog"]);
|
|
10336
|
+
/**
|
|
10337
|
+
* `params` with each `string` and `text` parameter (options too) saying
|
|
10338
|
+
* what it holds: what it declares, else `holds`.
|
|
10339
|
+
*/
|
|
10340
|
+
function withHolds(params, holds) {
|
|
10341
|
+
return params.map((param) => {
|
|
10342
|
+
if (param.parameters) return {
|
|
10343
|
+
...param,
|
|
10344
|
+
parameters: withHolds(param.parameters, holds)
|
|
10345
|
+
};
|
|
10346
|
+
return (param.type === "string" || param.type === "text") && !param.holds ? {
|
|
10347
|
+
...param,
|
|
10348
|
+
holds
|
|
10349
|
+
} : param;
|
|
10350
|
+
});
|
|
10351
|
+
}
|
|
10352
|
+
/**
|
|
10353
|
+
* The declared parameters of `macros`, by name in any case, and of the
|
|
10354
|
+
* built-in sub-macros (`{option}`): what the story-start check, reference
|
|
10355
|
+
* collection and tooling read arguments by. Each `string` and `text`
|
|
10356
|
+
* parameter says what it holds: what it declares, else `markup` for a macro
|
|
10357
|
+
* with `interpolate` (which can resolve markup) and `text` for any other.
|
|
10358
|
+
*/
|
|
10359
|
+
function parameterLookup(macros) {
|
|
10360
|
+
const parameters = new NameMap();
|
|
10361
|
+
for (const { name, parameters: params, interpolate } of macros) if (params) parameters.set(name, withHolds(params, holdsByDefault(interpolate)));
|
|
10362
|
+
return (name) => {
|
|
10363
|
+
let params = parameters.get(name);
|
|
10364
|
+
if (!params) {
|
|
10365
|
+
const sub = subMacroParameters(name);
|
|
10366
|
+
if (sub) parameters.set(name, params = withHolds(sub, "text"));
|
|
10367
|
+
}
|
|
10368
|
+
return params;
|
|
10369
|
+
};
|
|
10370
|
+
}
|
|
10371
|
+
/** What a `string` or `text` argument holds when its parameter doesn't say. */
|
|
10372
|
+
var holdsByDefault = (interpolate) => interpolate ? "markup" : "text";
|
|
10021
10373
|
/** The goal of the code an argument of this type holds, if it is code. */
|
|
10022
10374
|
function codeGoal(param) {
|
|
10023
10375
|
if (param.type === "expression" || param.type === "passage") return "expression";
|
|
10024
10376
|
if (param.type === "statements") return "statements";
|
|
10025
10377
|
}
|
|
10026
10378
|
/**
|
|
10379
|
+
* What a `string` or `text` argument holds: what its parameter says (see
|
|
10380
|
+
* parameterLookup), else markup, which is checked.
|
|
10381
|
+
*/
|
|
10382
|
+
function stringHolds(param) {
|
|
10383
|
+
if (param.type !== "string" && param.type !== "text") return void 0;
|
|
10384
|
+
return param.holds ?? "markup";
|
|
10385
|
+
}
|
|
10386
|
+
/**
|
|
10027
10387
|
* The code and the markup-holding text in `tokens`, the tokens of `src`.
|
|
10028
10388
|
* `parametersOf` gives the declared parameters of a macro, if it has any.
|
|
10029
10389
|
*/
|
|
@@ -10035,7 +10395,9 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10035
10395
|
if (token.type === "link") yield {
|
|
10036
10396
|
kind: "passage",
|
|
10037
10397
|
name: token.target,
|
|
10038
|
-
offset:
|
|
10398
|
+
offset: token.targetStart,
|
|
10399
|
+
length: token.targetEnd - token.targetStart,
|
|
10400
|
+
macro: "link",
|
|
10039
10401
|
label: src.slice(token.start, token.end)
|
|
10040
10402
|
};
|
|
10041
10403
|
else if (token.type === "expression") yield {
|
|
@@ -10045,32 +10407,35 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10045
10407
|
goal: "expression",
|
|
10046
10408
|
label: `{${token.expression}}`
|
|
10047
10409
|
};
|
|
10048
|
-
else if (token.type === "html")
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10410
|
+
else if (token.type === "html") {
|
|
10411
|
+
let cursor = token.start;
|
|
10412
|
+
for (const [name, value] of Object.entries(token.attributes)) {
|
|
10413
|
+
const at = locate(src, value, cursor);
|
|
10414
|
+
cursor = at + value.length;
|
|
10415
|
+
if (!isCodeAttribute(name)) {
|
|
10416
|
+
yield {
|
|
10417
|
+
kind: "text",
|
|
10418
|
+
text: value,
|
|
10419
|
+
offset: at,
|
|
10420
|
+
where: `In the ${name} attribute of <${token.tag}>: `
|
|
10421
|
+
};
|
|
10422
|
+
continue;
|
|
10423
|
+
}
|
|
10424
|
+
let from = 0;
|
|
10425
|
+
for (const part of splitSigilTemplate(value)) {
|
|
10426
|
+
if (!("expr" in part)) continue;
|
|
10427
|
+
const k = locate(value, part.expr, from);
|
|
10428
|
+
from = k + part.expr.length;
|
|
10429
|
+
yield {
|
|
10430
|
+
kind: "code",
|
|
10431
|
+
code: part.expr,
|
|
10432
|
+
offset: at + k,
|
|
10433
|
+
goal: "expression",
|
|
10434
|
+
label: `{${part.expr}} in the ${name} attribute of <${token.tag}>`
|
|
10435
|
+
};
|
|
10436
|
+
}
|
|
10071
10437
|
}
|
|
10072
|
-
}
|
|
10073
|
-
else if (token.type === "macro" && !token.isClose) {
|
|
10438
|
+
} else if (token.type === "macro" && !token.isClose) {
|
|
10074
10439
|
const name = token.name.toLowerCase();
|
|
10075
10440
|
if (name === "do") {
|
|
10076
10441
|
const close = closeOf(t, name);
|
|
@@ -10090,18 +10455,22 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10090
10455
|
const close = closeOf(t, name);
|
|
10091
10456
|
const body = tokens.slice(t + 1, close);
|
|
10092
10457
|
if (close > t + 1 && body.every((b) => b.type === "text")) {
|
|
10093
|
-
const
|
|
10458
|
+
const text = src.slice(token.end, tokens[close].start);
|
|
10459
|
+
const written = text.trim();
|
|
10094
10460
|
yield {
|
|
10095
10461
|
kind: "passage",
|
|
10096
|
-
name:
|
|
10097
|
-
offset:
|
|
10462
|
+
name: written.replace(/^["']|["']$/g, ""),
|
|
10463
|
+
offset: token.end + text.indexOf(written),
|
|
10464
|
+
length: written.length,
|
|
10465
|
+
macro: name,
|
|
10098
10466
|
label: src.slice(token.start, token.end)
|
|
10099
10467
|
};
|
|
10100
10468
|
}
|
|
10101
10469
|
}
|
|
10102
10470
|
if (!args) continue;
|
|
10103
10471
|
const label = `{${token.name} ${args}}`;
|
|
10104
|
-
const
|
|
10472
|
+
const tag = src.slice(token.start, token.end - 1);
|
|
10473
|
+
const argsAt = token.start + tag.trimEnd().length - args.length;
|
|
10105
10474
|
if (CONDITION_MACROS.has(name)) {
|
|
10106
10475
|
yield {
|
|
10107
10476
|
kind: "code",
|
|
@@ -10120,8 +10489,9 @@ function* codeAndText(src, tokens, parametersOf) {
|
|
|
10120
10489
|
/** The code and text in the arguments `args` (at `offset`) of `macro`. */
|
|
10121
10490
|
function* argPieces(args, offset, params, macro, label) {
|
|
10122
10491
|
let values;
|
|
10492
|
+
const spans = /* @__PURE__ */ new Map();
|
|
10123
10493
|
try {
|
|
10124
|
-
values = parseMacroArgs(args, params);
|
|
10494
|
+
values = parseMacroArgs(args, params, spans);
|
|
10125
10495
|
} catch (error) {
|
|
10126
10496
|
if (!(error instanceof MacroArgumentError)) throw error;
|
|
10127
10497
|
yield {
|
|
@@ -10132,9 +10502,7 @@ function* argPieces(args, offset, params, macro, label) {
|
|
|
10132
10502
|
};
|
|
10133
10503
|
return;
|
|
10134
10504
|
}
|
|
10135
|
-
const
|
|
10136
|
-
const passageStrings = PASSAGE_STRINGS[macro.toLowerCase()] ?? [];
|
|
10137
|
-
let cursor = 0;
|
|
10505
|
+
const name = macro.toLowerCase();
|
|
10138
10506
|
/** The pieces of the parameters `list`, with their values in `from`. */
|
|
10139
10507
|
function* visit(list, from) {
|
|
10140
10508
|
for (const param of list) {
|
|
@@ -10143,32 +10511,57 @@ function* argPieces(args, offset, params, macro, label) {
|
|
|
10143
10511
|
yield* visit(param.parameters, value ?? {});
|
|
10144
10512
|
continue;
|
|
10145
10513
|
}
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
const
|
|
10514
|
+
const span = spans.get(param);
|
|
10515
|
+
if (typeof value !== "string" || !value.trim() || !span) continue;
|
|
10516
|
+
const [start, end] = span;
|
|
10517
|
+
const written = args.slice(start, end);
|
|
10518
|
+
/** The name as written (quotes included), at the argument. */
|
|
10519
|
+
const passage = (passageName) => ({
|
|
10520
|
+
kind: "passage",
|
|
10521
|
+
name: passageName,
|
|
10522
|
+
offset: offset + start,
|
|
10523
|
+
length: end - start,
|
|
10524
|
+
macro: name,
|
|
10525
|
+
label
|
|
10526
|
+
});
|
|
10527
|
+
const goal = codeGoal(param);
|
|
10528
|
+
if (param.type === "passage") {
|
|
10529
|
+
const target = passageTarget(value);
|
|
10530
|
+
if (target.kind === "name") {
|
|
10531
|
+
yield passage(target.name);
|
|
10532
|
+
continue;
|
|
10533
|
+
}
|
|
10534
|
+
}
|
|
10150
10535
|
if (goal) {
|
|
10151
10536
|
const piece = {
|
|
10152
10537
|
kind: "code",
|
|
10153
10538
|
code: value,
|
|
10154
|
-
offset: offset +
|
|
10539
|
+
offset: offset + start,
|
|
10155
10540
|
goal,
|
|
10156
10541
|
label
|
|
10157
10542
|
};
|
|
10158
|
-
if (param.type === "passage")
|
|
10543
|
+
if (param.type === "passage") {
|
|
10544
|
+
piece.passage = true;
|
|
10545
|
+
piece.macro = name;
|
|
10546
|
+
}
|
|
10159
10547
|
yield piece;
|
|
10548
|
+
continue;
|
|
10160
10549
|
}
|
|
10161
|
-
const
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10550
|
+
const at = offset + start + (value !== written && /^["']/.test(written) ? 1 : 0);
|
|
10551
|
+
const holds = stringHolds(param);
|
|
10552
|
+
if (holds === "expression" || holds === "statements") yield {
|
|
10553
|
+
kind: "code",
|
|
10554
|
+
code: value,
|
|
10555
|
+
offset: at,
|
|
10556
|
+
goal: holds,
|
|
10557
|
+
label,
|
|
10558
|
+
inString: true
|
|
10167
10559
|
};
|
|
10168
|
-
if (
|
|
10560
|
+
else if (holds === "passage") yield passage(value);
|
|
10561
|
+
else if (holds === "markup") yield {
|
|
10169
10562
|
kind: "text",
|
|
10170
10563
|
text: value,
|
|
10171
|
-
offset:
|
|
10564
|
+
offset: at,
|
|
10172
10565
|
where: `In the ${param.name} of {${macro}}: `
|
|
10173
10566
|
};
|
|
10174
10567
|
}
|
|
@@ -10185,12 +10578,37 @@ function locate(text, part, from) {
|
|
|
10185
10578
|
const anywhere = text.indexOf(part);
|
|
10186
10579
|
return anywhere >= 0 ? anywhere : from;
|
|
10187
10580
|
}
|
|
10188
|
-
/**
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
}
|
|
10581
|
+
/**
|
|
10582
|
+
* The passages the markup `src` (with the `tokens` it tokenizes to) names:
|
|
10583
|
+
* `[[…]]` links, the passage argument of `{goto}`, `{include}` and `{link}`
|
|
10584
|
+
* (and of macros that declare one), the `goto` and `dialog` actions of
|
|
10585
|
+
* `{watch}` (and the `string` and `text` arguments of macros that declare
|
|
10586
|
+
* they hold one) and the body of `{dialog}`, in source order. A name written
|
|
10587
|
+
* out is a literal; the others are expressions, which name a passage when
|
|
10588
|
+
* they run.
|
|
10589
|
+
*/
|
|
10590
|
+
function collectPassageReferences(src, tokens, parametersOf) {
|
|
10591
|
+
const refs = [];
|
|
10592
|
+
for (const piece of codeAndText(src, tokens, parametersOf)) if (piece.kind === "passage") refs.push({
|
|
10593
|
+
macro: piece.macro,
|
|
10594
|
+
target: {
|
|
10595
|
+
kind: "name",
|
|
10596
|
+
name: piece.name
|
|
10597
|
+
},
|
|
10598
|
+
start: piece.offset,
|
|
10599
|
+
end: piece.offset + piece.length
|
|
10600
|
+
});
|
|
10601
|
+
else if (piece.kind === "code" && piece.passage) refs.push({
|
|
10602
|
+
macro: piece.macro,
|
|
10603
|
+
target: {
|
|
10604
|
+
kind: "expression",
|
|
10605
|
+
expression: piece.code
|
|
10606
|
+
},
|
|
10607
|
+
start: piece.offset,
|
|
10608
|
+
end: piece.offset + piece.code.length
|
|
10609
|
+
});
|
|
10610
|
+
return refs;
|
|
10611
|
+
}
|
|
10194
10612
|
//#endregion
|
|
10195
10613
|
//#region src/markup/validate.ts
|
|
10196
10614
|
/**
|
|
@@ -10200,8 +10618,15 @@ var SUB_MACRO_PARAMETERS = { option: [{
|
|
|
10200
10618
|
* line and column they are at, instead of failing when the passage renders.
|
|
10201
10619
|
* Tooling runs it too (see src/tooling.ts).
|
|
10202
10620
|
*/
|
|
10203
|
-
/**
|
|
10204
|
-
|
|
10621
|
+
/**
|
|
10622
|
+
* Passages that hold no markup: declarations, and the JavaScript function
|
|
10623
|
+
* body of SaveTitle.
|
|
10624
|
+
*/
|
|
10625
|
+
var NOT_MARKUP = new Set([
|
|
10626
|
+
"StoryVariables",
|
|
10627
|
+
"StoryTransients",
|
|
10628
|
+
"SaveTitle"
|
|
10629
|
+
]);
|
|
10205
10630
|
var NOT_MARKUP_TAGS = ["script", "stylesheet"];
|
|
10206
10631
|
/** Branch macros, which their parent macro renders. */
|
|
10207
10632
|
var BRANCH_MACROS = new Set([
|
|
@@ -10312,7 +10737,7 @@ function validateMarkup(passages, options) {
|
|
|
10312
10737
|
report(passage, base + token.start, `${where}Unknown macro {${token.name}}.${didYouMean(token.name, names, (n) => `{${n}}`)}`);
|
|
10313
10738
|
}
|
|
10314
10739
|
};
|
|
10315
|
-
const parametersOf =
|
|
10740
|
+
const parametersOf = options.parametersOf ?? (() => void 0);
|
|
10316
10741
|
/**
|
|
10317
10742
|
* Check `tokens`, the tokens of `src`, which starts at `base` in the
|
|
10318
10743
|
* passage: their macros, the code in them and the markup in their text.
|
|
@@ -10358,6 +10783,83 @@ function validateMarkup(passages, options) {
|
|
|
10358
10783
|
for (const [passage, tokens] of parsed) withParseCache(() => checkTokens(passage, passage.content, tokens, 0, ""));
|
|
10359
10784
|
return diagnostics;
|
|
10360
10785
|
}
|
|
10786
|
+
new NameMap();
|
|
10787
|
+
new NameMap();
|
|
10788
|
+
new NameSet();
|
|
10789
|
+
/**
|
|
10790
|
+
* How a macro argument is read (see components/macros/macro-args.ts). Every
|
|
10791
|
+
* declared parameter names one: there is no default.
|
|
10792
|
+
* Quoted strings accept `\"`, `\'` and `\\` escapes.
|
|
10793
|
+
* - `expression`: code, as written.
|
|
10794
|
+
* - `statements`: code run as statements (`{set}`), as written.
|
|
10795
|
+
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
10796
|
+
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
10797
|
+
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
10798
|
+
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
10799
|
+
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
10800
|
+
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
10801
|
+
* - `number`: a number.
|
|
10802
|
+
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
10803
|
+
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
10804
|
+
* from those after it; a boolean.
|
|
10805
|
+
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
10806
|
+
* quoted string or a number unless it is a flag.
|
|
10807
|
+
*/
|
|
10808
|
+
var PARAMETER_TYPES = [
|
|
10809
|
+
"expression",
|
|
10810
|
+
"statements",
|
|
10811
|
+
"passage",
|
|
10812
|
+
"variable",
|
|
10813
|
+
"string",
|
|
10814
|
+
"text",
|
|
10815
|
+
"names",
|
|
10816
|
+
"delay",
|
|
10817
|
+
"number",
|
|
10818
|
+
"flag",
|
|
10819
|
+
"separator",
|
|
10820
|
+
"options"
|
|
10821
|
+
];
|
|
10822
|
+
/**
|
|
10823
|
+
* What the value of a `string` or `text` argument holds, for the check at
|
|
10824
|
+
* story start and for tooling (see code-check.ts):
|
|
10825
|
+
* - `markup`: markup the macro renders (`{button}`'s label): its markup
|
|
10826
|
+
* is checked.
|
|
10827
|
+
* - `text`: plain text the macro uses as written (`{checkbox}`'s label).
|
|
10828
|
+
* - `passage`: a passage name (`{watch}`'s `goto`): the passage must exist.
|
|
10829
|
+
* - `expression`, `statements`: code (`{watch}`'s condition and `run`): it
|
|
10830
|
+
* is checked as code, and its variable references against the schema.
|
|
10831
|
+
*
|
|
10832
|
+
* Without it, the argument of a macro with `interpolate` holds `markup`,
|
|
10833
|
+
* any other `text`.
|
|
10834
|
+
*/
|
|
10835
|
+
var STRING_HOLDS = [
|
|
10836
|
+
"markup",
|
|
10837
|
+
"text",
|
|
10838
|
+
"passage",
|
|
10839
|
+
"expression",
|
|
10840
|
+
"statements"
|
|
10841
|
+
];
|
|
10842
|
+
/**
|
|
10843
|
+
* Throw if a parameter `macro` declares (or an option of one) has no type,
|
|
10844
|
+
* or one that isn't a ParameterType: arguments are read by their type, and
|
|
10845
|
+
* there is no default. Throw too if it declares what it `holds` wrongly.
|
|
10846
|
+
*/
|
|
10847
|
+
function checkParameterTypes(macro, parameters) {
|
|
10848
|
+
const holdsFix = `Give a \`string\` or \`text\` parameter one of ${STRING_HOLDS.join(", ")} to hold (see docs/custom-macros.md#what-a-string-holds).`;
|
|
10849
|
+
for (const param of parameters) {
|
|
10850
|
+
const type = param.type;
|
|
10851
|
+
if (!PARAMETER_TYPES.includes(type)) throw parameterError(macro, param, type === void 0 ? "has no type" : `has the unknown type ${JSON.stringify(type)}`, `Give it one of the types ${PARAMETER_TYPES.join(", ")} (see docs/custom-macros.md#parameter-types), or declare no parameters and read props.rawArgs.`);
|
|
10852
|
+
const holds = param.holds;
|
|
10853
|
+
if (holds !== void 0 && type !== "string" && type !== "text") throw parameterError(macro, param, `is of the type ${type}, which holds what it is`, holdsFix);
|
|
10854
|
+
if (holds !== void 0 && !STRING_HOLDS.includes(holds)) throw parameterError(macro, param, `holds the unknown ${JSON.stringify(holds)}`, holdsFix);
|
|
10855
|
+
if (param.parameters) checkParameterTypes(macro, param.parameters);
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
/** The error for the parameter `param` of `macro`: what is wrong, and the fix. */
|
|
10859
|
+
function parameterError(macro, param, problem, fix) {
|
|
10860
|
+
return /* @__PURE__ */ new Error(`spindle: The parameter "${param.name}" of the macro {${macro}} ${problem}. ${fix}`);
|
|
10861
|
+
}
|
|
10862
|
+
new NameMap();
|
|
10361
10863
|
//#endregion
|
|
10362
10864
|
//#region src/utils/namespace.ts
|
|
10363
10865
|
/** The variable name no namespace can hold. */
|
|
@@ -10448,58 +10950,84 @@ function parseStoryVariables(content, sigil = "$") {
|
|
|
10448
10950
|
}
|
|
10449
10951
|
return schema;
|
|
10450
10952
|
}
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
new NameSet();
|
|
10953
|
+
//#endregion
|
|
10954
|
+
//#region src/components/macros/include-args.ts
|
|
10454
10955
|
/**
|
|
10455
|
-
*
|
|
10456
|
-
*
|
|
10457
|
-
*
|
|
10458
|
-
* - `expression`: code, as written.
|
|
10459
|
-
* - `statements`: code run as statements (`{set}`), as written.
|
|
10460
|
-
* - `passage`: a passage name: a quoted string or an expression, as written.
|
|
10461
|
-
* - `variable`: a variable reference such as `$name` or `"$name"`, as written.
|
|
10462
|
-
* - `string`: one quoted string; anything else leaves the argument unset.
|
|
10463
|
-
* - `text`: one quoted string, or text with any loose quotes stripped.
|
|
10464
|
-
* - `names`: a comma-separated list of names (`@item, @i`).
|
|
10465
|
-
* - `delay`: a duration (`2s`, `500ms`, `300`) in milliseconds.
|
|
10466
|
-
* - `number`: a number.
|
|
10467
|
-
* - `flag`: a keyword, the parameter's name, at the start or end; a boolean.
|
|
10468
|
-
* - `separator`: a word (`of`) or `=` separating the parameters before it
|
|
10469
|
-
* from those after it; a boolean.
|
|
10470
|
-
* - `options`: keywords, the names of its `parameters`, each followed by a
|
|
10471
|
-
* quoted string or a number unless it is a flag.
|
|
10956
|
+
* The parameters of `{include}`. A standalone `inline` flag only counts
|
|
10957
|
+
* outside quotes and brackets, so passage names and expressions containing
|
|
10958
|
+
* the word stay intact (#201).
|
|
10472
10959
|
*/
|
|
10473
|
-
var
|
|
10474
|
-
"
|
|
10475
|
-
"
|
|
10476
|
-
|
|
10477
|
-
"
|
|
10478
|
-
"
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
"delay",
|
|
10482
|
-
"number",
|
|
10483
|
-
"flag",
|
|
10484
|
-
"separator",
|
|
10485
|
-
"options"
|
|
10486
|
-
];
|
|
10960
|
+
var INCLUDE_PARAMETERS = [{
|
|
10961
|
+
name: "inline",
|
|
10962
|
+
type: "flag"
|
|
10963
|
+
}, {
|
|
10964
|
+
name: "passage",
|
|
10965
|
+
type: "passage",
|
|
10966
|
+
required: true
|
|
10967
|
+
}];
|
|
10487
10968
|
/**
|
|
10488
|
-
*
|
|
10489
|
-
* or
|
|
10490
|
-
* there is no default.
|
|
10969
|
+
* The arguments of `{include}`: whether it has the `inline` flag, as its
|
|
10970
|
+
* first or last word, and the passage expression that is left.
|
|
10491
10971
|
*/
|
|
10492
|
-
function
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
}
|
|
10499
|
-
if (param.parameters) checkParameterTypes(macro, param.parameters);
|
|
10500
|
-
}
|
|
10972
|
+
function splitIncludeFlag(rawArgs) {
|
|
10973
|
+
const { inline, passage } = parseMacroArgs(rawArgs, INCLUDE_PARAMETERS);
|
|
10974
|
+
return {
|
|
10975
|
+
inline,
|
|
10976
|
+
passage
|
|
10977
|
+
};
|
|
10501
10978
|
}
|
|
10502
|
-
|
|
10979
|
+
//#endregion
|
|
10980
|
+
//#region src/transform.ts
|
|
10981
|
+
/**
|
|
10982
|
+
* The sigil transform: the story's code with its variable references
|
|
10983
|
+
* (`$var`, `_var`, `@var`, `%var`) turned into the namespace lookups the
|
|
10984
|
+
* expression engine runs. Pure string-to-string, so tooling can use it
|
|
10985
|
+
* (see src/tooling.ts).
|
|
10986
|
+
*/
|
|
10987
|
+
var TRANSFORM_CACHE_MAX = 500;
|
|
10988
|
+
var NAMESPACES = {
|
|
10989
|
+
$: "variables",
|
|
10990
|
+
_: "temporary",
|
|
10991
|
+
"@": "locals",
|
|
10992
|
+
"%": "transient"
|
|
10993
|
+
};
|
|
10994
|
+
/** Ends with an identifier character. */
|
|
10995
|
+
var IDENT_END_RE = /[\p{ID_Continue}$\u200c\u200d]$/u;
|
|
10996
|
+
/**
|
|
10997
|
+
* Transform expression: $var → variables["var"], _var → temporary["var"],
|
|
10998
|
+
* @var → locals["var"], %var → transient["var"].
|
|
10999
|
+
* The lexer (`lexJs`) finds the references: only in code (string, template
|
|
11000
|
+
* and regex literal text and comments are left untouched), only where an
|
|
11001
|
+
* identifier starts (not in `a$b`, nor as the property name in `obj._x`),
|
|
11002
|
+
* and `%var` only where an operand is expected, since `%` is also the
|
|
11003
|
+
* modulo operator. `goal` tells whether `expr` is an expression or a list of
|
|
11004
|
+
* statements, as that decides whether a leading `{` opens an object literal
|
|
11005
|
+
* or a block. A reference to a variable named `__proto__` throws a
|
|
11006
|
+
* SyntaxError: no namespace can hold one (see utils/namespace.ts).
|
|
11007
|
+
* */
|
|
11008
|
+
function transform(expr, goal = "expression") {
|
|
11009
|
+
const key = goal === "statements" ? "s" + expr : "e" + expr;
|
|
11010
|
+
const cached = transformCache.get(key);
|
|
11011
|
+
if (cached !== void 0) return cached;
|
|
11012
|
+
const { refs } = parseCode(expr, goal);
|
|
11013
|
+
let result = "";
|
|
11014
|
+
let at = 0;
|
|
11015
|
+
for (const ref of refs) {
|
|
11016
|
+
const error = variableNameError(ref.name, ref.sigil + ref.name);
|
|
11017
|
+
if (error) throw new SyntaxError(`spindle: ${error}`);
|
|
11018
|
+
result += expr.slice(at, ref.start);
|
|
11019
|
+
if (ref.shorthand) result += `${ref.name}: `;
|
|
11020
|
+
else if (IDENT_END_RE.test(result.slice(-2))) result += " ";
|
|
11021
|
+
result += `${NAMESPACES[ref.sigil]}["${ref.name}"]`;
|
|
11022
|
+
at = ref.end;
|
|
11023
|
+
}
|
|
11024
|
+
result += expr.slice(at);
|
|
11025
|
+
if (transformCache.size >= TRANSFORM_CACHE_MAX) transformCache.delete(transformCache.keys().next().value);
|
|
11026
|
+
transformCache.set(key, result);
|
|
11027
|
+
return result;
|
|
11028
|
+
}
|
|
11029
|
+
/** Transformed code by goal and source. */
|
|
11030
|
+
var transformCache = /* @__PURE__ */ new Map();
|
|
10503
11031
|
//#endregion
|
|
10504
11032
|
//#region src/tooling.ts
|
|
10505
11033
|
/**
|
|
@@ -10516,23 +11044,31 @@ new NameMap();
|
|
|
10516
11044
|
*/
|
|
10517
11045
|
function validateStoryMarkup(passages, macros, options = {}) {
|
|
10518
11046
|
const list = [...passages];
|
|
11047
|
+
const all = [...macros];
|
|
10519
11048
|
const known = /* @__PURE__ */ new Set();
|
|
10520
|
-
const parameters = /* @__PURE__ */ new Map();
|
|
10521
11049
|
const blocks = new Set(blockWidgetNames(list).map((n) => n.toLowerCase()));
|
|
10522
|
-
for (const macro of
|
|
11050
|
+
for (const macro of all) {
|
|
10523
11051
|
const name = macro.name.toLowerCase();
|
|
10524
11052
|
known.add(name);
|
|
10525
|
-
parameters.set(name, macro.parameters);
|
|
10526
11053
|
for (const sub of macro.subMacros) known.add(sub.toLowerCase());
|
|
10527
11054
|
if (macro.block) blocks.add(name);
|
|
10528
11055
|
}
|
|
10529
11056
|
return validateMarkup(list, {
|
|
10530
11057
|
isKnownMacro: (name) => known.has(name),
|
|
10531
11058
|
macroNames: known,
|
|
10532
|
-
parametersOf: (
|
|
11059
|
+
parametersOf: parameterLookup(all),
|
|
10533
11060
|
isBlockMacro: (name) => blocks.has(name.toLowerCase()) || isBlockMacro(name),
|
|
10534
11061
|
checkPassageNames: options.checkPassageNames
|
|
10535
11062
|
});
|
|
10536
11063
|
}
|
|
11064
|
+
/**
|
|
11065
|
+
* The passages the markup of `source` names (see PassageReference), against
|
|
11066
|
+
* the given macros: what the story-start check looks up. Malformed tags are
|
|
11067
|
+
* skipped, so it reads half-typed markup.
|
|
11068
|
+
*/
|
|
11069
|
+
function collectStoryPassageReferences(source, macros) {
|
|
11070
|
+
const { tokens } = tokenizeMarkupTolerant(source);
|
|
11071
|
+
return collectPassageReferences(source, tokens, parameterLookup(macros));
|
|
11072
|
+
}
|
|
10537
11073
|
//#endregion
|
|
10538
|
-
export { checkParameterTypes, formatDiagnostic, parseStoryVariables, validateStoryMarkup };
|
|
11074
|
+
export { MarkupError, SIGIL_SCOPES, checkParameterTypes, collectStoryPassageReferences, endsWithOperator, evaluatePassageName, findCodeEnd, formatDiagnostic, isSigil, lexJs, lexTemplate, parseSelectors, parseStoryVariables, passageTarget, readQuoted, scanStringLiteral, splitArgs, splitIncludeFlag, splitTopLevel, stripLooseQuotes, tokenizeMarkup, tokenizeMarkupTolerant, transform, unescapeQuoted, validateStoryMarkup };
|