@neocompose/cli 0.17.0 → 0.19.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/CHANGELOG.md +12 -0
- package/dist/neo.mjs +3334 -267
- package/package.json +1 -1
package/dist/neo.mjs
CHANGED
|
@@ -1502,7 +1502,7 @@ function vectorTypes() {
|
|
|
1502
1502
|
members: structuredLeafFieldProperties(name)
|
|
1503
1503
|
}));
|
|
1504
1504
|
}
|
|
1505
|
-
var NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_TYPES, NEOSCRIPT_OPERATORS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
|
|
1505
|
+
var NEOSCRIPT_KEYWORDS, NEOSCRIPT_INFERRED_LOCAL_KEYWORD, NEOSCRIPT_CONSTRUCTOR_KEYWORD, NEOSCRIPT_PRIMITIVE_TYPES, NEOSCRIPT_BUILTIN_TYPES, NEOSCRIPT_OPERATORS, NEOSCRIPT_STATEMENT_SNIPPETS, STRING_TYPE, INT_TYPE, FLOAT_TYPE, BOOL_TYPE, IMAGE_REF_TYPE, FLOAT_OPTIONAL_TYPE, NEOSCRIPT_STRUCTURED_LEAF_FIELDS, NEOSCRIPT_IMAGE_REGISTRY_TYPE, NEOSCRIPT_AUDIO_CLIP_REGISTRY_TYPE, NEOSCRIPT_PENDING_SYMBOL_ID_PREFIX, REGISTRY_ENTRY_TYPES, NEOSCRIPT_GLOBALS, NEOSCRIPT_BUILTIN_TYPE_SYMBOLS;
|
|
1506
1506
|
var init_language_spec = __esm({
|
|
1507
1507
|
"../packages/neoscript-language/src/language-spec.ts"() {
|
|
1508
1508
|
"use strict";
|
|
@@ -1513,6 +1513,16 @@ var init_language_spec = __esm({
|
|
|
1513
1513
|
"enum",
|
|
1514
1514
|
"if",
|
|
1515
1515
|
"else",
|
|
1516
|
+
"try",
|
|
1517
|
+
"catch",
|
|
1518
|
+
"for",
|
|
1519
|
+
"foreach",
|
|
1520
|
+
"in",
|
|
1521
|
+
"break",
|
|
1522
|
+
"continue",
|
|
1523
|
+
"switch",
|
|
1524
|
+
"case",
|
|
1525
|
+
"default",
|
|
1516
1526
|
"extends",
|
|
1517
1527
|
"interface",
|
|
1518
1528
|
"native",
|
|
@@ -1595,6 +1605,58 @@ var init_language_spec = __esm({
|
|
|
1595
1605
|
"<",
|
|
1596
1606
|
">"
|
|
1597
1607
|
];
|
|
1608
|
+
NEOSCRIPT_STATEMENT_SNIPPETS = [
|
|
1609
|
+
{
|
|
1610
|
+
label: "if",
|
|
1611
|
+
insertText: "if (${1:condition}) {\n ${0}\n}",
|
|
1612
|
+
detail: "Conditional statement"
|
|
1613
|
+
},
|
|
1614
|
+
{
|
|
1615
|
+
label: "try / catch",
|
|
1616
|
+
insertText: "try {\n ${1}\n}\ncatch (string ${2:message}) {\n ${0}\n}",
|
|
1617
|
+
detail: "Handle a string error"
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
label: "for",
|
|
1621
|
+
insertText: "for (${1:int} ${2:i} = ${3:0}; ${2:i} < ${4:count}; ${2:i}++) {\n ${0}\n}",
|
|
1622
|
+
detail: "Counted loop"
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
label: "foreach",
|
|
1626
|
+
insertText: "foreach (var ${1:item} in ${2:collection}) {\n ${0}\n}",
|
|
1627
|
+
detail: "Collection loop"
|
|
1628
|
+
},
|
|
1629
|
+
{
|
|
1630
|
+
label: "switch",
|
|
1631
|
+
insertText: "switch (${1:value}) {\n case ${2:constant}:\n ${3}\n break;\n default:\n ${0}\n break;\n}",
|
|
1632
|
+
detail: "Scalar or enum switch statement"
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
label: "break",
|
|
1636
|
+
insertText: "break;",
|
|
1637
|
+
detail: "Exit the nearest loop or switch"
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
label: "continue",
|
|
1641
|
+
insertText: "continue;",
|
|
1642
|
+
detail: "Advance the nearest loop"
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
label: "if / else",
|
|
1646
|
+
insertText: "if (${1:condition}) {\n ${2}\n} else {\n ${0}\n}",
|
|
1647
|
+
detail: "Conditional statement with fallback"
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
label: "return",
|
|
1651
|
+
insertText: "return ${0:value};",
|
|
1652
|
+
detail: "Return from the current script"
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
label: "throw",
|
|
1656
|
+
insertText: 'throw "${0:message}";',
|
|
1657
|
+
detail: "Stop execution with an error"
|
|
1658
|
+
}
|
|
1659
|
+
];
|
|
1598
1660
|
STRING_TYPE = { kind: "primitive", name: "string" };
|
|
1599
1661
|
INT_TYPE = { kind: "primitive", name: "int" };
|
|
1600
1662
|
FLOAT_TYPE = { kind: "primitive", name: "float" };
|
|
@@ -2287,7 +2349,29 @@ function complete(snapshot, position) {
|
|
|
2287
2349
|
const tokens = significantTokensBefore(snapshot.lexed.tokens, word.start);
|
|
2288
2350
|
const tail = tokens[tokens.length - 1];
|
|
2289
2351
|
let candidates;
|
|
2290
|
-
|
|
2352
|
+
const catchFilterCandidates = catchFilterCompletionItems(
|
|
2353
|
+
snapshot,
|
|
2354
|
+
word.start,
|
|
2355
|
+
word
|
|
2356
|
+
);
|
|
2357
|
+
const catchClauseCandidates = catchClauseCompletionItems(
|
|
2358
|
+
snapshot,
|
|
2359
|
+
word.start,
|
|
2360
|
+
word
|
|
2361
|
+
);
|
|
2362
|
+
const switchCaseCandidates = switchCaseCompletionItems(
|
|
2363
|
+
snapshot,
|
|
2364
|
+
word.start,
|
|
2365
|
+
prefix,
|
|
2366
|
+
word
|
|
2367
|
+
);
|
|
2368
|
+
if (catchFilterCandidates) {
|
|
2369
|
+
candidates = catchFilterCandidates;
|
|
2370
|
+
} else if (catchClauseCandidates) {
|
|
2371
|
+
candidates = catchClauseCandidates;
|
|
2372
|
+
} else if (switchCaseCandidates) {
|
|
2373
|
+
candidates = switchCaseCandidates;
|
|
2374
|
+
} else if (tail && (tail.kind === "punctuation" && tail.text === "." || tail.kind === "operator" && tail.text === "?.")) {
|
|
2291
2375
|
const receiverTokens = expressionTokensBefore(tokens, tokens.length - 1);
|
|
2292
2376
|
const resolved = resolveChain(snapshot, receiverTokens, offset);
|
|
2293
2377
|
candidates = completionItemsForResolution(snapshot, resolved, word);
|
|
@@ -2503,13 +2587,28 @@ function semanticTokens(snapshot) {
|
|
|
2503
2587
|
}
|
|
2504
2588
|
if (token.kind !== "identifier") continue;
|
|
2505
2589
|
if (snapshot.parsed.locals.some(
|
|
2506
|
-
(local) => local.
|
|
2590
|
+
(local) => local.contextualKeywordRange?.start.line === token.range.start.line && local.contextualKeywordRange.start.character === token.range.start.character
|
|
2507
2591
|
) || snapshot.parsed.calls.some(
|
|
2508
2592
|
(call) => call.kind === "constructor" && call.contextualKeywordRange?.start.line === token.range.start.line && call.contextualKeywordRange.start.character === token.range.start.character
|
|
2509
2593
|
)) {
|
|
2510
2594
|
result.push({ range: token.range, type: "keyword", modifiers: [] });
|
|
2511
2595
|
continue;
|
|
2512
2596
|
}
|
|
2597
|
+
const declaredLocal = snapshot.parsed.locals.find(
|
|
2598
|
+
(local) => local.nameRange.start.line === token.range.start.line && local.nameRange.start.character === token.range.start.character
|
|
2599
|
+
);
|
|
2600
|
+
if (declaredLocal) {
|
|
2601
|
+
const symbol2 = localSymbol(declaredLocal, snapshot);
|
|
2602
|
+
result.push({
|
|
2603
|
+
range: token.range,
|
|
2604
|
+
type: semanticSymbolKind(symbol2),
|
|
2605
|
+
modifiers: [
|
|
2606
|
+
"declaration",
|
|
2607
|
+
...symbol2.writable === false ? ["readonly"] : []
|
|
2608
|
+
]
|
|
2609
|
+
});
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2513
2612
|
const reference2 = snapshot.parsed.references.find(
|
|
2514
2613
|
(candidate) => candidate.start === token.start
|
|
2515
2614
|
);
|
|
@@ -2572,6 +2671,19 @@ function isValidRenameIdentifier(name) {
|
|
|
2572
2671
|
}
|
|
2573
2672
|
function topLevelCompletionItems(snapshot, offset, word) {
|
|
2574
2673
|
const items = [];
|
|
2674
|
+
if (insideCatchBody(snapshot, offset)) {
|
|
2675
|
+
items.push(
|
|
2676
|
+
completion(
|
|
2677
|
+
"throw;",
|
|
2678
|
+
"snippet",
|
|
2679
|
+
"throw;",
|
|
2680
|
+
"Rethrow the current catch message",
|
|
2681
|
+
word,
|
|
2682
|
+
snapshot,
|
|
2683
|
+
"snippet"
|
|
2684
|
+
)
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2575
2687
|
for (const keyword of NEOSCRIPT_KEYWORDS) {
|
|
2576
2688
|
items.push(
|
|
2577
2689
|
completion(
|
|
@@ -2584,6 +2696,19 @@ function topLevelCompletionItems(snapshot, offset, word) {
|
|
|
2584
2696
|
)
|
|
2585
2697
|
);
|
|
2586
2698
|
}
|
|
2699
|
+
for (const snippet of NEOSCRIPT_STATEMENT_SNIPPETS) {
|
|
2700
|
+
items.push(
|
|
2701
|
+
completion(
|
|
2702
|
+
snippet.label,
|
|
2703
|
+
"snippet",
|
|
2704
|
+
snippet.insertText,
|
|
2705
|
+
snippet.detail,
|
|
2706
|
+
word,
|
|
2707
|
+
snapshot,
|
|
2708
|
+
"snippet"
|
|
2709
|
+
)
|
|
2710
|
+
);
|
|
2711
|
+
}
|
|
2587
2712
|
items.push(
|
|
2588
2713
|
completion(
|
|
2589
2714
|
NEOSCRIPT_INFERRED_LOCAL_KEYWORD,
|
|
@@ -2638,6 +2763,135 @@ function topLevelCompletionItems(snapshot, offset, word) {
|
|
|
2638
2763
|
}
|
|
2639
2764
|
return items;
|
|
2640
2765
|
}
|
|
2766
|
+
function catchFilterCompletionItems(snapshot, offset, word) {
|
|
2767
|
+
const prefix = snapshot.source.text.slice(0, offset);
|
|
2768
|
+
if (!/\bcatch\s*\(\s*string\s+[A-Za-z_][A-Za-z0-9_]*\s*\)\s*$/.test(prefix)) {
|
|
2769
|
+
return null;
|
|
2770
|
+
}
|
|
2771
|
+
return [
|
|
2772
|
+
completion(
|
|
2773
|
+
"when",
|
|
2774
|
+
"snippet",
|
|
2775
|
+
"when (${1:condition})",
|
|
2776
|
+
"Filter this catch clause",
|
|
2777
|
+
word,
|
|
2778
|
+
snapshot,
|
|
2779
|
+
"snippet"
|
|
2780
|
+
)
|
|
2781
|
+
];
|
|
2782
|
+
}
|
|
2783
|
+
function catchClauseCompletionItems(snapshot, offset, word) {
|
|
2784
|
+
if (!catchClauseCanFollow(snapshot, offset)) return null;
|
|
2785
|
+
return [
|
|
2786
|
+
completion(
|
|
2787
|
+
"catch",
|
|
2788
|
+
"snippet",
|
|
2789
|
+
"catch (string ${1:message}) {\n ${0}\n}",
|
|
2790
|
+
"Handle another string error",
|
|
2791
|
+
word,
|
|
2792
|
+
snapshot,
|
|
2793
|
+
"snippet"
|
|
2794
|
+
)
|
|
2795
|
+
];
|
|
2796
|
+
}
|
|
2797
|
+
function catchClauseCanFollow(snapshot, offset) {
|
|
2798
|
+
const tokens = significantTokensBefore(snapshot.lexed.tokens, offset);
|
|
2799
|
+
const closeIndex = tokens.length - 1;
|
|
2800
|
+
const close = tokens[closeIndex];
|
|
2801
|
+
if (close?.kind !== "punctuation" || close.text !== "}") return false;
|
|
2802
|
+
const closedCatch = catchEndingAt(snapshot, close.start);
|
|
2803
|
+
if (closedCatch && closedCatch.contextualKeywordRange === void 0) {
|
|
2804
|
+
return false;
|
|
2805
|
+
}
|
|
2806
|
+
return closesTryOrCatchChain(snapshot, tokens, closeIndex);
|
|
2807
|
+
}
|
|
2808
|
+
function closesTryOrCatchChain(snapshot, tokens, closeIndex) {
|
|
2809
|
+
const close = tokens[closeIndex];
|
|
2810
|
+
if (close?.kind !== "punctuation" || close.text !== "}") return false;
|
|
2811
|
+
const closedCatch = catchEndingAt(snapshot, close.start);
|
|
2812
|
+
if (closedCatch) {
|
|
2813
|
+
const catchKeywordStart = closedCatch.catchKeywordStart;
|
|
2814
|
+
if (catchKeywordStart === void 0) return false;
|
|
2815
|
+
const catchIndex = tokens.findIndex(
|
|
2816
|
+
(token) => token.start === catchKeywordStart
|
|
2817
|
+
);
|
|
2818
|
+
if (catchIndex <= 0) return false;
|
|
2819
|
+
const precedingClose = tokens[catchIndex - 1];
|
|
2820
|
+
if (precedingClose?.kind !== "punctuation" || precedingClose.text !== "}") {
|
|
2821
|
+
return false;
|
|
2822
|
+
}
|
|
2823
|
+
return closesTryOrCatchChain(snapshot, tokens, catchIndex - 1);
|
|
2824
|
+
}
|
|
2825
|
+
const openIndex = matchingOpenBraceIndex(tokens, closeIndex);
|
|
2826
|
+
if (openIndex === null) return false;
|
|
2827
|
+
const keyword = tokens[openIndex - 1];
|
|
2828
|
+
return keyword?.kind === "keyword" && keyword.text === "try";
|
|
2829
|
+
}
|
|
2830
|
+
function catchEndingAt(snapshot, closeStart) {
|
|
2831
|
+
return snapshot.parsed.locals.find(
|
|
2832
|
+
(local) => local.catchParameter && local.scopeEnd === closeStart
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
function matchingOpenBraceIndex(tokens, closeIndex) {
|
|
2836
|
+
let depth = 0;
|
|
2837
|
+
for (let index = closeIndex; index >= 0; index--) {
|
|
2838
|
+
const token = tokens[index];
|
|
2839
|
+
if (token?.kind !== "punctuation") continue;
|
|
2840
|
+
if (token.text === "}") depth++;
|
|
2841
|
+
if (token.text !== "{") continue;
|
|
2842
|
+
depth--;
|
|
2843
|
+
if (depth === 0) return index;
|
|
2844
|
+
}
|
|
2845
|
+
return null;
|
|
2846
|
+
}
|
|
2847
|
+
function insideCatchBody(snapshot, offset) {
|
|
2848
|
+
let clause;
|
|
2849
|
+
for (const local of snapshot.parsed.locals) {
|
|
2850
|
+
if (!local.catchParameter || local.catchBodyStart === void 0 || local.catchBodyStart > offset || offset > local.scopeEnd) {
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
if (clause?.catchBodyStart === void 0 || local.catchBodyStart > clause.catchBodyStart) {
|
|
2854
|
+
clause = local;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
if (clause?.catchBodyStart === void 0) return false;
|
|
2858
|
+
return !insideLambdaBlock(
|
|
2859
|
+
snapshot.lexed.tokens,
|
|
2860
|
+
clause.catchBodyStart,
|
|
2861
|
+
offset
|
|
2862
|
+
);
|
|
2863
|
+
}
|
|
2864
|
+
function insideLambdaBlock(tokens, catchBodyStart, offset) {
|
|
2865
|
+
const significant = tokens.filter(
|
|
2866
|
+
(token) => token.kind !== "comment" && token.kind !== "eof"
|
|
2867
|
+
);
|
|
2868
|
+
for (let index = 0; index < significant.length; index++) {
|
|
2869
|
+
const arrow = significant[index];
|
|
2870
|
+
if (arrow?.kind !== "operator" || arrow.text !== "=>" || arrow.start < catchBodyStart || arrow.start >= offset) {
|
|
2871
|
+
continue;
|
|
2872
|
+
}
|
|
2873
|
+
const open = significant[index + 1];
|
|
2874
|
+
if (open?.kind !== "punctuation" || open.text !== "{") continue;
|
|
2875
|
+
const closeIndex = matchingCloseBraceIndex(significant, index + 1);
|
|
2876
|
+
const close = closeIndex === null ? void 0 : significant[closeIndex];
|
|
2877
|
+
if (open.end <= offset && (close === void 0 || offset <= close.start)) {
|
|
2878
|
+
return true;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
return false;
|
|
2882
|
+
}
|
|
2883
|
+
function matchingCloseBraceIndex(tokens, openIndex) {
|
|
2884
|
+
let depth = 0;
|
|
2885
|
+
for (let index = openIndex; index < tokens.length; index++) {
|
|
2886
|
+
const token = tokens[index];
|
|
2887
|
+
if (token?.kind !== "punctuation") continue;
|
|
2888
|
+
if (token.text === "{") depth++;
|
|
2889
|
+
if (token.text !== "}") continue;
|
|
2890
|
+
depth--;
|
|
2891
|
+
if (depth === 0) return index;
|
|
2892
|
+
}
|
|
2893
|
+
return null;
|
|
2894
|
+
}
|
|
2641
2895
|
function typeCompletionItems(snapshot, word) {
|
|
2642
2896
|
const items = NEOSCRIPT_PRIMITIVE_TYPES.map(
|
|
2643
2897
|
(name) => completion(name, "keyword", name, "Primitive type", word, snapshot)
|
|
@@ -2753,9 +3007,62 @@ function resolveReference(snapshot, reference2) {
|
|
|
2753
3007
|
const dotIndex = tokenIndex - 1;
|
|
2754
3008
|
const chainTokens = expressionTokensBefore(tokens, dotIndex);
|
|
2755
3009
|
const receiver = resolveChain(snapshot, chainTokens, reference2.start);
|
|
2756
|
-
if (!receiver)
|
|
3010
|
+
if (!receiver) {
|
|
3011
|
+
return resolveContextualSwitchEnumReference(snapshot, reference2);
|
|
3012
|
+
}
|
|
2757
3013
|
return resolveMember(snapshot, receiver, reference2.name);
|
|
2758
3014
|
}
|
|
3015
|
+
function switchCaseCompletionItems(snapshot, offset, prefix, word) {
|
|
3016
|
+
const context = switchCaseEnumContext(snapshot, offset);
|
|
3017
|
+
if (!context) return null;
|
|
3018
|
+
const preceding = snapshot.source.text[word.start - 1];
|
|
3019
|
+
const authoredDot = context.label.expressionTokens.some(
|
|
3020
|
+
(token) => token.kind === "punctuation" && token.text === "."
|
|
3021
|
+
);
|
|
3022
|
+
if (prefix.length > 0 && preceding !== "." && !authoredDot) return null;
|
|
3023
|
+
const needsContextualDot = !authoredDot && preceding !== ".";
|
|
3024
|
+
return context.type.members.filter((member) => member.kind === "enumMember").map((member) => {
|
|
3025
|
+
const item = symbolCompletion(member, word, snapshot);
|
|
3026
|
+
if (!needsContextualDot) return item;
|
|
3027
|
+
const insertText = `.${member.name}`;
|
|
3028
|
+
return {
|
|
3029
|
+
...item,
|
|
3030
|
+
label: insertText,
|
|
3031
|
+
insertText,
|
|
3032
|
+
textEdit: {
|
|
3033
|
+
range: snapshot.source.range(word.start, word.end),
|
|
3034
|
+
newText: insertText
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
function resolveContextualSwitchEnumReference(snapshot, reference2) {
|
|
3040
|
+
const context = switchCaseEnumContext(snapshot, reference2.start);
|
|
3041
|
+
if (!context) return null;
|
|
3042
|
+
const symbol = context.type.members.find(
|
|
3043
|
+
(member) => member.kind === "enumMember" && member.name === reference2.name
|
|
3044
|
+
);
|
|
3045
|
+
return symbol ? { type: symbol.type, symbol } : null;
|
|
3046
|
+
}
|
|
3047
|
+
function switchCaseEnumContext(snapshot, offset) {
|
|
3048
|
+
for (const statement of snapshot.parsed.switches) {
|
|
3049
|
+
for (const section of statement.sections) {
|
|
3050
|
+
for (const label of section.labels) {
|
|
3051
|
+
if (label.kind !== "case") continue;
|
|
3052
|
+
if (offset < label.start || offset > label.end) continue;
|
|
3053
|
+
const selectorType = inferExpressionType(
|
|
3054
|
+
statement.selectorTokens,
|
|
3055
|
+
snapshot,
|
|
3056
|
+
statement.start
|
|
3057
|
+
);
|
|
3058
|
+
if (selectorType.kind !== "named") return null;
|
|
3059
|
+
const type = snapshot.project.typeById.get(selectorType.typeId);
|
|
3060
|
+
return type?.kind === "enum" ? { type, label } : null;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
2759
3066
|
function resolveChain(snapshot, tokens, offset) {
|
|
2760
3067
|
if (tokens.length === 0) return null;
|
|
2761
3068
|
const constructorType = tokens[0]?.kind === "identifier" && tokens[0].text === NEOSCRIPT_CONSTRUCTOR_KEYWORD && tokens[1]?.kind === "identifier" ? snapshot.project.typeByName.get(tokens[1].text) : void 0;
|
|
@@ -3058,11 +3365,17 @@ function resolveDeclaredType(local, snapshot) {
|
|
|
3058
3365
|
function inferLocalType(local, snapshot) {
|
|
3059
3366
|
const tokens = local.initializerTokens ?? [];
|
|
3060
3367
|
if (tokens.length === 0) return UNKNOWN_TYPE;
|
|
3061
|
-
|
|
3368
|
+
const inferred = inferExpressionType(
|
|
3062
3369
|
tokens,
|
|
3063
3370
|
snapshot,
|
|
3064
3371
|
tokens[0]?.start ?? snapshot.source.offsetAt(local.nameRange.end)
|
|
3065
3372
|
);
|
|
3373
|
+
if (!local.collectionIteration) return inferred;
|
|
3374
|
+
if (inferred.kind === "dictionary") return inferred.valueType;
|
|
3375
|
+
if (inferred.kind === "list" || inferred.kind === "set") {
|
|
3376
|
+
return inferred.elementType;
|
|
3377
|
+
}
|
|
3378
|
+
return UNKNOWN_TYPE;
|
|
3066
3379
|
}
|
|
3067
3380
|
function inferExpressionType(rawTokens, snapshot, offset) {
|
|
3068
3381
|
const tokens = unwrapExpressionTokens(rawTokens);
|
|
@@ -3467,7 +3780,8 @@ function localSymbol(local, snapshot) {
|
|
|
3467
3780
|
name: local.name,
|
|
3468
3781
|
kind: local.lambdaParameter ? "parameter" : "local",
|
|
3469
3782
|
type: local.lambdaParameter ? inferLambdaParameterType(local, snapshot) : resolveDeclaredType(local, snapshot),
|
|
3470
|
-
location: { uri: snapshot.uri, range: local.nameRange }
|
|
3783
|
+
location: { uri: snapshot.uri, range: local.nameRange },
|
|
3784
|
+
...local.collectionIteration || local.catchParameter ? { writable: false } : {}
|
|
3471
3785
|
},
|
|
3472
3786
|
local.scopeStart,
|
|
3473
3787
|
local.scopeEnd
|
|
@@ -4217,14 +4531,20 @@ function parseNeoScript(lexed, kind) {
|
|
|
4217
4531
|
const locals = [];
|
|
4218
4532
|
const references = [];
|
|
4219
4533
|
const calls = [];
|
|
4534
|
+
const switches = [];
|
|
4220
4535
|
for (const executable of executableRegions) {
|
|
4221
4536
|
const executablePairs = pairBrackets(executable);
|
|
4222
|
-
const
|
|
4537
|
+
const regionSwitches = parseSwitchStatements(
|
|
4223
4538
|
executable,
|
|
4224
4539
|
executablePairs,
|
|
4225
4540
|
lexed,
|
|
4226
4541
|
diagnostics
|
|
4227
4542
|
);
|
|
4543
|
+
const regionLocals = applySwitchSectionScopes(
|
|
4544
|
+
parseLocals(executable, executablePairs, lexed, diagnostics),
|
|
4545
|
+
regionSwitches,
|
|
4546
|
+
lexed
|
|
4547
|
+
);
|
|
4228
4548
|
const regionCalls = parseCalls(executable, executablePairs, lexed);
|
|
4229
4549
|
const declarations = new Set(
|
|
4230
4550
|
regionLocals.map(
|
|
@@ -4246,9 +4566,10 @@ function parseNeoScript(lexed, kind) {
|
|
|
4246
4566
|
...parseReferences(executable, declarations, contextualKeywords)
|
|
4247
4567
|
);
|
|
4248
4568
|
calls.push(...regionCalls);
|
|
4569
|
+
switches.push(...regionSwitches);
|
|
4249
4570
|
validateTerminatedStatements(executable, diagnostics);
|
|
4250
4571
|
}
|
|
4251
|
-
return { units, locals, references, calls, diagnostics };
|
|
4572
|
+
return { units, locals, references, calls, switches, diagnostics };
|
|
4252
4573
|
}
|
|
4253
4574
|
function parseUnits(tokens, pairs, kind, lexed) {
|
|
4254
4575
|
if (kind !== "property") {
|
|
@@ -4413,6 +4734,158 @@ function pairBrackets(tokens) {
|
|
|
4413
4734
|
}
|
|
4414
4735
|
return pairs;
|
|
4415
4736
|
}
|
|
4737
|
+
function parseSwitchStatements(tokens, pairs, lexed, diagnostics) {
|
|
4738
|
+
const switches = [];
|
|
4739
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
4740
|
+
const keyword = tokens[index];
|
|
4741
|
+
const openParen = tokens[index + 1];
|
|
4742
|
+
if (keyword?.kind !== "keyword" || keyword.text !== "switch") continue;
|
|
4743
|
+
if (!openParen || openParen.kind !== "punctuation" || openParen.text !== "(") {
|
|
4744
|
+
continue;
|
|
4745
|
+
}
|
|
4746
|
+
const closeParenIndex = pairs.get(index + 1);
|
|
4747
|
+
if (closeParenIndex === void 0) continue;
|
|
4748
|
+
const openBraceIndex = closeParenIndex + 1;
|
|
4749
|
+
const openBrace = tokens[openBraceIndex];
|
|
4750
|
+
if (!openBrace || openBrace.kind !== "punctuation" || openBrace.text !== "{") {
|
|
4751
|
+
continue;
|
|
4752
|
+
}
|
|
4753
|
+
const closeBraceIndex = pairs.get(openBraceIndex);
|
|
4754
|
+
const limit = closeBraceIndex ?? tokens.length;
|
|
4755
|
+
const closeBrace = closeBraceIndex === void 0 ? void 0 : tokens[closeBraceIndex];
|
|
4756
|
+
const selectorTokens = tokens.slice(index + 2, closeParenIndex);
|
|
4757
|
+
const selectorStart = selectorTokens[0]?.start ?? openParen.end;
|
|
4758
|
+
const selectorEnd = selectorTokens.at(-1)?.end ?? selectorStart;
|
|
4759
|
+
const sections = [];
|
|
4760
|
+
let cursor = openBraceIndex + 1;
|
|
4761
|
+
while (cursor < limit) {
|
|
4762
|
+
const firstLabelIndex = findNextSwitchLabel(tokens, cursor, limit, pairs);
|
|
4763
|
+
if (firstLabelIndex === null) break;
|
|
4764
|
+
cursor = firstLabelIndex;
|
|
4765
|
+
const labels = [];
|
|
4766
|
+
while (cursor < limit) {
|
|
4767
|
+
const labelKeyword = tokens[cursor];
|
|
4768
|
+
if (labelKeyword?.kind !== "keyword" || labelKeyword.text !== "case" && labelKeyword.text !== "default") {
|
|
4769
|
+
break;
|
|
4770
|
+
}
|
|
4771
|
+
const nextLabelIndex = findNextSwitchLabel(
|
|
4772
|
+
tokens,
|
|
4773
|
+
cursor + 1,
|
|
4774
|
+
limit,
|
|
4775
|
+
pairs
|
|
4776
|
+
);
|
|
4777
|
+
const labelLimit = nextLabelIndex ?? limit;
|
|
4778
|
+
const colonIndex = findSwitchLabelColon(
|
|
4779
|
+
tokens,
|
|
4780
|
+
cursor + 1,
|
|
4781
|
+
labelLimit,
|
|
4782
|
+
pairs
|
|
4783
|
+
);
|
|
4784
|
+
const colon = colonIndex === null ? void 0 : tokens[colonIndex];
|
|
4785
|
+
const expressionEndIndex = colonIndex ?? labelLimit;
|
|
4786
|
+
const expressionTokens = labelKeyword.text === "case" ? tokens.slice(cursor + 1, expressionEndIndex) : [];
|
|
4787
|
+
if (!colon) {
|
|
4788
|
+
diagnostics.push({
|
|
4789
|
+
range: labelKeyword.range,
|
|
4790
|
+
severity: "error",
|
|
4791
|
+
source: "neoscript",
|
|
4792
|
+
code: "missing-switch-label-colon",
|
|
4793
|
+
message: `The \`${labelKeyword.text}\` label must end with \`:\`.`,
|
|
4794
|
+
suggestions: ["Add `:` after the switch label."]
|
|
4795
|
+
});
|
|
4796
|
+
}
|
|
4797
|
+
const labelEnd = colon?.end ?? tokens[labelLimit]?.start ?? closeBrace?.start ?? lexed.source.text.length;
|
|
4798
|
+
labels.push({
|
|
4799
|
+
kind: labelKeyword.text,
|
|
4800
|
+
keywordRange: labelKeyword.range,
|
|
4801
|
+
expressionTokens,
|
|
4802
|
+
start: labelKeyword.start,
|
|
4803
|
+
end: labelEnd,
|
|
4804
|
+
colonEnd: colon?.end ?? labelEnd
|
|
4805
|
+
});
|
|
4806
|
+
cursor = colonIndex === null ? labelLimit : colonIndex + 1;
|
|
4807
|
+
if (labelKeyword.text === "default") break;
|
|
4808
|
+
const adjacent = tokens[cursor];
|
|
4809
|
+
if (adjacent?.kind !== "keyword" || adjacent.text !== "case") break;
|
|
4810
|
+
}
|
|
4811
|
+
const lastLabel = labels.at(-1);
|
|
4812
|
+
if (!lastLabel) {
|
|
4813
|
+
cursor++;
|
|
4814
|
+
continue;
|
|
4815
|
+
}
|
|
4816
|
+
const nextSectionIndex = findNextSwitchLabel(
|
|
4817
|
+
tokens,
|
|
4818
|
+
cursor,
|
|
4819
|
+
limit,
|
|
4820
|
+
pairs
|
|
4821
|
+
);
|
|
4822
|
+
sections.push({
|
|
4823
|
+
labels,
|
|
4824
|
+
bodyStart: lastLabel.colonEnd,
|
|
4825
|
+
bodyEnd: nextSectionIndex === null ? closeBrace?.start ?? lexed.source.text.length : tokens[nextSectionIndex]?.start ?? lexed.source.text.length
|
|
4826
|
+
});
|
|
4827
|
+
cursor = nextSectionIndex ?? limit;
|
|
4828
|
+
}
|
|
4829
|
+
switches.push({
|
|
4830
|
+
selectorTokens,
|
|
4831
|
+
selectorRange: lexed.source.range(selectorStart, selectorEnd),
|
|
4832
|
+
sections,
|
|
4833
|
+
start: keyword.start,
|
|
4834
|
+
end: closeBrace?.end ?? lexed.source.text.length
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
return switches;
|
|
4838
|
+
}
|
|
4839
|
+
function findNextSwitchLabel(tokens, start, limit, pairs) {
|
|
4840
|
+
let cursor = start;
|
|
4841
|
+
while (cursor < limit) {
|
|
4842
|
+
const token = tokens[cursor];
|
|
4843
|
+
if (!token) return null;
|
|
4844
|
+
if (token.kind === "keyword" && (token.text === "case" || token.text === "default")) {
|
|
4845
|
+
return cursor;
|
|
4846
|
+
}
|
|
4847
|
+
if (token.kind === "punctuation" && (token.text === "(" || token.text === "[" || token.text === "{")) {
|
|
4848
|
+
const close = pairs.get(cursor);
|
|
4849
|
+
if (close !== void 0 && close < limit) {
|
|
4850
|
+
cursor = close + 1;
|
|
4851
|
+
continue;
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
cursor++;
|
|
4855
|
+
}
|
|
4856
|
+
return null;
|
|
4857
|
+
}
|
|
4858
|
+
function findSwitchLabelColon(tokens, start, limit, pairs) {
|
|
4859
|
+
let cursor = start;
|
|
4860
|
+
while (cursor < limit) {
|
|
4861
|
+
const token = tokens[cursor];
|
|
4862
|
+
if (!token) return null;
|
|
4863
|
+
if (token.kind === "punctuation" && token.text === ":") return cursor;
|
|
4864
|
+
if (token.kind === "punctuation" && (token.text === "(" || token.text === "[" || token.text === "{")) {
|
|
4865
|
+
const close = pairs.get(cursor);
|
|
4866
|
+
if (close !== void 0 && close < limit) {
|
|
4867
|
+
cursor = close + 1;
|
|
4868
|
+
continue;
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
cursor++;
|
|
4872
|
+
}
|
|
4873
|
+
return null;
|
|
4874
|
+
}
|
|
4875
|
+
function applySwitchSectionScopes(locals, switches, lexed) {
|
|
4876
|
+
return locals.map((local) => {
|
|
4877
|
+
const declaration = lexed.source.offsetAt(local.nameRange.start);
|
|
4878
|
+
let scopeEnd = local.scopeEnd;
|
|
4879
|
+
for (const statement of switches) {
|
|
4880
|
+
for (const section of statement.sections) {
|
|
4881
|
+
if (declaration >= section.bodyStart && declaration < section.bodyEnd) {
|
|
4882
|
+
scopeEnd = Math.min(scopeEnd, section.bodyEnd);
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
return scopeEnd === local.scopeEnd ? local : { ...local, scopeEnd };
|
|
4887
|
+
});
|
|
4888
|
+
}
|
|
4416
4889
|
function validatePropertyUnits(units, diagnostics, lexed) {
|
|
4417
4890
|
const getters = units.filter((unit) => unit.kind === "getter");
|
|
4418
4891
|
const setters = units.filter((unit) => unit.kind === "setter");
|
|
@@ -4450,6 +4923,18 @@ function duplicateUnitDiagnostic(label, units, diagnostics) {
|
|
|
4450
4923
|
function parseLocals(tokens, pairs, lexed, diagnostics) {
|
|
4451
4924
|
const locals = [];
|
|
4452
4925
|
for (let index = 0; index < tokens.length; index++) {
|
|
4926
|
+
const caught = tryParseCatchParameter(tokens, index, pairs, lexed);
|
|
4927
|
+
if (caught) {
|
|
4928
|
+
locals.push(caught.local);
|
|
4929
|
+
index = caught.resumeTokenIndex;
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
const loop = tryParseLoopLocal(tokens, index, pairs, lexed, diagnostics);
|
|
4933
|
+
if (loop) {
|
|
4934
|
+
locals.push(loop.local);
|
|
4935
|
+
index = loop.resumeTokenIndex;
|
|
4936
|
+
continue;
|
|
4937
|
+
}
|
|
4453
4938
|
const lambda = tryParseLambdaParameters(tokens, index, pairs, lexed);
|
|
4454
4939
|
if (lambda) {
|
|
4455
4940
|
locals.push(...lambda.locals);
|
|
@@ -4711,13 +5196,17 @@ function validateTerminatedStatements(tokens, diagnostics) {
|
|
|
4711
5196
|
for (let index = 0; index < tokens.length; index++) {
|
|
4712
5197
|
const token = tokens[index];
|
|
4713
5198
|
if (!token || token.kind !== "keyword") continue;
|
|
4714
|
-
if (token.text !== "return" && token.text !== "throw"
|
|
5199
|
+
if (token.text !== "return" && token.text !== "throw" && token.text !== "break" && token.text !== "continue")
|
|
5200
|
+
continue;
|
|
4715
5201
|
let cursor = index + 1;
|
|
4716
5202
|
let nested = 0;
|
|
4717
5203
|
let terminated = false;
|
|
4718
5204
|
while (cursor < tokens.length) {
|
|
4719
5205
|
const current = tokens[cursor];
|
|
4720
5206
|
if (!current) break;
|
|
5207
|
+
if (nested === 0 && current.kind === "keyword" && (current.text === "case" || current.text === "default")) {
|
|
5208
|
+
break;
|
|
5209
|
+
}
|
|
4721
5210
|
if (current.kind === "punctuation" && ["(", "[", "{"].includes(current.text))
|
|
4722
5211
|
nested++;
|
|
4723
5212
|
if (current.kind === "punctuation" && current.text === "}") {
|
|
@@ -4778,6 +5267,126 @@ function containingBraceRange(tokens, tokenIndex, documentEnd) {
|
|
|
4778
5267
|
}
|
|
4779
5268
|
return { start, end };
|
|
4780
5269
|
}
|
|
5270
|
+
function tryParseCatchParameter(tokens, start, pairs, lexed) {
|
|
5271
|
+
const keyword = tokens[start];
|
|
5272
|
+
const open = tokens[start + 1];
|
|
5273
|
+
if (keyword?.kind !== "keyword" || keyword.text !== "catch" || open?.kind !== "punctuation" || open.text !== "(") {
|
|
5274
|
+
return null;
|
|
5275
|
+
}
|
|
5276
|
+
const closeIndex = pairs.get(start + 1);
|
|
5277
|
+
if (closeIndex === void 0) return null;
|
|
5278
|
+
const type = tokens[start + 2];
|
|
5279
|
+
const name = tokens[start + 3];
|
|
5280
|
+
if (closeIndex !== start + 4 || type?.kind !== "type" || type.text !== "string" || name?.kind !== "identifier") {
|
|
5281
|
+
return null;
|
|
5282
|
+
}
|
|
5283
|
+
const possibleWhen = tokens[closeIndex + 1];
|
|
5284
|
+
const when = possibleWhen?.kind === "identifier" && possibleWhen.text === "when" ? possibleWhen : void 0;
|
|
5285
|
+
let bodyIndex = closeIndex + 1;
|
|
5286
|
+
if (when) {
|
|
5287
|
+
const filterOpen = tokens[closeIndex + 2];
|
|
5288
|
+
if (filterOpen?.kind === "punctuation" && filterOpen.text === "(") {
|
|
5289
|
+
const filterCloseIndex = pairs.get(closeIndex + 2);
|
|
5290
|
+
bodyIndex = filterCloseIndex === void 0 ? tokens.length : filterCloseIndex + 1;
|
|
5291
|
+
} else {
|
|
5292
|
+
bodyIndex = closeIndex + 2;
|
|
5293
|
+
}
|
|
5294
|
+
}
|
|
5295
|
+
const scopeEnd = loopBodyScopeEnd(
|
|
5296
|
+
tokens,
|
|
5297
|
+
bodyIndex,
|
|
5298
|
+
pairs,
|
|
5299
|
+
lexed.source.text.length
|
|
5300
|
+
);
|
|
5301
|
+
const body = tokens[bodyIndex];
|
|
5302
|
+
const catchBodyStart = body?.kind === "punctuation" && body.text === "{" ? body.end : body?.start;
|
|
5303
|
+
return {
|
|
5304
|
+
local: {
|
|
5305
|
+
name: name.text,
|
|
5306
|
+
nameRange: name.range,
|
|
5307
|
+
typeTokens: [type],
|
|
5308
|
+
inferred: false,
|
|
5309
|
+
...when ? { contextualKeywordRange: when.range } : {},
|
|
5310
|
+
catchParameter: true,
|
|
5311
|
+
catchKeywordStart: keyword.start,
|
|
5312
|
+
...catchBodyStart === void 0 ? {} : { catchBodyStart },
|
|
5313
|
+
declarationRange: lexed.source.range(type.start, name.end),
|
|
5314
|
+
scopeStart: name.start,
|
|
5315
|
+
scopeEnd,
|
|
5316
|
+
lambdaParameter: false
|
|
5317
|
+
},
|
|
5318
|
+
resumeTokenIndex: closeIndex
|
|
5319
|
+
};
|
|
5320
|
+
}
|
|
5321
|
+
function tryParseLoopLocal(tokens, start, pairs, lexed, diagnostics) {
|
|
5322
|
+
const keyword = tokens[start];
|
|
5323
|
+
if (!keyword || keyword.kind !== "keyword" || keyword.text !== "for" && keyword.text !== "foreach") {
|
|
5324
|
+
return null;
|
|
5325
|
+
}
|
|
5326
|
+
const open = tokens[start + 1];
|
|
5327
|
+
if (!open || open.kind !== "punctuation" || open.text !== "(") return null;
|
|
5328
|
+
const closeIndex = pairs.get(start + 1);
|
|
5329
|
+
if (closeIndex === void 0) return null;
|
|
5330
|
+
const scopeEnd = loopBodyScopeEnd(
|
|
5331
|
+
tokens,
|
|
5332
|
+
closeIndex + 1,
|
|
5333
|
+
pairs,
|
|
5334
|
+
lexed.source.text.length
|
|
5335
|
+
);
|
|
5336
|
+
if (keyword.text === "for") {
|
|
5337
|
+
const declaration = tryParseVariableDeclaration(tokens, start + 2, lexed);
|
|
5338
|
+
if (!declaration || declaration.endTokenIndex >= closeIndex) return null;
|
|
5339
|
+
if (declaration.missingInitializer) {
|
|
5340
|
+
diagnostics.push({
|
|
5341
|
+
range: declaration.local.nameRange,
|
|
5342
|
+
severity: "error",
|
|
5343
|
+
source: "neoscript",
|
|
5344
|
+
code: "missing-var-initializer",
|
|
5345
|
+
message: `Cannot infer the type of \`${declaration.local.name}\` without an initializer; use \`var ${declaration.local.name} = ...;\` or an explicit type.`
|
|
5346
|
+
});
|
|
5347
|
+
}
|
|
5348
|
+
return {
|
|
5349
|
+
local: { ...declaration.local, scopeEnd },
|
|
5350
|
+
resumeTokenIndex: declaration.endTokenIndex
|
|
5351
|
+
};
|
|
5352
|
+
}
|
|
5353
|
+
const header = tokens.slice(start + 2, closeIndex);
|
|
5354
|
+
const inOffset = header.findIndex(
|
|
5355
|
+
(token) => token.kind === "keyword" && token.text === "in"
|
|
5356
|
+
);
|
|
5357
|
+
if (inOffset < 2) return null;
|
|
5358
|
+
const inIndex = start + 2 + inOffset;
|
|
5359
|
+
const name = tokens[inIndex - 1];
|
|
5360
|
+
const first = tokens[start + 2];
|
|
5361
|
+
if (!name || name.kind !== "identifier" || !first) return null;
|
|
5362
|
+
const inferred = first.kind === "identifier" && first.text === NEOSCRIPT_INFERRED_LOCAL_KEYWORD;
|
|
5363
|
+
return {
|
|
5364
|
+
local: {
|
|
5365
|
+
name: name.text,
|
|
5366
|
+
nameRange: name.range,
|
|
5367
|
+
typeTokens: inferred ? [] : tokens.slice(start + 2, inIndex - 1),
|
|
5368
|
+
inferred,
|
|
5369
|
+
...inferred ? { contextualKeywordRange: first.range } : {},
|
|
5370
|
+
initializerTokens: tokens.slice(inIndex + 1, closeIndex),
|
|
5371
|
+
collectionIteration: true,
|
|
5372
|
+
declarationRange: lexed.source.range(first.start, name.end),
|
|
5373
|
+
scopeStart: tokens[closeIndex]?.end ?? name.end,
|
|
5374
|
+
scopeEnd,
|
|
5375
|
+
lambdaParameter: false
|
|
5376
|
+
},
|
|
5377
|
+
resumeTokenIndex: inIndex - 1
|
|
5378
|
+
};
|
|
5379
|
+
}
|
|
5380
|
+
function loopBodyScopeEnd(tokens, bodyStart, pairs, documentEnd) {
|
|
5381
|
+
const first = tokens[bodyStart];
|
|
5382
|
+
if (!first) return documentEnd;
|
|
5383
|
+
if (first.kind === "punctuation" && first.text === "{") {
|
|
5384
|
+
const closeIndex = pairs.get(bodyStart);
|
|
5385
|
+
return closeIndex === void 0 ? documentEnd : tokens[closeIndex]?.start ?? documentEnd;
|
|
5386
|
+
}
|
|
5387
|
+
const endIndex = statementEndTokenIndex(tokens, bodyStart);
|
|
5388
|
+
return tokens[endIndex]?.end ?? documentEnd;
|
|
5389
|
+
}
|
|
4781
5390
|
var OPEN_TO_CLOSE, CLOSE_TO_OPEN;
|
|
4782
5391
|
var init_parser = __esm({
|
|
4783
5392
|
"../packages/neoscript-language/src/parser.ts"() {
|
|
@@ -5285,6 +5894,16 @@ var init_strict_lexer = __esm({
|
|
|
5285
5894
|
// statement keywords
|
|
5286
5895
|
"if",
|
|
5287
5896
|
"else",
|
|
5897
|
+
"for",
|
|
5898
|
+
"foreach",
|
|
5899
|
+
"in",
|
|
5900
|
+
"break",
|
|
5901
|
+
"continue",
|
|
5902
|
+
"switch",
|
|
5903
|
+
"case",
|
|
5904
|
+
"default",
|
|
5905
|
+
"try",
|
|
5906
|
+
"catch",
|
|
5288
5907
|
"abstract",
|
|
5289
5908
|
"async",
|
|
5290
5909
|
"class",
|
|
@@ -5436,12 +6055,34 @@ var init_strict_parser = __esm({
|
|
|
5436
6055
|
if (t.kind === "keyword" && t.text === "if") {
|
|
5437
6056
|
return this.parseIf();
|
|
5438
6057
|
}
|
|
6058
|
+
if (t.kind === "keyword" && t.text === "try") {
|
|
6059
|
+
return this.parseTry();
|
|
6060
|
+
}
|
|
6061
|
+
if (t.kind === "keyword" && t.text === "for") {
|
|
6062
|
+
return this.parseFor();
|
|
6063
|
+
}
|
|
6064
|
+
if (t.kind === "keyword" && t.text === "foreach") {
|
|
6065
|
+
return this.parseForEach();
|
|
6066
|
+
}
|
|
6067
|
+
if (t.kind === "keyword" && t.text === "switch") {
|
|
6068
|
+
return this.parseSwitch();
|
|
6069
|
+
}
|
|
5439
6070
|
if (t.kind === "keyword" && t.text === "return") {
|
|
5440
6071
|
return this.parseReturn();
|
|
5441
6072
|
}
|
|
5442
6073
|
if (t.kind === "keyword" && t.text === "throw") {
|
|
5443
6074
|
return this.parseThrow();
|
|
5444
6075
|
}
|
|
6076
|
+
if (t.kind === "keyword" && t.text === "break") {
|
|
6077
|
+
this.next();
|
|
6078
|
+
this.expect("punct", ";");
|
|
6079
|
+
return { kind: "break", pos: t.pos };
|
|
6080
|
+
}
|
|
6081
|
+
if (t.kind === "keyword" && t.text === "continue") {
|
|
6082
|
+
this.next();
|
|
6083
|
+
this.expect("punct", ";");
|
|
6084
|
+
return { kind: "continue", pos: t.pos };
|
|
6085
|
+
}
|
|
5445
6086
|
if (this.looksLikeVarDecl()) {
|
|
5446
6087
|
return this.parseVarDecl();
|
|
5447
6088
|
}
|
|
@@ -5505,6 +6146,226 @@ var init_strict_parser = __esm({
|
|
|
5505
6146
|
const body = this.parseStatementBody();
|
|
5506
6147
|
return { cond, body, pos };
|
|
5507
6148
|
}
|
|
6149
|
+
parseFor() {
|
|
6150
|
+
const token = this.expect("keyword", "for");
|
|
6151
|
+
this.expect("punct", "(");
|
|
6152
|
+
if (!this.looksLikeVarDecl()) {
|
|
6153
|
+
throw new CompileError(
|
|
6154
|
+
"NeoScript for loops require one initialized local declaration.",
|
|
6155
|
+
this.peek().pos
|
|
6156
|
+
);
|
|
6157
|
+
}
|
|
6158
|
+
const initializer = this.parseVarDecl(false);
|
|
6159
|
+
this.expectSingleForClause(";");
|
|
6160
|
+
if (this.peek().kind === "punct" && this.peek().text === ";") {
|
|
6161
|
+
throw new CompileError(
|
|
6162
|
+
"NeoScript for loops require a condition.",
|
|
6163
|
+
this.peek().pos
|
|
6164
|
+
);
|
|
6165
|
+
}
|
|
6166
|
+
const condition = this.parseExpr();
|
|
6167
|
+
this.expectSingleForClause(";");
|
|
6168
|
+
if (this.peek().kind === "punct" && this.peek().text === ")") {
|
|
6169
|
+
throw new CompileError(
|
|
6170
|
+
"NeoScript for loops require one iterator.",
|
|
6171
|
+
this.peek().pos
|
|
6172
|
+
);
|
|
6173
|
+
}
|
|
6174
|
+
const iterator = this.parseForIterator();
|
|
6175
|
+
this.expectSingleForClause(")");
|
|
6176
|
+
return {
|
|
6177
|
+
kind: "for",
|
|
6178
|
+
initializer,
|
|
6179
|
+
condition,
|
|
6180
|
+
iterator,
|
|
6181
|
+
body: this.parseStatementBody(),
|
|
6182
|
+
pos: token.pos
|
|
6183
|
+
};
|
|
6184
|
+
}
|
|
6185
|
+
parseForEach() {
|
|
6186
|
+
const token = this.expect("keyword", "foreach");
|
|
6187
|
+
this.expect("punct", "(");
|
|
6188
|
+
const inferred = this.peek().kind === "ident" && this.peek().text === NEOSCRIPT_INFERRED_LOCAL_KEYWORD;
|
|
6189
|
+
let type;
|
|
6190
|
+
if (inferred) {
|
|
6191
|
+
this.next();
|
|
6192
|
+
type = null;
|
|
6193
|
+
} else {
|
|
6194
|
+
type = this.parseType();
|
|
6195
|
+
}
|
|
6196
|
+
const name = this.expect("ident");
|
|
6197
|
+
this.expect("keyword", "in");
|
|
6198
|
+
const collection = this.parseExpr();
|
|
6199
|
+
this.expect("punct", ")");
|
|
6200
|
+
return {
|
|
6201
|
+
kind: "forEach",
|
|
6202
|
+
type,
|
|
6203
|
+
name: name.text,
|
|
6204
|
+
collection,
|
|
6205
|
+
body: this.parseStatementBody(),
|
|
6206
|
+
pos: token.pos
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6209
|
+
parseTry() {
|
|
6210
|
+
const token = this.expect("keyword", "try");
|
|
6211
|
+
const body = this.parseBlock();
|
|
6212
|
+
const catches = [];
|
|
6213
|
+
while (this.peek().kind === "keyword" && this.peek().text === "catch") {
|
|
6214
|
+
catches.push(this.parseCatchClause());
|
|
6215
|
+
}
|
|
6216
|
+
if (catches.length === 0) {
|
|
6217
|
+
throw new CompileError(
|
|
6218
|
+
"`try` must be followed by at least one `catch` clause.",
|
|
6219
|
+
token.pos
|
|
6220
|
+
);
|
|
6221
|
+
}
|
|
6222
|
+
return { kind: "try", body, catches, pos: token.pos };
|
|
6223
|
+
}
|
|
6224
|
+
parseCatchClause() {
|
|
6225
|
+
const token = this.expect("keyword", "catch");
|
|
6226
|
+
const open = this.peek();
|
|
6227
|
+
if (open.kind !== "punct" || open.text !== "(") {
|
|
6228
|
+
throw new CompileError(
|
|
6229
|
+
"NeoScript catch parameters must have the form `catch (string name)`.",
|
|
6230
|
+
open.pos
|
|
6231
|
+
);
|
|
6232
|
+
}
|
|
6233
|
+
this.next();
|
|
6234
|
+
const type = this.peek();
|
|
6235
|
+
const name = this.peek(1);
|
|
6236
|
+
const close = this.peek(2);
|
|
6237
|
+
if (type.kind !== "keyword" || type.text !== "string" || name.kind !== "ident" || close.kind !== "punct" || close.text !== ")") {
|
|
6238
|
+
throw new CompileError(
|
|
6239
|
+
"NeoScript catch parameters must have the form `catch (string name)`.",
|
|
6240
|
+
type.pos
|
|
6241
|
+
);
|
|
6242
|
+
}
|
|
6243
|
+
this.next();
|
|
6244
|
+
this.next();
|
|
6245
|
+
this.next();
|
|
6246
|
+
let filter = null;
|
|
6247
|
+
if (this.peek().kind === "ident" && this.peek().text === "when") {
|
|
6248
|
+
this.next();
|
|
6249
|
+
this.expect("punct", "(");
|
|
6250
|
+
filter = this.parseExpr();
|
|
6251
|
+
this.expect("punct", ")");
|
|
6252
|
+
}
|
|
6253
|
+
return {
|
|
6254
|
+
name: name.text,
|
|
6255
|
+
filter,
|
|
6256
|
+
body: this.parseBlock(),
|
|
6257
|
+
pos: token.pos
|
|
6258
|
+
};
|
|
6259
|
+
}
|
|
6260
|
+
parseSwitch() {
|
|
6261
|
+
const token = this.expect("keyword", "switch");
|
|
6262
|
+
this.expect("punct", "(");
|
|
6263
|
+
const selector = this.parseExpr();
|
|
6264
|
+
this.expect("punct", ")");
|
|
6265
|
+
this.expect("punct", "{");
|
|
6266
|
+
const sections = [];
|
|
6267
|
+
while (!(this.peek().kind === "punct" && this.peek().text === "}")) {
|
|
6268
|
+
const sectionStart = this.peek();
|
|
6269
|
+
if (sectionStart.kind === "eof") {
|
|
6270
|
+
throw new CompileError(
|
|
6271
|
+
"Unexpected end of input \u2014 missing `}`?",
|
|
6272
|
+
sectionStart.pos
|
|
6273
|
+
);
|
|
6274
|
+
}
|
|
6275
|
+
if (sectionStart.kind !== "keyword" || sectionStart.text !== "case" && sectionStart.text !== "default") {
|
|
6276
|
+
throw new CompileError(
|
|
6277
|
+
"Expected `case` or `default` in switch statement.",
|
|
6278
|
+
sectionStart.pos
|
|
6279
|
+
);
|
|
6280
|
+
}
|
|
6281
|
+
if (sectionStart.text === "default") {
|
|
6282
|
+
this.next();
|
|
6283
|
+
this.expect("punct", ":");
|
|
6284
|
+
sections.push({
|
|
6285
|
+
labels: [{ kind: "default", pos: sectionStart.pos }],
|
|
6286
|
+
body: this.parseSwitchSectionBody(),
|
|
6287
|
+
pos: sectionStart.pos
|
|
6288
|
+
});
|
|
6289
|
+
continue;
|
|
6290
|
+
}
|
|
6291
|
+
const labels = [];
|
|
6292
|
+
while (this.peek().kind === "keyword" && this.peek().text === "case") {
|
|
6293
|
+
const caseToken = this.next();
|
|
6294
|
+
labels.push({
|
|
6295
|
+
kind: "case",
|
|
6296
|
+
expression: this.parseExpr(),
|
|
6297
|
+
pos: caseToken.pos
|
|
6298
|
+
});
|
|
6299
|
+
this.expect("punct", ":");
|
|
6300
|
+
}
|
|
6301
|
+
sections.push({
|
|
6302
|
+
labels,
|
|
6303
|
+
body: this.parseSwitchSectionBody(),
|
|
6304
|
+
pos: sectionStart.pos
|
|
6305
|
+
});
|
|
6306
|
+
}
|
|
6307
|
+
this.expect("punct", "}");
|
|
6308
|
+
return { kind: "switch", selector, sections, pos: token.pos };
|
|
6309
|
+
}
|
|
6310
|
+
parseSwitchSectionBody() {
|
|
6311
|
+
const body = [];
|
|
6312
|
+
while (true) {
|
|
6313
|
+
const token = this.peek();
|
|
6314
|
+
if (token.kind === "punct" && token.text === "}") return body;
|
|
6315
|
+
if (token.kind === "keyword" && (token.text === "case" || token.text === "default")) {
|
|
6316
|
+
return body;
|
|
6317
|
+
}
|
|
6318
|
+
body.push(this.parseStatement());
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
parseForIterator() {
|
|
6322
|
+
const start = this.peek();
|
|
6323
|
+
if (isIncrementOp(start)) {
|
|
6324
|
+
const op = this.next();
|
|
6325
|
+
return {
|
|
6326
|
+
kind: "assign",
|
|
6327
|
+
target: this.parseExpr(),
|
|
6328
|
+
op: op.text,
|
|
6329
|
+
value: null,
|
|
6330
|
+
pos: start.pos
|
|
6331
|
+
};
|
|
6332
|
+
}
|
|
6333
|
+
const target = this.parseExpr();
|
|
6334
|
+
const operator = this.peek();
|
|
6335
|
+
if (isIncrementOp(operator)) {
|
|
6336
|
+
this.next();
|
|
6337
|
+
return {
|
|
6338
|
+
kind: "assign",
|
|
6339
|
+
target,
|
|
6340
|
+
op: operator.text,
|
|
6341
|
+
value: null,
|
|
6342
|
+
pos: start.pos
|
|
6343
|
+
};
|
|
6344
|
+
}
|
|
6345
|
+
if (!isAssignmentOp(operator)) {
|
|
6346
|
+
throw new CompileError(
|
|
6347
|
+
"NeoScript for loop iterator must be an assignment, increment, or decrement.",
|
|
6348
|
+
operator.pos
|
|
6349
|
+
);
|
|
6350
|
+
}
|
|
6351
|
+
this.next();
|
|
6352
|
+
return {
|
|
6353
|
+
kind: "assign",
|
|
6354
|
+
target,
|
|
6355
|
+
op: operator.text,
|
|
6356
|
+
value: this.parseExpr(),
|
|
6357
|
+
pos: start.pos
|
|
6358
|
+
};
|
|
6359
|
+
}
|
|
6360
|
+
expectSingleForClause(terminator) {
|
|
6361
|
+
if (this.peek().kind === "punct" && this.peek().text === ",") {
|
|
6362
|
+
throw new CompileError(
|
|
6363
|
+
"NeoScript for loops support one initializer and one iterator.",
|
|
6364
|
+
this.peek().pos
|
|
6365
|
+
);
|
|
6366
|
+
}
|
|
6367
|
+
this.expect("punct", terminator);
|
|
6368
|
+
}
|
|
5508
6369
|
parseStatementBody() {
|
|
5509
6370
|
if (this.peek().kind === "punct" && this.peek().text === "{") {
|
|
5510
6371
|
return this.parseBlock();
|
|
@@ -5528,11 +6389,11 @@ var init_strict_parser = __esm({
|
|
|
5528
6389
|
}
|
|
5529
6390
|
parseThrow() {
|
|
5530
6391
|
const thrTok = this.expect("keyword", "throw");
|
|
5531
|
-
const expr = this.parseExpr();
|
|
6392
|
+
const expr = this.peek().kind === "punct" && this.peek().text === ";" ? null : this.parseExpr();
|
|
5532
6393
|
this.expect("punct", ";");
|
|
5533
6394
|
return { kind: "throw", expr, pos: thrTok.pos };
|
|
5534
6395
|
}
|
|
5535
|
-
parseVarDecl() {
|
|
6396
|
+
parseVarDecl(terminated = true) {
|
|
5536
6397
|
const startPos = this.peek().pos;
|
|
5537
6398
|
const inferred = this.peek().kind === "ident" && this.peek().text === NEOSCRIPT_INFERRED_LOCAL_KEYWORD;
|
|
5538
6399
|
let type;
|
|
@@ -5550,7 +6411,7 @@ var init_strict_parser = __esm({
|
|
|
5550
6411
|
);
|
|
5551
6412
|
}
|
|
5552
6413
|
const init = this.parseExpr();
|
|
5553
|
-
this.expect("punct", ";");
|
|
6414
|
+
if (terminated) this.expect("punct", ";");
|
|
5554
6415
|
return {
|
|
5555
6416
|
kind: "varDecl",
|
|
5556
6417
|
type,
|
|
@@ -5925,7 +6786,7 @@ var init_strict_parser = __esm({
|
|
|
5925
6786
|
}
|
|
5926
6787
|
while (true) {
|
|
5927
6788
|
let name = null;
|
|
5928
|
-
if (this.peek().kind === "ident" && this.peek(1).kind === "punct" && this.peek(1).text === ":") {
|
|
6789
|
+
if ((this.peek().kind === "ident" || this.peek().kind === "keyword" && this.peek().text === "default") && this.peek(1).kind === "punct" && this.peek(1).text === ":") {
|
|
5929
6790
|
name = this.next().text;
|
|
5930
6791
|
this.next();
|
|
5931
6792
|
}
|
|
@@ -6369,7 +7230,7 @@ var NEOSCRIPT_COMPILER_REVISION;
|
|
|
6369
7230
|
var init_strict_ir = __esm({
|
|
6370
7231
|
"../packages/neoscript-language/src/strict-ir.ts"() {
|
|
6371
7232
|
"use strict";
|
|
6372
|
-
NEOSCRIPT_COMPILER_REVISION =
|
|
7233
|
+
NEOSCRIPT_COMPILER_REVISION = 6;
|
|
6373
7234
|
}
|
|
6374
7235
|
});
|
|
6375
7236
|
|
|
@@ -6555,16 +7416,176 @@ function callMatchesDeclaredOverloadNames(declared, expression) {
|
|
|
6555
7416
|
) === callKey
|
|
6556
7417
|
);
|
|
6557
7418
|
}
|
|
6558
|
-
function scopeVariable(name, value, sourceType, writability, ownership, writeRoot) {
|
|
7419
|
+
function scopeVariable(name, value, sourceType, writability, ownership, writeRoot, readonlyBinding) {
|
|
6559
7420
|
return {
|
|
6560
7421
|
name,
|
|
6561
7422
|
type: sourceType ?? fromWireType(value.typeInfo),
|
|
6562
7423
|
variableId: value.id,
|
|
6563
7424
|
...writability ? { writability } : {},
|
|
6564
7425
|
...ownership ? { ownership } : {},
|
|
6565
|
-
...writeRoot ? { writeRoot } : {}
|
|
7426
|
+
...writeRoot ? { writeRoot } : {},
|
|
7427
|
+
...readonlyBinding ? { readonlyBinding } : {}
|
|
6566
7428
|
};
|
|
6567
7429
|
}
|
|
7430
|
+
function invalidateLoopEntryNarrowingsAssignedBy(loopEntryScope, outerScope, statements) {
|
|
7431
|
+
for (const rootName of collectAssignedPathRoots(statements)) {
|
|
7432
|
+
const outerEntry = outerScope.lookup(rootName);
|
|
7433
|
+
if (outerEntry) {
|
|
7434
|
+
loopEntryScope.hideInheritedNarrowing(outerEntry);
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
}
|
|
7438
|
+
function collectAssignedPathRoots(statements, roots = /* @__PURE__ */ new Set()) {
|
|
7439
|
+
for (const statement of statements) {
|
|
7440
|
+
if (statement.kind === "assign") {
|
|
7441
|
+
const assignedPath = canonicalPath(statement.target);
|
|
7442
|
+
if (assignedPath) roots.add(pathRoot(assignedPath));
|
|
7443
|
+
collectAssignedPathRootsInExpression(statement.target, roots);
|
|
7444
|
+
if (statement.value)
|
|
7445
|
+
collectAssignedPathRootsInExpression(statement.value, roots);
|
|
7446
|
+
continue;
|
|
7447
|
+
}
|
|
7448
|
+
if (statement.kind === "varDecl") {
|
|
7449
|
+
collectAssignedPathRootsInExpression(statement.init, roots);
|
|
7450
|
+
continue;
|
|
7451
|
+
}
|
|
7452
|
+
if (statement.kind === "if") {
|
|
7453
|
+
for (const branch of statement.branches) {
|
|
7454
|
+
collectAssignedPathRootsInExpression(branch.cond, roots);
|
|
7455
|
+
collectAssignedPathRoots(branch.body, roots);
|
|
7456
|
+
}
|
|
7457
|
+
if (statement.elseBody) {
|
|
7458
|
+
collectAssignedPathRoots(statement.elseBody, roots);
|
|
7459
|
+
}
|
|
7460
|
+
continue;
|
|
7461
|
+
}
|
|
7462
|
+
if (statement.kind === "try") {
|
|
7463
|
+
collectAssignedPathRoots(statement.body, roots);
|
|
7464
|
+
for (const clause of statement.catches) {
|
|
7465
|
+
if (clause.filter) {
|
|
7466
|
+
collectAssignedPathRootsInExpression(clause.filter, roots);
|
|
7467
|
+
}
|
|
7468
|
+
collectAssignedPathRoots(clause.body, roots);
|
|
7469
|
+
}
|
|
7470
|
+
continue;
|
|
7471
|
+
}
|
|
7472
|
+
if (statement.kind === "for") {
|
|
7473
|
+
collectAssignedPathRootsInExpression(statement.initializer.init, roots);
|
|
7474
|
+
collectAssignedPathRootsInExpression(statement.condition, roots);
|
|
7475
|
+
collectAssignedPathRoots([statement.iterator], roots);
|
|
7476
|
+
collectAssignedPathRoots(statement.body, roots);
|
|
7477
|
+
continue;
|
|
7478
|
+
}
|
|
7479
|
+
if (statement.kind === "forEach") {
|
|
7480
|
+
collectAssignedPathRootsInExpression(statement.collection, roots);
|
|
7481
|
+
collectAssignedPathRoots(statement.body, roots);
|
|
7482
|
+
continue;
|
|
7483
|
+
}
|
|
7484
|
+
if (statement.kind === "switch") {
|
|
7485
|
+
collectAssignedPathRootsInExpression(statement.selector, roots);
|
|
7486
|
+
for (const section of statement.sections) {
|
|
7487
|
+
for (const label of section.labels) {
|
|
7488
|
+
if (label.kind === "case") {
|
|
7489
|
+
collectAssignedPathRootsInExpression(label.expression, roots);
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
collectAssignedPathRoots(section.body, roots);
|
|
7493
|
+
}
|
|
7494
|
+
continue;
|
|
7495
|
+
}
|
|
7496
|
+
if (statement.kind === "return" || statement.kind === "throw") {
|
|
7497
|
+
if (statement.expr)
|
|
7498
|
+
collectAssignedPathRootsInExpression(statement.expr, roots);
|
|
7499
|
+
continue;
|
|
7500
|
+
}
|
|
7501
|
+
if (statement.kind === "exprStmt") {
|
|
7502
|
+
collectAssignedPathRootsInExpression(statement.expr, roots);
|
|
7503
|
+
}
|
|
7504
|
+
}
|
|
7505
|
+
return roots;
|
|
7506
|
+
}
|
|
7507
|
+
function collectAssignedPathRootsInExpression(expression, roots) {
|
|
7508
|
+
switch (expression.kind) {
|
|
7509
|
+
case "litInterp":
|
|
7510
|
+
for (const part of expression.parts) {
|
|
7511
|
+
if (part.kind === "expr") {
|
|
7512
|
+
collectAssignedPathRootsInExpression(part.expr, roots);
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
return;
|
|
7516
|
+
case "litList":
|
|
7517
|
+
for (const element of expression.elements) {
|
|
7518
|
+
collectAssignedPathRootsInExpression(element, roots);
|
|
7519
|
+
}
|
|
7520
|
+
return;
|
|
7521
|
+
case "litDict":
|
|
7522
|
+
for (const entry of expression.entries) {
|
|
7523
|
+
collectAssignedPathRootsInExpression(entry.key, roots);
|
|
7524
|
+
collectAssignedPathRootsInExpression(entry.value, roots);
|
|
7525
|
+
}
|
|
7526
|
+
return;
|
|
7527
|
+
case "member":
|
|
7528
|
+
collectAssignedPathRootsInExpression(expression.receiver, roots);
|
|
7529
|
+
return;
|
|
7530
|
+
case "index":
|
|
7531
|
+
collectAssignedPathRootsInExpression(expression.receiver, roots);
|
|
7532
|
+
collectAssignedPathRootsInExpression(expression.index, roots);
|
|
7533
|
+
return;
|
|
7534
|
+
case "call":
|
|
7535
|
+
collectAssignedPathRootsInExpression(expression.callee, roots);
|
|
7536
|
+
for (const argument2 of expression.args) {
|
|
7537
|
+
collectAssignedPathRootsInExpression(argument2, roots);
|
|
7538
|
+
}
|
|
7539
|
+
return;
|
|
7540
|
+
case "new":
|
|
7541
|
+
for (const argument2 of expression.args) {
|
|
7542
|
+
collectAssignedPathRootsInExpression(argument2, roots);
|
|
7543
|
+
}
|
|
7544
|
+
for (const initializer of expression.initializer ?? []) {
|
|
7545
|
+
collectAssignedPathRootsInExpression(initializer.value, roots);
|
|
7546
|
+
}
|
|
7547
|
+
return;
|
|
7548
|
+
case "annotated":
|
|
7549
|
+
for (const annotation2 of expression.annotations) {
|
|
7550
|
+
for (const argument2 of annotation2.args) {
|
|
7551
|
+
collectAssignedPathRootsInExpression(argument2, roots);
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
collectAssignedPathRootsInExpression(expression.expression, roots);
|
|
7555
|
+
return;
|
|
7556
|
+
case "binary":
|
|
7557
|
+
case "coalesce":
|
|
7558
|
+
collectAssignedPathRootsInExpression(expression.left, roots);
|
|
7559
|
+
collectAssignedPathRootsInExpression(expression.right, roots);
|
|
7560
|
+
return;
|
|
7561
|
+
case "unary":
|
|
7562
|
+
case "force":
|
|
7563
|
+
collectAssignedPathRootsInExpression(expression.operand, roots);
|
|
7564
|
+
return;
|
|
7565
|
+
case "is":
|
|
7566
|
+
collectAssignedPathRootsInExpression(expression.operand, roots);
|
|
7567
|
+
return;
|
|
7568
|
+
case "lambda":
|
|
7569
|
+
collectAssignedPathRoots(expression.body, roots);
|
|
7570
|
+
return;
|
|
7571
|
+
case "litNull":
|
|
7572
|
+
case "litBool":
|
|
7573
|
+
case "litInt":
|
|
7574
|
+
case "litFloat":
|
|
7575
|
+
case "litString":
|
|
7576
|
+
case "litTripleString":
|
|
7577
|
+
case "contextualEnum":
|
|
7578
|
+
case "ident":
|
|
7579
|
+
return;
|
|
7580
|
+
}
|
|
7581
|
+
}
|
|
7582
|
+
function mergeBranchInvalidations(parent, branches) {
|
|
7583
|
+
for (const branch of branches) {
|
|
7584
|
+
for (const entry of branch.invalidatedRootEntries()) {
|
|
7585
|
+
if (parent.containsEntry(entry)) parent.invalidate(entry);
|
|
7586
|
+
}
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
6568
7589
|
function mergeBranchOwnership(parent, branches) {
|
|
6569
7590
|
if (branches.length === 0) return;
|
|
6570
7591
|
const changed = /* @__PURE__ */ new Set();
|
|
@@ -7094,8 +8115,19 @@ function isAssignmentTarget(expression) {
|
|
|
7094
8115
|
}
|
|
7095
8116
|
function instructionsTerminate(instructions) {
|
|
7096
8117
|
for (const instruction of instructions) {
|
|
7097
|
-
if (instruction.type === "return" /* Return */ || instruction.type === "throw" /* Throw */)
|
|
8118
|
+
if (instruction.type === "return" /* Return */ || instruction.type === "throw" /* Throw */ || instruction.type === "break" /* Break */ || instruction.type === "continue" /* Continue */)
|
|
7098
8119
|
return true;
|
|
8120
|
+
if (instruction.type === "try" /* Try */ && instructionsTerminate(instruction.instructions) && instruction.catches.every(
|
|
8121
|
+
(clause) => instructionsTerminate(clause.instructions)
|
|
8122
|
+
)) {
|
|
8123
|
+
return true;
|
|
8124
|
+
}
|
|
8125
|
+
if (instruction.type === "switch" /* Switch */) {
|
|
8126
|
+
if (instruction.defaultInstructions !== void 0 && instruction.defaultInstructions !== null && instruction.sections.every(
|
|
8127
|
+
(section) => instructionsPropagatePastSwitch(section.instructions)
|
|
8128
|
+
) && instructionsPropagatePastSwitch(instruction.defaultInstructions))
|
|
8129
|
+
return true;
|
|
8130
|
+
}
|
|
7099
8131
|
if (instruction.type === "if" /* If */) {
|
|
7100
8132
|
if (!instruction.else) continue;
|
|
7101
8133
|
if (instruction.branches.every(
|
|
@@ -7106,6 +8138,35 @@ function instructionsTerminate(instructions) {
|
|
|
7106
8138
|
}
|
|
7107
8139
|
return false;
|
|
7108
8140
|
}
|
|
8141
|
+
function instructionsPropagatePastSwitch(instructions) {
|
|
8142
|
+
for (const instruction of instructions) {
|
|
8143
|
+
if (instruction.type === "return" /* Return */ || instruction.type === "throw" /* Throw */ || instruction.type === "continue" /* Continue */) {
|
|
8144
|
+
return true;
|
|
8145
|
+
}
|
|
8146
|
+
if (instruction.type === "break" /* Break */) return false;
|
|
8147
|
+
if (instruction.type === "try" /* Try */ && instructionsPropagatePastSwitch(instruction.instructions) && instruction.catches.every(
|
|
8148
|
+
(clause) => instructionsPropagatePastSwitch(clause.instructions)
|
|
8149
|
+
)) {
|
|
8150
|
+
return true;
|
|
8151
|
+
}
|
|
8152
|
+
if (instruction.type === "switch" /* Switch */) {
|
|
8153
|
+
if (instruction.defaultInstructions !== void 0 && instruction.defaultInstructions !== null && instruction.sections.every(
|
|
8154
|
+
(section) => instructionsPropagatePastSwitch(section.instructions)
|
|
8155
|
+
) && instructionsPropagatePastSwitch(instruction.defaultInstructions)) {
|
|
8156
|
+
return true;
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
if (instruction.type === "if" /* If */) {
|
|
8160
|
+
if (!instruction.else) continue;
|
|
8161
|
+
if (instruction.branches.every(
|
|
8162
|
+
(branch) => instructionsPropagatePastSwitch(branch.instructions)
|
|
8163
|
+
) && instructionsPropagatePastSwitch(instruction.else)) {
|
|
8164
|
+
return true;
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
}
|
|
8168
|
+
return false;
|
|
8169
|
+
}
|
|
7109
8170
|
function collectReturns(statements) {
|
|
7110
8171
|
const result = [];
|
|
7111
8172
|
for (const statement of statements) {
|
|
@@ -7116,6 +8177,18 @@ function collectReturns(statements) {
|
|
|
7116
8177
|
if (statement.elseBody)
|
|
7117
8178
|
result.push(...collectReturns(statement.elseBody));
|
|
7118
8179
|
}
|
|
8180
|
+
if (statement.kind === "for" || statement.kind === "forEach") {
|
|
8181
|
+
result.push(...collectReturns(statement.body));
|
|
8182
|
+
}
|
|
8183
|
+
if (statement.kind === "switch") {
|
|
8184
|
+
for (const section of statement.sections)
|
|
8185
|
+
result.push(...collectReturns(section.body));
|
|
8186
|
+
}
|
|
8187
|
+
if (statement.kind === "try") {
|
|
8188
|
+
result.push(...collectReturns(statement.body));
|
|
8189
|
+
for (const clause of statement.catches)
|
|
8190
|
+
result.push(...collectReturns(clause.body));
|
|
8191
|
+
}
|
|
7119
8192
|
}
|
|
7120
8193
|
return result;
|
|
7121
8194
|
}
|
|
@@ -7152,6 +8225,13 @@ function canonicalDecimal(raw, pos) {
|
|
|
7152
8225
|
const zero = whole === "0" && /^0*$/.test(fractionalRaw);
|
|
7153
8226
|
return `${negative && !zero ? "-" : ""}${whole}${fractionalRaw ? `.${fractionalRaw}` : ""}`;
|
|
7154
8227
|
}
|
|
8228
|
+
function isSwitchSelectorType(type, project) {
|
|
8229
|
+
return isPrimitive(type, "int") || isPrimitive(type, "string") || isPrimitive(type, "bool") || isNamedEnum(type, project);
|
|
8230
|
+
}
|
|
8231
|
+
function switchValueKey(value) {
|
|
8232
|
+
const enumId = value.typeInfo.type === 8 /* Enum */ ? value.typeInfo.enumId : null;
|
|
8233
|
+
return JSON.stringify([value.typeInfo.type, enumId, value.value]);
|
|
8234
|
+
}
|
|
7155
8235
|
var CONSTRUCTING_FUNCTION_KINDS, WRITE_TARGET_DESCRIPTIONS, FALLTHROUGH_LABELS, Scope, StrictNeoScriptResolver, SPRITE_DERIVED_MEMBERS;
|
|
7156
8236
|
var init_strict_resolver = __esm({
|
|
7157
8237
|
"../packages/neoscript-language/src/strict-resolver.ts"() {
|
|
@@ -7189,6 +8269,7 @@ var init_strict_resolver = __esm({
|
|
|
7189
8269
|
parent;
|
|
7190
8270
|
entries = /* @__PURE__ */ new Map();
|
|
7191
8271
|
invalidatedRoots = /* @__PURE__ */ new Set();
|
|
8272
|
+
hiddenNarrowingRoots = /* @__PURE__ */ new Set();
|
|
7192
8273
|
narrowedPaths = /* @__PURE__ */ new Map();
|
|
7193
8274
|
ownershipOverrides = /* @__PURE__ */ new Map();
|
|
7194
8275
|
define(entry) {
|
|
@@ -7197,10 +8278,19 @@ var init_strict_resolver = __esm({
|
|
|
7197
8278
|
lookup(name) {
|
|
7198
8279
|
return this.entries.get(name) ?? this.parent?.lookup(name) ?? null;
|
|
7199
8280
|
}
|
|
8281
|
+
containsEntry(entry) {
|
|
8282
|
+
for (const candidate of this.entries.values()) {
|
|
8283
|
+
if (candidate === entry) return true;
|
|
8284
|
+
}
|
|
8285
|
+
return this.parent?.containsEntry(entry) ?? false;
|
|
8286
|
+
}
|
|
7200
8287
|
/**
|
|
7201
8288
|
* P43 §7.1. The write-target root recorded for the binding a pointer names.
|
|
7202
8289
|
* Innermost scope wins, so a shadowing declaration governs its own block.
|
|
7203
8290
|
*/
|
|
8291
|
+
parentScope() {
|
|
8292
|
+
return this.parent;
|
|
8293
|
+
}
|
|
7204
8294
|
writeRootOf(variableId) {
|
|
7205
8295
|
for (const entry of this.entries.values()) {
|
|
7206
8296
|
if (entry.variableId === variableId) return entry.writeRoot;
|
|
@@ -7216,15 +8306,34 @@ var init_strict_resolver = __esm({
|
|
|
7216
8306
|
ownershipChanges() {
|
|
7217
8307
|
return this.ownershipOverrides;
|
|
7218
8308
|
}
|
|
8309
|
+
invalidatedRootEntries() {
|
|
8310
|
+
return this.invalidatedRoots;
|
|
8311
|
+
}
|
|
8312
|
+
effectiveNarrowedPaths() {
|
|
8313
|
+
const effective = this.parent ? new Map(this.parent.effectiveNarrowedPaths()) : /* @__PURE__ */ new Map();
|
|
8314
|
+
for (const invalidated of this.invalidatedRoots) {
|
|
8315
|
+
for (const [path, narrowed] of effective) {
|
|
8316
|
+
if (narrowed.rootEntry === invalidated) effective.delete(path);
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
for (const [path, narrowed] of this.narrowedPaths) {
|
|
8320
|
+
effective.set(path, narrowed);
|
|
8321
|
+
}
|
|
8322
|
+
return effective;
|
|
8323
|
+
}
|
|
7219
8324
|
lookupNarrowedType(path, rootEntry) {
|
|
7220
|
-
if (this.invalidatedRoots.has(rootEntry)) return null;
|
|
7221
8325
|
const local = this.narrowedPaths.get(path);
|
|
7222
8326
|
if (local?.rootEntry === rootEntry) return local.type;
|
|
8327
|
+
if (this.hiddenNarrowingRoots.has(rootEntry)) return null;
|
|
8328
|
+
if (this.invalidatedRoots.has(rootEntry)) return null;
|
|
7223
8329
|
return this.parent?.lookupNarrowedType(path, rootEntry) ?? null;
|
|
7224
8330
|
}
|
|
7225
8331
|
narrow(path, rootEntry, type) {
|
|
7226
8332
|
this.narrowedPaths.set(path, { type, rootEntry });
|
|
7227
8333
|
}
|
|
8334
|
+
hideInheritedNarrowing(rootEntry) {
|
|
8335
|
+
this.hiddenNarrowingRoots.add(rootEntry);
|
|
8336
|
+
}
|
|
7228
8337
|
invalidate(rootEntry) {
|
|
7229
8338
|
this.invalidatedRoots.add(rootEntry);
|
|
7230
8339
|
for (const [path, narrowed] of this.narrowedPaths) {
|
|
@@ -7280,7 +8389,15 @@ var init_strict_resolver = __esm({
|
|
|
7280
8389
|
argumentVariables;
|
|
7281
8390
|
callSiteCounter = 0;
|
|
7282
8391
|
lambdaDepth = 0;
|
|
8392
|
+
unreachableControlEffectDepth = 0;
|
|
8393
|
+
catchBindingCounter = 0;
|
|
8394
|
+
catchContexts = [];
|
|
7283
8395
|
expectedReturnStack = [];
|
|
8396
|
+
controlContexts = [];
|
|
8397
|
+
loopFlowContexts = [];
|
|
8398
|
+
switchFlowContexts = [];
|
|
8399
|
+
tryMutationContexts = [];
|
|
8400
|
+
functionControlBoundaries = [];
|
|
7284
8401
|
compile(body) {
|
|
7285
8402
|
const scope = new Scope(null);
|
|
7286
8403
|
scope.define(
|
|
@@ -7424,9 +8541,36 @@ var init_strict_resolver = __esm({
|
|
|
7424
8541
|
}
|
|
7425
8542
|
}
|
|
7426
8543
|
resolveStatements(statements, scope) {
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
8544
|
+
const instructions = [];
|
|
8545
|
+
let reachable = true;
|
|
8546
|
+
let unreachableScope = null;
|
|
8547
|
+
for (const statement of statements) {
|
|
8548
|
+
const statementScope = reachable ? scope : unreachableScope ??= new Scope(scope);
|
|
8549
|
+
const instruction = this.resolveSequencedStatement(
|
|
8550
|
+
statement,
|
|
8551
|
+
statementScope,
|
|
8552
|
+
reachable
|
|
8553
|
+
);
|
|
8554
|
+
instructions.push(instruction);
|
|
8555
|
+
if (reachable && instructionsTerminate([instruction])) reachable = false;
|
|
8556
|
+
if (reachable) {
|
|
8557
|
+
for (const context of this.tryMutationContexts) {
|
|
8558
|
+
context.effects.push(
|
|
8559
|
+
this.captureScopeEffects(statementScope, context.boundary)
|
|
8560
|
+
);
|
|
8561
|
+
}
|
|
8562
|
+
}
|
|
8563
|
+
}
|
|
8564
|
+
return instructions;
|
|
8565
|
+
}
|
|
8566
|
+
resolveSequencedStatement(statement, scope, reachable) {
|
|
8567
|
+
if (reachable) return this.resolveStatement(statement, scope);
|
|
8568
|
+
this.unreachableControlEffectDepth++;
|
|
8569
|
+
try {
|
|
8570
|
+
return this.resolveStatement(statement, scope);
|
|
8571
|
+
} finally {
|
|
8572
|
+
this.unreachableControlEffectDepth--;
|
|
8573
|
+
}
|
|
7430
8574
|
}
|
|
7431
8575
|
resolveStatement(statement, scope) {
|
|
7432
8576
|
switch (statement.kind) {
|
|
@@ -7437,6 +8581,7 @@ var init_strict_resolver = __esm({
|
|
|
7437
8581
|
statement.pos
|
|
7438
8582
|
);
|
|
7439
8583
|
}
|
|
8584
|
+
this.assertLocalNameAvailable(statement.name, scope, statement.pos);
|
|
7440
8585
|
const inferred = statement.type === null;
|
|
7441
8586
|
if (inferred && (statement.init.kind === "litList" || statement.init.kind === "litDict") && (statement.init.kind === "litList" ? statement.init.elements.length === 0 : statement.init.entries.length === 0)) {
|
|
7442
8587
|
throw new CompileError(
|
|
@@ -7519,6 +8664,7 @@ var init_strict_resolver = __esm({
|
|
|
7519
8664
|
branchExitNarrowings.push(new Map(noBranchScope.narrowedPaths));
|
|
7520
8665
|
branchExitOwnershipScopes.push(noBranchScope);
|
|
7521
8666
|
}
|
|
8667
|
+
mergeBranchInvalidations(scope, branchExitOwnershipScopes);
|
|
7522
8668
|
if (branchExitNarrowings.length > 0) {
|
|
7523
8669
|
for (const [path, narrowed] of mergeNarrowingMaps(
|
|
7524
8670
|
branchExitNarrowings
|
|
@@ -7536,6 +8682,332 @@ var init_strict_resolver = __esm({
|
|
|
7536
8682
|
else: otherwise
|
|
7537
8683
|
};
|
|
7538
8684
|
}
|
|
8685
|
+
case "try":
|
|
8686
|
+
return this.resolveTryStatement(statement, scope);
|
|
8687
|
+
case "switch": {
|
|
8688
|
+
const selector = this.resolveExpression(statement.selector, scope);
|
|
8689
|
+
if (!isSwitchSelectorType(selector.type, this.project)) {
|
|
8690
|
+
throw new CompileError(
|
|
8691
|
+
`switch selector must be int, string, bool, enum, or an optional form; got ${this.describe(selector.type)}`,
|
|
8692
|
+
statement.selector.pos
|
|
8693
|
+
);
|
|
8694
|
+
}
|
|
8695
|
+
const switchFlow = {
|
|
8696
|
+
boundary: scope,
|
|
8697
|
+
breakEffects: []
|
|
8698
|
+
};
|
|
8699
|
+
const sections = [];
|
|
8700
|
+
let defaultInstructions;
|
|
8701
|
+
const normalizedLabels = /* @__PURE__ */ new Set();
|
|
8702
|
+
this.controlContexts.push("switch");
|
|
8703
|
+
this.switchFlowContexts.push(switchFlow);
|
|
8704
|
+
try {
|
|
8705
|
+
for (const section of statement.sections) {
|
|
8706
|
+
const defaultLabels = section.labels.filter(
|
|
8707
|
+
(label) => label.kind === "default"
|
|
8708
|
+
);
|
|
8709
|
+
if (defaultLabels.length > 0 && section.labels.length !== 1) {
|
|
8710
|
+
throw new CompileError(
|
|
8711
|
+
"`default` must be a switch section of its own.",
|
|
8712
|
+
defaultLabels[0]?.pos ?? section.pos
|
|
8713
|
+
);
|
|
8714
|
+
}
|
|
8715
|
+
if (defaultLabels.length > 0 && defaultInstructions !== void 0) {
|
|
8716
|
+
throw new CompileError(
|
|
8717
|
+
"A switch statement can contain only one default section.",
|
|
8718
|
+
defaultLabels[0]?.pos ?? section.pos
|
|
8719
|
+
);
|
|
8720
|
+
}
|
|
8721
|
+
const labels = section.labels.flatMap((label) => {
|
|
8722
|
+
if (label.kind === "default") return [];
|
|
8723
|
+
const normalized = this.resolveSwitchCaseLabel(
|
|
8724
|
+
label.expression,
|
|
8725
|
+
selector.type,
|
|
8726
|
+
scope
|
|
8727
|
+
);
|
|
8728
|
+
const key = switchValueKey(normalized);
|
|
8729
|
+
if (normalizedLabels.has(key)) {
|
|
8730
|
+
throw new CompileError(
|
|
8731
|
+
"Duplicate switch case label.",
|
|
8732
|
+
label.pos
|
|
8733
|
+
);
|
|
8734
|
+
}
|
|
8735
|
+
normalizedLabels.add(key);
|
|
8736
|
+
return [normalized];
|
|
8737
|
+
});
|
|
8738
|
+
const sectionScope = new Scope(scope);
|
|
8739
|
+
const instructions = this.resolveStatements(
|
|
8740
|
+
section.body,
|
|
8741
|
+
sectionScope
|
|
8742
|
+
);
|
|
8743
|
+
if (!instructionsTerminate(instructions)) {
|
|
8744
|
+
throw new CompileError(
|
|
8745
|
+
"Switch section can reach its end; add break, return, or throw.",
|
|
8746
|
+
section.pos
|
|
8747
|
+
);
|
|
8748
|
+
}
|
|
8749
|
+
if (defaultLabels.length > 0) {
|
|
8750
|
+
defaultInstructions = instructions;
|
|
8751
|
+
} else {
|
|
8752
|
+
sections.push({ labels, instructions });
|
|
8753
|
+
}
|
|
8754
|
+
}
|
|
8755
|
+
} finally {
|
|
8756
|
+
this.switchFlowContexts.pop();
|
|
8757
|
+
this.controlContexts.pop();
|
|
8758
|
+
}
|
|
8759
|
+
const exitNarrowings = switchFlow.breakEffects.map(
|
|
8760
|
+
(effect) => effect.narrowedPaths
|
|
8761
|
+
);
|
|
8762
|
+
const exitEffects = [...switchFlow.breakEffects];
|
|
8763
|
+
if (defaultInstructions === void 0) {
|
|
8764
|
+
exitEffects.push({
|
|
8765
|
+
invalidatedRoots: /* @__PURE__ */ new Set(),
|
|
8766
|
+
ownershipOverrides: /* @__PURE__ */ new Map(),
|
|
8767
|
+
narrowedPaths: scope.effectiveNarrowedPaths()
|
|
8768
|
+
});
|
|
8769
|
+
exitNarrowings.push(scope.effectiveNarrowedPaths());
|
|
8770
|
+
}
|
|
8771
|
+
this.applyMergedScopeEffects(scope, exitEffects);
|
|
8772
|
+
this.mergeLoopNarrowings(scope, exitNarrowings);
|
|
8773
|
+
return {
|
|
8774
|
+
type: "switch" /* Switch */,
|
|
8775
|
+
selector: selector.pointer,
|
|
8776
|
+
selectorTypeInfo: toWireType(selector.type, this.project),
|
|
8777
|
+
sections,
|
|
8778
|
+
...defaultInstructions !== void 0 ? { defaultInstructions } : {}
|
|
8779
|
+
};
|
|
8780
|
+
}
|
|
8781
|
+
case "for": {
|
|
8782
|
+
const loopScope = new Scope(scope);
|
|
8783
|
+
const initializer = this.resolveStatement(
|
|
8784
|
+
statement.initializer,
|
|
8785
|
+
loopScope
|
|
8786
|
+
);
|
|
8787
|
+
if (initializer.type !== "variable" /* Variable */) {
|
|
8788
|
+
throw new Error(
|
|
8789
|
+
"NeoScript for-loop initializer did not resolve to a variable instruction."
|
|
8790
|
+
);
|
|
8791
|
+
}
|
|
8792
|
+
const loopEntryScope = new Scope(loopScope);
|
|
8793
|
+
invalidateLoopEntryNarrowingsAssignedBy(loopEntryScope, scope, [
|
|
8794
|
+
...statement.body,
|
|
8795
|
+
statement.iterator
|
|
8796
|
+
]);
|
|
8797
|
+
const condition = this.toBool(
|
|
8798
|
+
this.resolveExpression(statement.condition, loopEntryScope),
|
|
8799
|
+
statement.condition.pos
|
|
8800
|
+
);
|
|
8801
|
+
const iterationScope = new Scope(loopEntryScope);
|
|
8802
|
+
this.applyFactsToScope(loopEntryScope, iterationScope, [
|
|
8803
|
+
...factsWhenTrue(statement.condition)
|
|
8804
|
+
]);
|
|
8805
|
+
const bodyScope = new Scope(iterationScope);
|
|
8806
|
+
const loopFlow = {
|
|
8807
|
+
boundary: iterationScope,
|
|
8808
|
+
continueEffects: [],
|
|
8809
|
+
breakEffects: []
|
|
8810
|
+
};
|
|
8811
|
+
this.controlContexts.push("loop");
|
|
8812
|
+
this.loopFlowContexts.push(loopFlow);
|
|
8813
|
+
let instructions;
|
|
8814
|
+
try {
|
|
8815
|
+
instructions = this.resolveStatements(statement.body, bodyScope);
|
|
8816
|
+
} finally {
|
|
8817
|
+
this.loopFlowContexts.pop();
|
|
8818
|
+
this.controlContexts.pop();
|
|
8819
|
+
}
|
|
8820
|
+
const iteratorEffects = [...loopFlow.continueEffects];
|
|
8821
|
+
if (!instructionsTerminate(instructions)) {
|
|
8822
|
+
iteratorEffects.push(
|
|
8823
|
+
this.captureScopeEffects(bodyScope, iterationScope)
|
|
8824
|
+
);
|
|
8825
|
+
}
|
|
8826
|
+
this.applyMergedScopeEffects(iterationScope, iteratorEffects);
|
|
8827
|
+
const iteratorScope = new Scope(iterationScope);
|
|
8828
|
+
const iterator = this.resolveAssignment(
|
|
8829
|
+
statement.iterator,
|
|
8830
|
+
iteratorScope
|
|
8831
|
+
);
|
|
8832
|
+
const initialConditionFalseScope = new Scope(loopScope);
|
|
8833
|
+
this.applyFactsToScope(loopScope, initialConditionFalseScope, [
|
|
8834
|
+
...factsWhenFalse(statement.condition)
|
|
8835
|
+
]);
|
|
8836
|
+
const effectScopes = [loopScope];
|
|
8837
|
+
const normalExitScopes = [initialConditionFalseScope];
|
|
8838
|
+
if (iteratorEffects.length > 0) {
|
|
8839
|
+
const postIterationScope = new Scope(loopScope);
|
|
8840
|
+
this.applyMergedScopeEffects(postIterationScope, [
|
|
8841
|
+
this.captureScopeEffects(iteratorScope, loopScope)
|
|
8842
|
+
]);
|
|
8843
|
+
effectScopes.push(postIterationScope);
|
|
8844
|
+
const subsequentConditionFalseScope = new Scope(postIterationScope);
|
|
8845
|
+
this.applyFactsToScope(
|
|
8846
|
+
postIterationScope,
|
|
8847
|
+
subsequentConditionFalseScope,
|
|
8848
|
+
[...factsWhenFalse(statement.condition)]
|
|
8849
|
+
);
|
|
8850
|
+
normalExitScopes.push(subsequentConditionFalseScope);
|
|
8851
|
+
}
|
|
8852
|
+
this.mergeLoopEffects(scope, effectScopes, loopFlow.breakEffects);
|
|
8853
|
+
this.mergeLoopNarrowings(scope, [
|
|
8854
|
+
...normalExitScopes.map((exit) => exit.effectiveNarrowedPaths()),
|
|
8855
|
+
...loopFlow.breakEffects.map((exit) => exit.narrowedPaths)
|
|
8856
|
+
]);
|
|
8857
|
+
return {
|
|
8858
|
+
type: "for" /* For */,
|
|
8859
|
+
initializer: initializer.variable,
|
|
8860
|
+
condition: pointerToBooleanExpression(condition.pointer),
|
|
8861
|
+
iterator,
|
|
8862
|
+
instructions
|
|
8863
|
+
};
|
|
8864
|
+
}
|
|
8865
|
+
case "forEach": {
|
|
8866
|
+
const collection = this.resolveExpression(statement.collection, scope);
|
|
8867
|
+
if (isNullable(collection.type)) {
|
|
8868
|
+
throw new CompileError(
|
|
8869
|
+
`foreach requires a non-optional collection, got ${this.describe(collection.type)}`,
|
|
8870
|
+
statement.collection.pos
|
|
8871
|
+
);
|
|
8872
|
+
}
|
|
8873
|
+
const parts = collectionPartsFor(collection);
|
|
8874
|
+
if (!parts) {
|
|
8875
|
+
throw new CompileError(
|
|
8876
|
+
`foreach requires a List, Dictionary, Set, lookup, or derived collection, got ${this.describe(collection.type)}`,
|
|
8877
|
+
statement.collection.pos
|
|
8878
|
+
);
|
|
8879
|
+
}
|
|
8880
|
+
this.assertLocalNameAvailable(statement.name, scope, statement.pos);
|
|
8881
|
+
const inferredType = parts.valueType;
|
|
8882
|
+
const bindingType = statement.type ? this.resolveType(statement.type) : inferredType;
|
|
8883
|
+
if (statement.type) {
|
|
8884
|
+
this.requireAssignable(
|
|
8885
|
+
inferredType,
|
|
8886
|
+
bindingType,
|
|
8887
|
+
statement.pos,
|
|
8888
|
+
"foreach iterator"
|
|
8889
|
+
);
|
|
8890
|
+
}
|
|
8891
|
+
const binding = variable(
|
|
8892
|
+
statement.name,
|
|
8893
|
+
bindingType,
|
|
8894
|
+
void 0,
|
|
8895
|
+
this.project
|
|
8896
|
+
);
|
|
8897
|
+
const bindingWritability = collection.entryWritability ?? collection.writability ?? "runtime" /* Runtime */;
|
|
8898
|
+
const loopScope = new Scope(scope);
|
|
8899
|
+
loopScope.define(
|
|
8900
|
+
scopeVariable(
|
|
8901
|
+
statement.name,
|
|
8902
|
+
binding,
|
|
8903
|
+
bindingType,
|
|
8904
|
+
bindingWritability,
|
|
8905
|
+
bindingWritability,
|
|
8906
|
+
collection.writeRoot ?? this.writeThroughRoot(collection.pointer, scope),
|
|
8907
|
+
"foreach"
|
|
8908
|
+
)
|
|
8909
|
+
);
|
|
8910
|
+
const iterationScope = new Scope(loopScope);
|
|
8911
|
+
invalidateLoopEntryNarrowingsAssignedBy(
|
|
8912
|
+
iterationScope,
|
|
8913
|
+
scope,
|
|
8914
|
+
statement.body
|
|
8915
|
+
);
|
|
8916
|
+
const bodyScope = new Scope(iterationScope);
|
|
8917
|
+
const loopFlow = {
|
|
8918
|
+
boundary: iterationScope,
|
|
8919
|
+
continueEffects: [],
|
|
8920
|
+
breakEffects: []
|
|
8921
|
+
};
|
|
8922
|
+
this.controlContexts.push("loop");
|
|
8923
|
+
this.loopFlowContexts.push(loopFlow);
|
|
8924
|
+
let instructions;
|
|
8925
|
+
try {
|
|
8926
|
+
instructions = this.resolveStatements(statement.body, bodyScope);
|
|
8927
|
+
} finally {
|
|
8928
|
+
this.loopFlowContexts.pop();
|
|
8929
|
+
this.controlContexts.pop();
|
|
8930
|
+
}
|
|
8931
|
+
const nextItemEffects = [...loopFlow.continueEffects];
|
|
8932
|
+
if (!instructionsTerminate(instructions)) {
|
|
8933
|
+
nextItemEffects.push(
|
|
8934
|
+
this.captureScopeEffects(bodyScope, iterationScope)
|
|
8935
|
+
);
|
|
8936
|
+
}
|
|
8937
|
+
const nextItemScope = new Scope(loopScope);
|
|
8938
|
+
this.applyMergedScopeEffects(nextItemScope, nextItemEffects);
|
|
8939
|
+
this.mergeLoopEffects(
|
|
8940
|
+
scope,
|
|
8941
|
+
[loopScope, nextItemScope],
|
|
8942
|
+
[...loopFlow.breakEffects]
|
|
8943
|
+
);
|
|
8944
|
+
this.mergeLoopNarrowings(scope, [
|
|
8945
|
+
loopScope.effectiveNarrowedPaths(),
|
|
8946
|
+
nextItemScope.effectiveNarrowedPaths(),
|
|
8947
|
+
...loopFlow.breakEffects.map((exit) => exit.narrowedPaths)
|
|
8948
|
+
]);
|
|
8949
|
+
return {
|
|
8950
|
+
type: "forEach" /* ForEach */,
|
|
8951
|
+
binding: {
|
|
8952
|
+
id: binding.id,
|
|
8953
|
+
typeInfo: binding.typeInfo,
|
|
8954
|
+
readonly: true,
|
|
8955
|
+
writability: bindingWritability
|
|
8956
|
+
},
|
|
8957
|
+
collectionPointer: collection.pointer,
|
|
8958
|
+
collectionTypeInfo: collection.wireType ?? toWireType(collection.type, this.project),
|
|
8959
|
+
instructions
|
|
8960
|
+
};
|
|
8961
|
+
}
|
|
8962
|
+
case "break": {
|
|
8963
|
+
const functionBoundary = this.functionControlBoundaries.at(-1);
|
|
8964
|
+
const controlDepth = functionBoundary?.controlDepth ?? 0;
|
|
8965
|
+
if (this.controlContexts.length <= controlDepth) {
|
|
8966
|
+
throw new CompileError(
|
|
8967
|
+
"`break;` is only valid inside a loop or switch.",
|
|
8968
|
+
statement.pos
|
|
8969
|
+
);
|
|
8970
|
+
}
|
|
8971
|
+
const control = this.controlContexts.at(-1);
|
|
8972
|
+
if (control === "switch") {
|
|
8973
|
+
const switchFlow = this.switchFlowContexts.at(-1);
|
|
8974
|
+
if (!switchFlow) {
|
|
8975
|
+
throw new Error("NeoScript switch flow stack is unbalanced.");
|
|
8976
|
+
}
|
|
8977
|
+
if (this.unreachableControlEffectDepth === 0) {
|
|
8978
|
+
switchFlow.breakEffects.push(
|
|
8979
|
+
this.captureScopeEffects(scope, switchFlow.boundary)
|
|
8980
|
+
);
|
|
8981
|
+
}
|
|
8982
|
+
} else {
|
|
8983
|
+
const loopFlow = this.loopFlowContexts.at(-1);
|
|
8984
|
+
if (!loopFlow) {
|
|
8985
|
+
throw new Error("NeoScript loop flow stack is unbalanced.");
|
|
8986
|
+
}
|
|
8987
|
+
if (this.unreachableControlEffectDepth === 0) {
|
|
8988
|
+
loopFlow.breakEffects.push(
|
|
8989
|
+
this.captureScopeEffects(scope, loopFlow.boundary)
|
|
8990
|
+
);
|
|
8991
|
+
}
|
|
8992
|
+
}
|
|
8993
|
+
return { type: "break" /* Break */ };
|
|
8994
|
+
}
|
|
8995
|
+
case "continue": {
|
|
8996
|
+
const functionBoundary = this.functionControlBoundaries.at(-1);
|
|
8997
|
+
const loopFlow = this.loopFlowContexts.length > (functionBoundary?.loopFlowDepth ?? 0) ? this.loopFlowContexts.at(-1) : void 0;
|
|
8998
|
+
if (loopFlow === void 0) {
|
|
8999
|
+
throw new CompileError(
|
|
9000
|
+
"`continue;` is only valid inside a loop.",
|
|
9001
|
+
statement.pos
|
|
9002
|
+
);
|
|
9003
|
+
}
|
|
9004
|
+
if (this.unreachableControlEffectDepth === 0) {
|
|
9005
|
+
loopFlow.continueEffects.push(
|
|
9006
|
+
this.captureScopeEffects(scope, loopFlow.boundary)
|
|
9007
|
+
);
|
|
9008
|
+
}
|
|
9009
|
+
return { type: "continue" /* Continue */ };
|
|
9010
|
+
}
|
|
7539
9011
|
case "return": {
|
|
7540
9012
|
const expected = this.currentExpectedReturn();
|
|
7541
9013
|
if (statement.expr === null) {
|
|
@@ -7580,6 +9052,21 @@ var init_strict_resolver = __esm({
|
|
|
7580
9052
|
};
|
|
7581
9053
|
}
|
|
7582
9054
|
case "throw": {
|
|
9055
|
+
if (statement.expr === null) {
|
|
9056
|
+
const functionBoundary = this.functionControlBoundaries.at(-1);
|
|
9057
|
+
const catchDepth = functionBoundary?.catchDepth ?? 0;
|
|
9058
|
+
const rethrowPointer = this.catchContexts.length > catchDepth ? this.catchContexts.at(-1) : void 0;
|
|
9059
|
+
if (!rethrowPointer) {
|
|
9060
|
+
throw new CompileError(
|
|
9061
|
+
"`throw;` is only valid inside a catch body.",
|
|
9062
|
+
statement.pos
|
|
9063
|
+
);
|
|
9064
|
+
}
|
|
9065
|
+
return {
|
|
9066
|
+
type: "throw" /* Throw */,
|
|
9067
|
+
pointer: rethrowPointer
|
|
9068
|
+
};
|
|
9069
|
+
}
|
|
7583
9070
|
const message = this.resolveExpression(statement.expr, scope);
|
|
7584
9071
|
if (!isPrimitive(message.type, "string") || isNullable(message.type)) {
|
|
7585
9072
|
throw new CompileError(
|
|
@@ -7618,6 +9105,271 @@ var init_strict_resolver = __esm({
|
|
|
7618
9105
|
}
|
|
7619
9106
|
}
|
|
7620
9107
|
}
|
|
9108
|
+
resolveTryStatement(statement, scope) {
|
|
9109
|
+
const tryScope = new Scope(scope);
|
|
9110
|
+
const mutations = {
|
|
9111
|
+
boundary: scope,
|
|
9112
|
+
effects: []
|
|
9113
|
+
};
|
|
9114
|
+
this.tryMutationContexts.push(mutations);
|
|
9115
|
+
let instructions;
|
|
9116
|
+
try {
|
|
9117
|
+
instructions = this.resolveStatements(statement.body, tryScope);
|
|
9118
|
+
} finally {
|
|
9119
|
+
this.tryMutationContexts.pop();
|
|
9120
|
+
}
|
|
9121
|
+
const normalEffects = [];
|
|
9122
|
+
const normalNarrowings = [];
|
|
9123
|
+
if (!instructionsTerminate(instructions)) {
|
|
9124
|
+
const exitEffect = this.captureScopeEffects(tryScope, scope);
|
|
9125
|
+
normalEffects.push(exitEffect);
|
|
9126
|
+
normalNarrowings.push(exitEffect.narrowedPaths);
|
|
9127
|
+
}
|
|
9128
|
+
const baseline = {
|
|
9129
|
+
invalidatedRoots: /* @__PURE__ */ new Set(),
|
|
9130
|
+
ownershipOverrides: /* @__PURE__ */ new Map(),
|
|
9131
|
+
narrowedPaths: scope.effectiveNarrowedPaths()
|
|
9132
|
+
};
|
|
9133
|
+
const incomingCatchEffects = [baseline, ...mutations.effects];
|
|
9134
|
+
const catches = statement.catches.map((clause, index) => {
|
|
9135
|
+
const resolved = this.resolveCatchClause(
|
|
9136
|
+
clause,
|
|
9137
|
+
scope,
|
|
9138
|
+
incomingCatchEffects,
|
|
9139
|
+
index,
|
|
9140
|
+
statement.catches.length
|
|
9141
|
+
);
|
|
9142
|
+
if (resolved.exitEffect) {
|
|
9143
|
+
normalEffects.push(resolved.exitEffect);
|
|
9144
|
+
normalNarrowings.push(resolved.exitEffect.narrowedPaths);
|
|
9145
|
+
}
|
|
9146
|
+
return resolved.catch;
|
|
9147
|
+
});
|
|
9148
|
+
this.applyMergedScopeEffects(scope, normalEffects);
|
|
9149
|
+
this.mergeLoopNarrowings(scope, normalNarrowings);
|
|
9150
|
+
return {
|
|
9151
|
+
type: "try" /* Try */,
|
|
9152
|
+
instructions,
|
|
9153
|
+
catches
|
|
9154
|
+
};
|
|
9155
|
+
}
|
|
9156
|
+
resolveCatchClause(clause, outerScope, incomingEffects, index, catchCount) {
|
|
9157
|
+
if (clause.filter === null && index !== catchCount - 1) {
|
|
9158
|
+
throw new CompileError(
|
|
9159
|
+
"An unfiltered catch clause must be the final catch clause.",
|
|
9160
|
+
clause.pos
|
|
9161
|
+
);
|
|
9162
|
+
}
|
|
9163
|
+
const catchScope = new Scope(outerScope);
|
|
9164
|
+
this.applyMergedScopeEffects(catchScope, incomingEffects);
|
|
9165
|
+
this.assertLocalNameAvailable(clause.name, catchScope, clause.pos);
|
|
9166
|
+
const catchType = {
|
|
9167
|
+
kind: "primitive",
|
|
9168
|
+
name: "string"
|
|
9169
|
+
};
|
|
9170
|
+
const bindingId = `__catch:${this.catchBindingCounter++}:${clause.name}`;
|
|
9171
|
+
const binding = variable(bindingId, catchType, void 0, this.project);
|
|
9172
|
+
catchScope.define(
|
|
9173
|
+
scopeVariable(
|
|
9174
|
+
clause.name,
|
|
9175
|
+
binding,
|
|
9176
|
+
catchType,
|
|
9177
|
+
"local" /* Local */,
|
|
9178
|
+
"local" /* Local */,
|
|
9179
|
+
void 0,
|
|
9180
|
+
"catch"
|
|
9181
|
+
)
|
|
9182
|
+
);
|
|
9183
|
+
let filter;
|
|
9184
|
+
if (clause.filter) {
|
|
9185
|
+
const condition = this.toBool(
|
|
9186
|
+
this.resolveExpression(clause.filter, catchScope),
|
|
9187
|
+
clause.filter.pos
|
|
9188
|
+
);
|
|
9189
|
+
filter = pointerToBooleanExpression(condition.pointer);
|
|
9190
|
+
}
|
|
9191
|
+
const bodyScope = new Scope(catchScope);
|
|
9192
|
+
if (clause.filter) {
|
|
9193
|
+
this.applyFactsToScope(catchScope, bodyScope, [
|
|
9194
|
+
...factsWhenTrue(clause.filter)
|
|
9195
|
+
]);
|
|
9196
|
+
}
|
|
9197
|
+
const catchPointer = {
|
|
9198
|
+
type: "variable" /* Variable */,
|
|
9199
|
+
variableId: bindingId
|
|
9200
|
+
};
|
|
9201
|
+
this.catchContexts.push(catchPointer);
|
|
9202
|
+
let instructions;
|
|
9203
|
+
try {
|
|
9204
|
+
instructions = this.resolveStatements(clause.body, bodyScope);
|
|
9205
|
+
} finally {
|
|
9206
|
+
this.catchContexts.pop();
|
|
9207
|
+
}
|
|
9208
|
+
const exitEffect = instructionsTerminate(instructions) ? null : this.captureScopeEffects(bodyScope, outerScope);
|
|
9209
|
+
return {
|
|
9210
|
+
catch: {
|
|
9211
|
+
binding: {
|
|
9212
|
+
id: bindingId,
|
|
9213
|
+
typeInfo: binding.typeInfo,
|
|
9214
|
+
readonly: true
|
|
9215
|
+
},
|
|
9216
|
+
...filter ? { filter } : {},
|
|
9217
|
+
instructions
|
|
9218
|
+
},
|
|
9219
|
+
exitEffect
|
|
9220
|
+
};
|
|
9221
|
+
}
|
|
9222
|
+
assertLocalNameAvailable(name, scope, pos) {
|
|
9223
|
+
if (!scope.lookup(name)) return;
|
|
9224
|
+
throw new CompileError(
|
|
9225
|
+
`Cannot declare local '${name}' because that name is already used in an enclosing or current scope.`,
|
|
9226
|
+
pos
|
|
9227
|
+
);
|
|
9228
|
+
}
|
|
9229
|
+
captureScopeEffects(effect, boundary) {
|
|
9230
|
+
const invalidatedRoots = /* @__PURE__ */ new Set();
|
|
9231
|
+
const ownershipOverrides = /* @__PURE__ */ new Map();
|
|
9232
|
+
let current = effect;
|
|
9233
|
+
while (current !== null && current !== boundary) {
|
|
9234
|
+
for (const entry of current.invalidatedRootEntries()) {
|
|
9235
|
+
invalidatedRoots.add(entry);
|
|
9236
|
+
}
|
|
9237
|
+
for (const [entry, ownership] of current.ownershipChanges()) {
|
|
9238
|
+
if (!ownershipOverrides.has(entry)) {
|
|
9239
|
+
ownershipOverrides.set(entry, ownership);
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
9242
|
+
current = current.parentScope();
|
|
9243
|
+
}
|
|
9244
|
+
return {
|
|
9245
|
+
invalidatedRoots,
|
|
9246
|
+
ownershipOverrides,
|
|
9247
|
+
narrowedPaths: effect.effectiveNarrowedPaths()
|
|
9248
|
+
};
|
|
9249
|
+
}
|
|
9250
|
+
applyMergedScopeEffects(target, effects) {
|
|
9251
|
+
const invalidatedRoots = /* @__PURE__ */ new Set();
|
|
9252
|
+
const changedOwnership = /* @__PURE__ */ new Set();
|
|
9253
|
+
for (const effect of effects) {
|
|
9254
|
+
for (const entry of effect.invalidatedRoots) {
|
|
9255
|
+
if (target.containsEntry(entry)) invalidatedRoots.add(entry);
|
|
9256
|
+
}
|
|
9257
|
+
for (const entry of effect.ownershipOverrides.keys()) {
|
|
9258
|
+
if (target.containsEntry(entry)) changedOwnership.add(entry);
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
for (const entry of invalidatedRoots) target.invalidate(entry);
|
|
9262
|
+
for (const entry of changedOwnership) {
|
|
9263
|
+
const original = target.ownership(entry) ?? "runtime" /* Runtime */;
|
|
9264
|
+
const ownerships = effects.map(
|
|
9265
|
+
(effect) => effect.ownershipOverrides.get(entry) ?? original
|
|
9266
|
+
);
|
|
9267
|
+
const first = ownerships[0] ?? original;
|
|
9268
|
+
target.setOwnership(
|
|
9269
|
+
entry,
|
|
9270
|
+
ownerships.every((ownership) => ownership === first) ? first : "runtime" /* Runtime */
|
|
9271
|
+
);
|
|
9272
|
+
}
|
|
9273
|
+
}
|
|
9274
|
+
mergeLoopEffects(parent, effects, capturedEffects = []) {
|
|
9275
|
+
const changedOwnership = /* @__PURE__ */ new Set();
|
|
9276
|
+
const invalidated = /* @__PURE__ */ new Set();
|
|
9277
|
+
for (const effect of effects) {
|
|
9278
|
+
for (const entry of effect.ownershipChanges().keys()) {
|
|
9279
|
+
if (parent.containsEntry(entry)) changedOwnership.add(entry);
|
|
9280
|
+
}
|
|
9281
|
+
for (const entry of effect.invalidatedRootEntries()) {
|
|
9282
|
+
if (parent.containsEntry(entry)) invalidated.add(entry);
|
|
9283
|
+
}
|
|
9284
|
+
}
|
|
9285
|
+
for (const effect of capturedEffects) {
|
|
9286
|
+
for (const entry of effect.ownershipOverrides.keys()) {
|
|
9287
|
+
if (parent.containsEntry(entry)) changedOwnership.add(entry);
|
|
9288
|
+
}
|
|
9289
|
+
for (const entry of effect.invalidatedRoots) {
|
|
9290
|
+
if (parent.containsEntry(entry)) invalidated.add(entry);
|
|
9291
|
+
}
|
|
9292
|
+
}
|
|
9293
|
+
for (const entry of invalidated) parent.invalidate(entry);
|
|
9294
|
+
for (const entry of changedOwnership) {
|
|
9295
|
+
const entryOwnership = parent.ownership(entry) ?? "runtime" /* Runtime */;
|
|
9296
|
+
const ownerships = [
|
|
9297
|
+
entryOwnership,
|
|
9298
|
+
...effects.map((effect) => effect.ownership(entry) ?? entryOwnership),
|
|
9299
|
+
...capturedEffects.map(
|
|
9300
|
+
(effect) => effect.ownershipOverrides.get(entry) ?? entryOwnership
|
|
9301
|
+
)
|
|
9302
|
+
];
|
|
9303
|
+
const first = ownerships[0] ?? "runtime" /* Runtime */;
|
|
9304
|
+
parent.setOwnership(
|
|
9305
|
+
entry,
|
|
9306
|
+
ownerships.every((ownership) => ownership === first) ? first : "runtime" /* Runtime */
|
|
9307
|
+
);
|
|
9308
|
+
}
|
|
9309
|
+
}
|
|
9310
|
+
mergeLoopNarrowings(parent, exitPaths) {
|
|
9311
|
+
if (exitPaths.length === 0) return;
|
|
9312
|
+
const merged = mergeNarrowingMaps(exitPaths);
|
|
9313
|
+
for (const [path, narrowed] of parent.effectiveNarrowedPaths()) {
|
|
9314
|
+
const common = merged.get(path);
|
|
9315
|
+
if (parent.containsEntry(narrowed.rootEntry) && (common?.rootEntry !== narrowed.rootEntry || !typeRefsEqual(common.type, narrowed.type))) {
|
|
9316
|
+
parent.invalidate(narrowed.rootEntry);
|
|
9317
|
+
}
|
|
9318
|
+
}
|
|
9319
|
+
for (const [path, narrowed] of merged) {
|
|
9320
|
+
if (!parent.containsEntry(narrowed.rootEntry)) continue;
|
|
9321
|
+
const rootEntry = parent.lookup(pathRoot(path));
|
|
9322
|
+
if (rootEntry === narrowed.rootEntry) {
|
|
9323
|
+
parent.narrow(path, rootEntry, narrowed.type);
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
}
|
|
9327
|
+
resolveSwitchCaseLabel(expression, selectorType, scope) {
|
|
9328
|
+
if (expression.kind === "litNull") {
|
|
9329
|
+
if (!isNullable(selectorType)) {
|
|
9330
|
+
throw new CompileError(
|
|
9331
|
+
`Cannot use null as switch case of required type ${this.describe(selectorType)}`,
|
|
9332
|
+
expression.pos
|
|
9333
|
+
);
|
|
9334
|
+
}
|
|
9335
|
+
return {
|
|
9336
|
+
typeInfo: { type: 0 /* Null */, required: true },
|
|
9337
|
+
value: null
|
|
9338
|
+
};
|
|
9339
|
+
}
|
|
9340
|
+
const expected = { ...selectorType, nullable: false };
|
|
9341
|
+
const integerValue = expression.kind === "litInt" ? expression.value : expression.kind === "unary" && expression.op === "-" && expression.operand.kind === "litInt" ? -expression.operand.value : null;
|
|
9342
|
+
if (integerValue !== null && !Number.isSafeInteger(integerValue)) {
|
|
9343
|
+
throw new CompileError(
|
|
9344
|
+
"Switch integer case labels must be safe integers.",
|
|
9345
|
+
expression.pos
|
|
9346
|
+
);
|
|
9347
|
+
}
|
|
9348
|
+
let resolved;
|
|
9349
|
+
if (integerValue !== null) {
|
|
9350
|
+
resolved = literal({ kind: "primitive", name: "int" }, integerValue);
|
|
9351
|
+
} else if (expression.kind === "litString" || expression.kind === "litBool" || expression.kind === "contextualEnum" || expression.kind === "member" && expression.receiver.kind === "ident") {
|
|
9352
|
+
resolved = this.resolveExpression(expression, scope, expected);
|
|
9353
|
+
} else {
|
|
9354
|
+
throw new CompileError(
|
|
9355
|
+
"Switch case labels must be compile-time int, string, bool, enum, or null constants.",
|
|
9356
|
+
expression.pos
|
|
9357
|
+
);
|
|
9358
|
+
}
|
|
9359
|
+
this.requireAssignable(
|
|
9360
|
+
resolved.type,
|
|
9361
|
+
expected,
|
|
9362
|
+
expression.pos,
|
|
9363
|
+
"switch case"
|
|
9364
|
+
);
|
|
9365
|
+
if (resolved.pointer.type !== "value" /* Value */) {
|
|
9366
|
+
throw new CompileError(
|
|
9367
|
+
"Switch case labels must be compile-time int, string, bool, enum, or null constants.",
|
|
9368
|
+
expression.pos
|
|
9369
|
+
);
|
|
9370
|
+
}
|
|
9371
|
+
return resolved.pointer.value;
|
|
9372
|
+
}
|
|
7621
9373
|
resolveAssignment(statement, scope) {
|
|
7622
9374
|
if (!isAssignmentTarget(statement.target)) {
|
|
7623
9375
|
throw new CompileError(
|
|
@@ -7626,8 +9378,21 @@ var init_strict_resolver = __esm({
|
|
|
7626
9378
|
);
|
|
7627
9379
|
}
|
|
7628
9380
|
const local = statement.target.kind === "ident" ? scope.lookup(statement.target.name) : null;
|
|
9381
|
+
if (local?.readonlyBinding) {
|
|
9382
|
+
const label = local.readonlyBinding === "catch" ? "catch parameter" : "foreach iterator";
|
|
9383
|
+
throw new CompileError(
|
|
9384
|
+
`Cannot assign to ${label} '${local.name}' because it is read-only.`,
|
|
9385
|
+
statement.pos
|
|
9386
|
+
);
|
|
9387
|
+
}
|
|
7629
9388
|
const resolvedTarget = this.resolveExpression(statement.target, scope);
|
|
7630
|
-
const
|
|
9389
|
+
const storageType = this.assignmentStorageType(
|
|
9390
|
+
statement.target,
|
|
9391
|
+
resolvedTarget,
|
|
9392
|
+
local,
|
|
9393
|
+
scope
|
|
9394
|
+
);
|
|
9395
|
+
const target = statement.op === "=" ? { ...resolvedTarget, type: storageType } : resolvedTarget;
|
|
7631
9396
|
const writability = local?.writability ?? target.writability;
|
|
7632
9397
|
if (target.symbol?.computed === true && target.symbol.writable !== true) {
|
|
7633
9398
|
throw new CompileError(
|
|
@@ -7682,7 +9447,15 @@ var init_strict_resolver = __esm({
|
|
|
7682
9447
|
const assignedPath = canonicalPath(statement.target);
|
|
7683
9448
|
if (assignedPath) {
|
|
7684
9449
|
const rootEntry = scope.lookup(pathRoot(assignedPath));
|
|
7685
|
-
if (rootEntry)
|
|
9450
|
+
if (rootEntry) {
|
|
9451
|
+
scope.invalidate(rootEntry);
|
|
9452
|
+
if (statement.op === "=" && !isNullable(value.type)) {
|
|
9453
|
+
scope.narrow(assignedPath, rootEntry, {
|
|
9454
|
+
...target.type,
|
|
9455
|
+
nullable: false
|
|
9456
|
+
});
|
|
9457
|
+
}
|
|
9458
|
+
}
|
|
7686
9459
|
}
|
|
7687
9460
|
return {
|
|
7688
9461
|
type: "assign" /* Assign */,
|
|
@@ -7695,6 +9468,23 @@ var init_strict_resolver = __esm({
|
|
|
7695
9468
|
pointer: value.pointer
|
|
7696
9469
|
};
|
|
7697
9470
|
}
|
|
9471
|
+
/**
|
|
9472
|
+
* The declared type of an assignment target, with any flow narrowing on the
|
|
9473
|
+
* path stripped back off. Locals carry their declaration on the scope entry;
|
|
9474
|
+
* a member re-resolves through `resolveMemberNatural`, which is the same
|
|
9475
|
+
* lookup `resolveMember` performs before it overlays a narrowed type.
|
|
9476
|
+
*/
|
|
9477
|
+
assignmentStorageType(targetAst, resolvedTarget, local, scope) {
|
|
9478
|
+
if (targetAst.kind === "ident") return local?.type ?? resolvedTarget.type;
|
|
9479
|
+
if (targetAst.kind !== "member") return resolvedTarget.type;
|
|
9480
|
+
return this.resolveMemberNatural(
|
|
9481
|
+
targetAst.receiver,
|
|
9482
|
+
targetAst.name,
|
|
9483
|
+
scope,
|
|
9484
|
+
targetAst.pos,
|
|
9485
|
+
targetAst.optional === true
|
|
9486
|
+
).type;
|
|
9487
|
+
}
|
|
7698
9488
|
resolveCompoundAssignment(operation, valueAst, target, scope, pos) {
|
|
7699
9489
|
const binaryOperation = operation === "++" ? "+" : operation === "--" ? "-" : operation.slice(0, 1);
|
|
7700
9490
|
const right = operation === "++" || operation === "--" ? literal({ kind: "primitive", name: "int" }, 1) : valueAst ? this.resolveExpression(valueAst, scope, target.type) : null;
|
|
@@ -8120,7 +9910,8 @@ var init_strict_resolver = __esm({
|
|
|
8120
9910
|
variableId: entry.variableId
|
|
8121
9911
|
},
|
|
8122
9912
|
type: scope.lookupNarrowedType(name, entry) ?? entry.type,
|
|
8123
|
-
...ownership ? { writability: ownership } : {}
|
|
9913
|
+
...ownership ? { writability: ownership } : {},
|
|
9914
|
+
...entry.writeRoot ? { writeRoot: entry.writeRoot } : {}
|
|
8124
9915
|
};
|
|
8125
9916
|
}
|
|
8126
9917
|
const global = this.context.project.globals.find(
|
|
@@ -8325,6 +10116,12 @@ var init_strict_resolver = __esm({
|
|
|
8325
10116
|
pos
|
|
8326
10117
|
);
|
|
8327
10118
|
}
|
|
10119
|
+
if (!optional && isNullable(receiver.type)) {
|
|
10120
|
+
throw new CompileError(
|
|
10121
|
+
`Member '${name}' is read on ${this.describe(receiver.type)}, which may be null. Handle null before reading through it \u2014 use \`!\` if it is always set, or \`?.\`, \`??\`, or a null check that narrows it.`,
|
|
10122
|
+
pos
|
|
10123
|
+
);
|
|
10124
|
+
}
|
|
8328
10125
|
if (receiver.staticType?.kind === "enum") {
|
|
8329
10126
|
if (optional) {
|
|
8330
10127
|
throw new CompileError(
|
|
@@ -8485,6 +10282,7 @@ var init_strict_resolver = __esm({
|
|
|
8485
10282
|
};
|
|
8486
10283
|
}
|
|
8487
10284
|
const writability = this.memberWritability(member, receiver);
|
|
10285
|
+
const entryWritability = member.lookup?.multiselect === true ? this.lookupEntryWritability(member, receiver) : void 0;
|
|
8488
10286
|
const lookupWire = member.lookup?.multiselect === true && memberType2.kind === "set" ? {
|
|
8489
10287
|
type: 9 /* Lookup */,
|
|
8490
10288
|
required: !isNullable(memberType2),
|
|
@@ -8505,7 +10303,8 @@ var init_strict_resolver = __esm({
|
|
|
8505
10303
|
),
|
|
8506
10304
|
symbol: member,
|
|
8507
10305
|
...writability ? { writability } : {},
|
|
8508
|
-
...lookupWire ? { wireType: lookupWire } : {}
|
|
10306
|
+
...lookupWire ? { wireType: lookupWire } : {},
|
|
10307
|
+
...entryWritability ? { entryWritability } : {}
|
|
8509
10308
|
};
|
|
8510
10309
|
}
|
|
8511
10310
|
throw new CompileError(
|
|
@@ -8583,6 +10382,10 @@ var init_strict_resolver = __esm({
|
|
|
8583
10382
|
if (member.lookup.multiselect) {
|
|
8584
10383
|
return member.writability ? toWritability(member.writability) : receiver.writability ?? "save" /* Save */;
|
|
8585
10384
|
}
|
|
10385
|
+
return this.lookupEntryWritability(member, receiver);
|
|
10386
|
+
}
|
|
10387
|
+
lookupEntryWritability(member, receiver) {
|
|
10388
|
+
if (!member.lookup) return "readOnly" /* ReadOnly */;
|
|
8586
10389
|
const target = this.project.symbolById.get(
|
|
8587
10390
|
member.lookup.collectionMemberId
|
|
8588
10391
|
);
|
|
@@ -9380,11 +11183,18 @@ var init_strict_resolver = __esm({
|
|
|
9380
11183
|
pos
|
|
9381
11184
|
);
|
|
9382
11185
|
const filteredType = receiver.type.kind === "list" || receiver.type.kind === "dictionary" || receiver.type.kind === "set" ? { ...receiver.type, readOnly: true } : receiver.type;
|
|
9383
|
-
|
|
11186
|
+
const filtered = intrinsic(
|
|
9384
11187
|
"where" /* Where */,
|
|
9385
11188
|
{ collectionPointer: receiver.pointer, function: fn },
|
|
9386
11189
|
filteredType
|
|
9387
11190
|
);
|
|
11191
|
+
return {
|
|
11192
|
+
...filtered,
|
|
11193
|
+
...receiver.writability ? { writability: receiver.writability } : {},
|
|
11194
|
+
...receiver.wireType ? { wireType: receiver.wireType } : {},
|
|
11195
|
+
writeRoot: receiver.writeRoot ?? this.writeThroughRoot(receiver.pointer, scope),
|
|
11196
|
+
...receiver.entryWritability ? { entryWritability: receiver.entryWritability } : {}
|
|
11197
|
+
};
|
|
9388
11198
|
}
|
|
9389
11199
|
if (name === "First" || name === "FirstOrDefault" || name === "Find") {
|
|
9390
11200
|
if (args.length > 1) {
|
|
@@ -9401,7 +11211,7 @@ var init_strict_resolver = __esm({
|
|
|
9401
11211
|
pos
|
|
9402
11212
|
) : void 0;
|
|
9403
11213
|
const result = name === "FirstOrDefault" || name === "Find" ? { ...collection.valueType, nullable: true } : collection.valueType;
|
|
9404
|
-
|
|
11214
|
+
const first = intrinsic(
|
|
9405
11215
|
name === "First" ? "first" /* First */ : "firstOrDefault" /* FirstOrDefault */,
|
|
9406
11216
|
{
|
|
9407
11217
|
collectionPointer: receiver.pointer,
|
|
@@ -9409,6 +11219,12 @@ var init_strict_resolver = __esm({
|
|
|
9409
11219
|
},
|
|
9410
11220
|
result
|
|
9411
11221
|
);
|
|
11222
|
+
const entryWritability = receiver.entryWritability ?? receiver.writability ?? "runtime" /* Runtime */;
|
|
11223
|
+
return {
|
|
11224
|
+
...first,
|
|
11225
|
+
writability: entryWritability,
|
|
11226
|
+
writeRoot: this.expressionWriteRoot(receiver, scope)
|
|
11227
|
+
};
|
|
9412
11228
|
}
|
|
9413
11229
|
if (name === "Select") {
|
|
9414
11230
|
requireArgCount(name, args, 1, pos);
|
|
@@ -9416,7 +11232,13 @@ var init_strict_resolver = __esm({
|
|
|
9416
11232
|
if (lambda.kind !== "lambda") {
|
|
9417
11233
|
throw new CompileError("Select requires a lambda argument.", pos);
|
|
9418
11234
|
}
|
|
9419
|
-
const
|
|
11235
|
+
const inferredReturn = this.inferLambdaReturn(
|
|
11236
|
+
lambda,
|
|
11237
|
+
collection,
|
|
11238
|
+
receiver,
|
|
11239
|
+
scope
|
|
11240
|
+
);
|
|
11241
|
+
const resultType = inferredReturn.type;
|
|
9420
11242
|
const fn = this.resolveCollectionLambda(
|
|
9421
11243
|
lambda,
|
|
9422
11244
|
collection,
|
|
@@ -9424,11 +11246,16 @@ var init_strict_resolver = __esm({
|
|
|
9424
11246
|
scope,
|
|
9425
11247
|
pos
|
|
9426
11248
|
);
|
|
9427
|
-
|
|
11249
|
+
const selected2 = intrinsic(
|
|
9428
11250
|
"select" /* Select */,
|
|
9429
11251
|
{ collectionPointer: receiver.pointer, function: fn },
|
|
9430
11252
|
{ kind: "list", elementType: resultType, readOnly: true }
|
|
9431
11253
|
);
|
|
11254
|
+
return {
|
|
11255
|
+
...selected2,
|
|
11256
|
+
entryWritability: inferredReturn.writability,
|
|
11257
|
+
writeRoot: inferredReturn.writeRoot
|
|
11258
|
+
};
|
|
9432
11259
|
}
|
|
9433
11260
|
return null;
|
|
9434
11261
|
}
|
|
@@ -9594,6 +11421,7 @@ var init_strict_resolver = __esm({
|
|
|
9594
11421
|
const scope = new Scope(outerScope);
|
|
9595
11422
|
const parameterTypes = collection.kind === "dictionary" ? [collection.keyType, collection.valueType] : [collection.valueType];
|
|
9596
11423
|
const parameters = ast.params.map((parameter3, index) => {
|
|
11424
|
+
this.assertLocalNameAvailable(parameter3.name, scope, parameter3.pos);
|
|
9597
11425
|
const inferred = requiredAt(parameterTypes, index);
|
|
9598
11426
|
const declared = parameter3.type ? this.resolveType(parameter3.type) : inferred;
|
|
9599
11427
|
if (parameter3.type)
|
|
@@ -9615,6 +11443,11 @@ var init_strict_resolver = __esm({
|
|
|
9615
11443
|
);
|
|
9616
11444
|
return item;
|
|
9617
11445
|
});
|
|
11446
|
+
this.functionControlBoundaries.push({
|
|
11447
|
+
controlDepth: this.controlContexts.length,
|
|
11448
|
+
loopFlowDepth: this.loopFlowContexts.length,
|
|
11449
|
+
catchDepth: this.catchContexts.length
|
|
11450
|
+
});
|
|
9618
11451
|
this.lambdaDepth++;
|
|
9619
11452
|
this.expectedReturnStack.push(returnType);
|
|
9620
11453
|
try {
|
|
@@ -9634,31 +11467,42 @@ var init_strict_resolver = __esm({
|
|
|
9634
11467
|
} finally {
|
|
9635
11468
|
this.expectedReturnStack.pop();
|
|
9636
11469
|
this.lambdaDepth--;
|
|
11470
|
+
this.functionControlBoundaries.pop();
|
|
9637
11471
|
}
|
|
9638
11472
|
}
|
|
9639
|
-
|
|
11473
|
+
inferLambdaReturn(lambda, collection, source, outerScope) {
|
|
9640
11474
|
const returns = collectReturns(lambda.body);
|
|
9641
11475
|
if (returns.length === 0) {
|
|
9642
11476
|
throw new CompileError("Select lambda must return a value.", lambda.pos);
|
|
9643
11477
|
}
|
|
9644
11478
|
const scope = new Scope(outerScope);
|
|
9645
11479
|
const parameterTypes = collection.kind === "dictionary" ? [collection.keyType, collection.valueType] : [collection.valueType];
|
|
11480
|
+
const valueParameterIndex = collection.kind === "dictionary" ? 1 : 0;
|
|
11481
|
+
const sourceWritability = source.entryWritability ?? source.writability ?? "runtime" /* Runtime */;
|
|
11482
|
+
const sourceWriteRoot = this.expressionWriteRoot(source, outerScope);
|
|
9646
11483
|
lambda.params.forEach((parameter3, index) => {
|
|
11484
|
+
this.assertLocalNameAvailable(parameter3.name, scope, parameter3.pos);
|
|
9647
11485
|
const type = parameter3.type ? this.resolveType(parameter3.type) : requiredAt(parameterTypes, index);
|
|
11486
|
+
const isValueParameter = index === valueParameterIndex;
|
|
9648
11487
|
scope.define(
|
|
9649
11488
|
scopeVariable(
|
|
9650
11489
|
parameter3.name,
|
|
9651
11490
|
variable(parameter3.name, type, void 0, this.project),
|
|
9652
11491
|
type,
|
|
9653
|
-
|
|
9654
|
-
"runtime" /* Runtime
|
|
11492
|
+
isValueParameter ? sourceWritability : "runtime" /* Runtime */,
|
|
11493
|
+
isValueParameter ? sourceWritability : "runtime" /* Runtime */,
|
|
11494
|
+
isValueParameter ? sourceWriteRoot : "local"
|
|
9655
11495
|
)
|
|
9656
11496
|
);
|
|
9657
11497
|
});
|
|
9658
11498
|
let inferred = null;
|
|
11499
|
+
let inferredWritability = null;
|
|
11500
|
+
let inferredWriteRoot = null;
|
|
9659
11501
|
for (const returned of returns) {
|
|
9660
11502
|
if (!returned.expr) continue;
|
|
9661
11503
|
const current = this.resolveExpression(returned.expr, scope);
|
|
11504
|
+
const currentWritability = current.writability ?? "runtime" /* Runtime */;
|
|
11505
|
+
const currentWriteRoot = this.expressionWriteRoot(current, scope);
|
|
9662
11506
|
if (!inferred) inferred = current.type;
|
|
9663
11507
|
else {
|
|
9664
11508
|
const common = commonNeoScriptAssignableType(
|
|
@@ -9674,8 +11518,14 @@ var init_strict_resolver = __esm({
|
|
|
9674
11518
|
}
|
|
9675
11519
|
inferred = common;
|
|
9676
11520
|
}
|
|
11521
|
+
inferredWritability = inferredWritability === null ? currentWritability : inferredWritability === currentWritability ? inferredWritability : "runtime" /* Runtime */;
|
|
11522
|
+
inferredWriteRoot = inferredWriteRoot === null ? currentWriteRoot : inferredWriteRoot === currentWriteRoot ? inferredWriteRoot : "unknown";
|
|
9677
11523
|
}
|
|
9678
|
-
return
|
|
11524
|
+
return {
|
|
11525
|
+
type: inferred ?? { kind: "primitive", name: "null", nullable: true },
|
|
11526
|
+
writability: inferredWritability ?? "runtime" /* Runtime */,
|
|
11527
|
+
writeRoot: inferredWriteRoot ?? "unknown"
|
|
11528
|
+
};
|
|
9679
11529
|
}
|
|
9680
11530
|
resolveFunctionStatement(expression, scope) {
|
|
9681
11531
|
if (expression.kind !== "call" || expression.callee.kind !== "member")
|
|
@@ -10129,6 +11979,14 @@ var init_strict_resolver = __esm({
|
|
|
10129
11979
|
}
|
|
10130
11980
|
return this.writeTargetRoot(pointer, scope);
|
|
10131
11981
|
}
|
|
11982
|
+
expressionWriteRoot(expression, scope) {
|
|
11983
|
+
if (expression.writeRoot) return expression.writeRoot;
|
|
11984
|
+
if (expression.pointer.type === "value" /* Value */) return "local";
|
|
11985
|
+
if (expression.pointer.type === "function" /* Function */ && CONSTRUCTING_FUNCTION_KINDS.has(expression.pointer.function.type)) {
|
|
11986
|
+
return "local";
|
|
11987
|
+
}
|
|
11988
|
+
return this.writeThroughRoot(expression.pointer, scope);
|
|
11989
|
+
}
|
|
10132
11990
|
/**
|
|
10133
11991
|
* P43 §7.1. The write-target root a newly declared local inherits.
|
|
10134
11992
|
*
|
|
@@ -10478,10 +12336,17 @@ function formatNeoScript(text, options = {}) {
|
|
|
10478
12336
|
const tabSize = options.tabSize ?? 2;
|
|
10479
12337
|
const indentUnit = options.insertSpaces === false ? " " : " ".repeat(tabSize);
|
|
10480
12338
|
const lines = text.replace(/\r\n/g, "\n").split("\n");
|
|
12339
|
+
const normalizedText = lines.join("\n");
|
|
10481
12340
|
const syntax = analyzeNeoScriptSyntax(
|
|
10482
|
-
|
|
12341
|
+
normalizedText,
|
|
10483
12342
|
options.kind ?? inferDocumentKind(text)
|
|
10484
12343
|
);
|
|
12344
|
+
const splitCatchText = splitSameLineCatchClauses(
|
|
12345
|
+
normalizedText,
|
|
12346
|
+
syntax.lexed.tokens
|
|
12347
|
+
);
|
|
12348
|
+
if (splitCatchText !== normalizedText)
|
|
12349
|
+
return formatNeoScript(splitCatchText, options);
|
|
10485
12350
|
const delimiterTokensByLine = /* @__PURE__ */ new Map();
|
|
10486
12351
|
for (const token of syntax.lexed.tokens) {
|
|
10487
12352
|
if (token.kind !== "punctuation" || !isDelimiter(token.text)) continue;
|
|
@@ -10505,8 +12370,14 @@ function formatNeoScript(text, options = {}) {
|
|
|
10505
12370
|
lineTokens,
|
|
10506
12371
|
firstContentCharacter
|
|
10507
12372
|
);
|
|
10508
|
-
const
|
|
10509
|
-
|
|
12373
|
+
const contentOffset = syntax.lexed.source.offsetAt({
|
|
12374
|
+
line: lineIndex,
|
|
12375
|
+
character: firstContentCharacter
|
|
12376
|
+
});
|
|
12377
|
+
const sectionDepth = switchSectionIndentDepth(syntax.parsed, contentOffset);
|
|
12378
|
+
const lineDepth = Math.max(0, depth - leadingClosers) + sectionDepth;
|
|
12379
|
+
const normalized = normalizeControlHeaderSpacing(content);
|
|
12380
|
+
formatted.push(`${indentUnit.repeat(lineDepth)}${normalized}`);
|
|
10510
12381
|
for (const token of lineTokens) {
|
|
10511
12382
|
depth = isOpeningDelimiter(token.text) ? depth + 1 : Math.max(0, depth - 1);
|
|
10512
12383
|
}
|
|
@@ -10523,6 +12394,53 @@ function formattingEdit(text, options) {
|
|
|
10523
12394
|
const source = new SourceText(text);
|
|
10524
12395
|
return [{ range: source.fullRange(), newText: formatted }];
|
|
10525
12396
|
}
|
|
12397
|
+
function normalizeControlHeaderSpacing(content) {
|
|
12398
|
+
const withKeywordSpace = content.replace(
|
|
12399
|
+
/^(for|foreach|switch|catch)\s*\(/,
|
|
12400
|
+
"$1 ("
|
|
12401
|
+
);
|
|
12402
|
+
const withCatchFilterSpace = withKeywordSpace.replace(
|
|
12403
|
+
/\)\s*when\s*\(/,
|
|
12404
|
+
") when ("
|
|
12405
|
+
);
|
|
12406
|
+
if (!withCatchFilterSpace.startsWith("foreach ("))
|
|
12407
|
+
return withCatchFilterSpace;
|
|
12408
|
+
return withCatchFilterSpace.replace(/\s+in\s+/, " in ");
|
|
12409
|
+
}
|
|
12410
|
+
function splitSameLineCatchClauses(text, tokens) {
|
|
12411
|
+
const boundaries = [];
|
|
12412
|
+
for (let index = 1; index < tokens.length; index++) {
|
|
12413
|
+
const catchKeyword = tokens[index];
|
|
12414
|
+
const preceding = tokens[index - 1];
|
|
12415
|
+
if (catchKeyword?.kind !== "keyword" || catchKeyword.text !== "catch" || preceding?.kind !== "punctuation" || preceding.text !== "}" || preceding.range.end.line !== catchKeyword.range.start.line) {
|
|
12416
|
+
continue;
|
|
12417
|
+
}
|
|
12418
|
+
if (!/^\s*$/.test(text.slice(preceding.end, catchKeyword.start))) continue;
|
|
12419
|
+
boundaries.push({ start: preceding.end, end: catchKeyword.start });
|
|
12420
|
+
}
|
|
12421
|
+
if (boundaries.length === 0) return text;
|
|
12422
|
+
let result = "";
|
|
12423
|
+
let cursor = 0;
|
|
12424
|
+
for (const boundary of boundaries) {
|
|
12425
|
+
result += `${text.slice(cursor, boundary.start)}
|
|
12426
|
+
`;
|
|
12427
|
+
cursor = boundary.end;
|
|
12428
|
+
}
|
|
12429
|
+
return result + text.slice(cursor);
|
|
12430
|
+
}
|
|
12431
|
+
function switchSectionIndentDepth(parsed, contentOffset) {
|
|
12432
|
+
let depth = 0;
|
|
12433
|
+
for (const statement of parsed.switches) {
|
|
12434
|
+
for (const section of statement.sections) {
|
|
12435
|
+
const lastLabel = section.labels.at(-1);
|
|
12436
|
+
if (!lastLabel) continue;
|
|
12437
|
+
if (contentOffset > lastLabel.colonEnd && contentOffset < section.bodyEnd) {
|
|
12438
|
+
depth++;
|
|
12439
|
+
}
|
|
12440
|
+
}
|
|
12441
|
+
}
|
|
12442
|
+
return depth;
|
|
12443
|
+
}
|
|
10526
12444
|
function countLeadingClosers(lineTokens, firstContentCharacter) {
|
|
10527
12445
|
let count = 0;
|
|
10528
12446
|
let expectedCharacter = firstContentCharacter;
|
|
@@ -16028,7 +17946,8 @@ function parseProjectRootInitializer(source) {
|
|
|
16028
17946
|
name: name.text,
|
|
16029
17947
|
type: type.text,
|
|
16030
17948
|
storage: storage.text,
|
|
16031
|
-
initializer
|
|
17949
|
+
initializer,
|
|
17950
|
+
start: expressionStart.pos
|
|
16032
17951
|
});
|
|
16033
17952
|
}
|
|
16034
17953
|
take("}");
|
|
@@ -16398,11 +18317,55 @@ function collectIdentifierReads(expression, names) {
|
|
|
16398
18317
|
for (const inner of statement.elseBody ?? [])
|
|
16399
18318
|
walkStatement(inner, visible);
|
|
16400
18319
|
return;
|
|
18320
|
+
case "for": {
|
|
18321
|
+
walk(statement.initializer.init, visible);
|
|
18322
|
+
const inner = new Set(visible);
|
|
18323
|
+
inner.delete(statement.initializer.name);
|
|
18324
|
+
walk(statement.condition, inner);
|
|
18325
|
+
walkStatement(statement.iterator, inner);
|
|
18326
|
+
for (const bodyStatement of statement.body) {
|
|
18327
|
+
walkStatement(bodyStatement, inner);
|
|
18328
|
+
}
|
|
18329
|
+
return;
|
|
18330
|
+
}
|
|
18331
|
+
case "forEach": {
|
|
18332
|
+
walk(statement.collection, visible);
|
|
18333
|
+
const inner = new Set(visible);
|
|
18334
|
+
inner.delete(statement.name);
|
|
18335
|
+
for (const bodyStatement of statement.body) {
|
|
18336
|
+
walkStatement(bodyStatement, inner);
|
|
18337
|
+
}
|
|
18338
|
+
return;
|
|
18339
|
+
}
|
|
18340
|
+
case "switch":
|
|
18341
|
+
walk(statement.selector, visible);
|
|
18342
|
+
for (const section of statement.sections) {
|
|
18343
|
+
for (const label of section.labels) {
|
|
18344
|
+
if (label.kind === "case") walk(label.expression, visible);
|
|
18345
|
+
}
|
|
18346
|
+
for (const bodyStatement of section.body) {
|
|
18347
|
+
walkStatement(bodyStatement, visible);
|
|
18348
|
+
}
|
|
18349
|
+
}
|
|
18350
|
+
return;
|
|
18351
|
+
case "try":
|
|
18352
|
+
for (const bodyStatement of statement.body) {
|
|
18353
|
+
walkStatement(bodyStatement, visible);
|
|
18354
|
+
}
|
|
18355
|
+
for (const clause of statement.catches) {
|
|
18356
|
+
const catchScope = new Set(visible);
|
|
18357
|
+
catchScope.delete(clause.name);
|
|
18358
|
+
if (clause.filter) walk(clause.filter, catchScope);
|
|
18359
|
+
for (const bodyStatement of clause.body) {
|
|
18360
|
+
walkStatement(bodyStatement, catchScope);
|
|
18361
|
+
}
|
|
18362
|
+
}
|
|
18363
|
+
return;
|
|
16401
18364
|
case "return":
|
|
16402
18365
|
if (statement.expr) walk(statement.expr, visible);
|
|
16403
18366
|
return;
|
|
16404
18367
|
case "throw":
|
|
16405
|
-
walk(statement.expr, visible);
|
|
18368
|
+
if (statement.expr) walk(statement.expr, visible);
|
|
16406
18369
|
return;
|
|
16407
18370
|
case "assign":
|
|
16408
18371
|
walk(statement.target, visible);
|
|
@@ -16411,6 +18374,9 @@ function collectIdentifierReads(expression, names) {
|
|
|
16411
18374
|
case "exprStmt":
|
|
16412
18375
|
walk(statement.expr, visible);
|
|
16413
18376
|
return;
|
|
18377
|
+
case "break":
|
|
18378
|
+
case "continue":
|
|
18379
|
+
return;
|
|
16414
18380
|
}
|
|
16415
18381
|
};
|
|
16416
18382
|
walk(expression, names);
|
|
@@ -16470,10 +18436,20 @@ function validateProjectRootGlobal(uri, declaration, environment, diagnostics) {
|
|
|
16470
18436
|
scope,
|
|
16471
18437
|
environment,
|
|
16472
18438
|
declaration.initializer.range,
|
|
16473
|
-
diagnostics
|
|
18439
|
+
diagnostics,
|
|
18440
|
+
{
|
|
18441
|
+
text: actual.initializer,
|
|
18442
|
+
start: slotPosition(declaration.initializer.range.start, actual.start)
|
|
18443
|
+
}
|
|
16474
18444
|
);
|
|
16475
18445
|
}
|
|
16476
18446
|
}
|
|
18447
|
+
function slotPosition(start, pos) {
|
|
18448
|
+
if (pos.line === 1) {
|
|
18449
|
+
return { line: start.line, character: start.character + pos.column - 1 };
|
|
18450
|
+
}
|
|
18451
|
+
return { line: start.line + pos.line - 1, character: pos.column - 1 };
|
|
18452
|
+
}
|
|
16477
18453
|
function validateMember(uri, member, documentKind, ownerScope, environment, diagnostics) {
|
|
16478
18454
|
const memberType2 = semanticType(member.type);
|
|
16479
18455
|
validateAnnotations(
|
|
@@ -17098,7 +19074,7 @@ function validateStatements(statements, returnType, scope, environment, uri, ran
|
|
|
17098
19074
|
range2,
|
|
17099
19075
|
diagnostics
|
|
17100
19076
|
);
|
|
17101
|
-
} else if (statement.kind === "throw") {
|
|
19077
|
+
} else if (statement.kind === "throw" && statement.expr) {
|
|
17102
19078
|
validateExpression(
|
|
17103
19079
|
statement.expr,
|
|
17104
19080
|
primitiveType("string"),
|
|
@@ -17108,6 +19084,40 @@ function validateStatements(statements, returnType, scope, environment, uri, ran
|
|
|
17108
19084
|
range2,
|
|
17109
19085
|
diagnostics
|
|
17110
19086
|
);
|
|
19087
|
+
} else if (statement.kind === "try") {
|
|
19088
|
+
validateStatements(
|
|
19089
|
+
statement.body,
|
|
19090
|
+
returnType,
|
|
19091
|
+
new Map(scope),
|
|
19092
|
+
environment,
|
|
19093
|
+
uri,
|
|
19094
|
+
range2,
|
|
19095
|
+
diagnostics
|
|
19096
|
+
);
|
|
19097
|
+
for (const clause of statement.catches) {
|
|
19098
|
+
const catchScope = new Map(scope);
|
|
19099
|
+
catchScope.set(clause.name, primitiveType("string"));
|
|
19100
|
+
if (clause.filter) {
|
|
19101
|
+
validateExpression(
|
|
19102
|
+
clause.filter,
|
|
19103
|
+
primitiveType("bool"),
|
|
19104
|
+
catchScope,
|
|
19105
|
+
environment,
|
|
19106
|
+
uri,
|
|
19107
|
+
range2,
|
|
19108
|
+
diagnostics
|
|
19109
|
+
);
|
|
19110
|
+
}
|
|
19111
|
+
validateStatements(
|
|
19112
|
+
clause.body,
|
|
19113
|
+
returnType,
|
|
19114
|
+
catchScope,
|
|
19115
|
+
environment,
|
|
19116
|
+
uri,
|
|
19117
|
+
range2,
|
|
19118
|
+
diagnostics
|
|
19119
|
+
);
|
|
19120
|
+
}
|
|
17111
19121
|
} else if (statement.kind === "if") {
|
|
17112
19122
|
for (const branch of statement.branches) {
|
|
17113
19123
|
validateExpression(
|
|
@@ -17490,7 +19500,11 @@ function constructionSiteRange(expression, target, fallback, anchor) {
|
|
|
17490
19500
|
if (!entry) return fallback;
|
|
17491
19501
|
return anchorSpan(anchor, entry.pos, entry.name.length);
|
|
17492
19502
|
}
|
|
17493
|
-
return
|
|
19503
|
+
return anchorSpan(
|
|
19504
|
+
anchor,
|
|
19505
|
+
expression.pos,
|
|
19506
|
+
NEOSCRIPT_CONSTRUCTOR_KEYWORD.length
|
|
19507
|
+
);
|
|
17494
19508
|
}
|
|
17495
19509
|
function namedArgumentRange(anchor, valuePos, name) {
|
|
17496
19510
|
const prefix = anchor.text.slice(0, anchorOffset(anchor, valuePos));
|
|
@@ -18053,6 +20067,7 @@ var primitiveType, IDENTIFIER_PATTERN2, LIST_COLUMN_INHERITANCE_KEY;
|
|
|
18053
20067
|
var init_project_source_semantics = __esm({
|
|
18054
20068
|
"../packages/neoscript-language/src/project-source-semantics.ts"() {
|
|
18055
20069
|
"use strict";
|
|
20070
|
+
init_language_spec();
|
|
18056
20071
|
init_project_schema_contract_generated();
|
|
18057
20072
|
init_strict_compile_error();
|
|
18058
20073
|
init_strict_parser();
|
|
@@ -24705,7 +26720,9 @@ function memberType(member, environment, visiting, field) {
|
|
|
24705
26720
|
string2(member.collectionMemberId, `${field}.collectionMemberId`)
|
|
24706
26721
|
);
|
|
24707
26722
|
const collectionType = collection ? memberType(collection, environment, next, `${field}.collection`) : unknownType();
|
|
24708
|
-
const
|
|
26723
|
+
const declared = optionalRecord(member.declaredType);
|
|
26724
|
+
const declaredType = declared ? manifestType2(declared, environment, `${field}.declaredType`) : null;
|
|
26725
|
+
const entryType = declaredType && !isUnknownType2(declaredType) ? declaredType : collectionElementType2(collectionType) ?? unknownType();
|
|
24709
26726
|
return member.multiselect === true ? { kind: "set", elementType: entryType, nullable } : { ...entryType, nullable };
|
|
24710
26727
|
}
|
|
24711
26728
|
if (kind === "dialogueLookup") {
|
|
@@ -25033,6 +27050,9 @@ function primitive(name, nullable) {
|
|
|
25033
27050
|
function unknownType() {
|
|
25034
27051
|
return { kind: "primitive", name: "unknown" };
|
|
25035
27052
|
}
|
|
27053
|
+
function isUnknownType2(type) {
|
|
27054
|
+
return type.kind === "primitive" && type.name === "unknown";
|
|
27055
|
+
}
|
|
25036
27056
|
function indexById(values, field) {
|
|
25037
27057
|
const result = /* @__PURE__ */ new Map();
|
|
25038
27058
|
values.forEach((value, index) => {
|
|
@@ -33513,6 +35533,9 @@ function isNSTypeInfoCollectionInternal(value, ancestors) {
|
|
|
33513
35533
|
}
|
|
33514
35534
|
return true;
|
|
33515
35535
|
}
|
|
35536
|
+
function isNSTypeInfoLookup(value) {
|
|
35537
|
+
return isNSTypeInfoLookupInternal(value, /* @__PURE__ */ new Set());
|
|
35538
|
+
}
|
|
33516
35539
|
function isNSTypeInfoLookupInternal(value, ancestors) {
|
|
33517
35540
|
if (!isNSTypeInfoBase(value)) return false;
|
|
33518
35541
|
if (value.type !== 9 /* Lookup */) return false;
|
|
@@ -33781,6 +35804,20 @@ function isNSFunctionWithReturnType(value) {
|
|
|
33781
35804
|
if (!Array.isArray(v.parameters)) return false;
|
|
33782
35805
|
if (!v.parameters.every(isNSVariable)) return false;
|
|
33783
35806
|
if (!isNSInstructions(v.instructions)) return false;
|
|
35807
|
+
if ([
|
|
35808
|
+
"for" /* for */,
|
|
35809
|
+
"forEach" /* forEach */,
|
|
35810
|
+
"break" /* break */,
|
|
35811
|
+
"continue" /* continue */
|
|
35812
|
+
].some((type) => nsInstructionsContainType(v.instructions ?? [], type)) && (v.compilerRevision ?? 1) < 4) {
|
|
35813
|
+
return false;
|
|
35814
|
+
}
|
|
35815
|
+
if (nsInstructionsContainType(v.instructions, "switch" /* switch */) && (v.compilerRevision ?? 1) < 5) {
|
|
35816
|
+
return false;
|
|
35817
|
+
}
|
|
35818
|
+
if (nsInstructionsContainType(v.instructions, "try" /* try */) && (v.compilerRevision ?? 1) < 6) {
|
|
35819
|
+
return false;
|
|
35820
|
+
}
|
|
33784
35821
|
if (!isNSTypeInfo(v.typeInfo)) return false;
|
|
33785
35822
|
return true;
|
|
33786
35823
|
}
|
|
@@ -34038,8 +36075,159 @@ function isNSInstructionFunctionCall(value) {
|
|
|
34038
36075
|
if (v?.type !== "functionCall" /* functionCall */) return false;
|
|
34039
36076
|
return isNSPointerCallFunction(v.call);
|
|
34040
36077
|
}
|
|
36078
|
+
function isNSLoopBinding(value) {
|
|
36079
|
+
const binding = value;
|
|
36080
|
+
if (typeof binding !== "object" || binding === null) return false;
|
|
36081
|
+
if (typeof binding.id !== "string" || binding.id.length === 0) return false;
|
|
36082
|
+
if (!isNSTypeInfo(binding.typeInfo)) return false;
|
|
36083
|
+
if (binding.readonly !== true) return false;
|
|
36084
|
+
if (binding.writability === void 0) return true;
|
|
36085
|
+
return Object.values(NSWritability).includes(binding.writability);
|
|
36086
|
+
}
|
|
36087
|
+
function isNSInstructionFor(value) {
|
|
36088
|
+
const instruction = value;
|
|
36089
|
+
if (instruction?.type !== "for" /* for */) return false;
|
|
36090
|
+
if (!isNSVariable(instruction.initializer)) return false;
|
|
36091
|
+
if (!isNSBooleanExpression(instruction.condition)) return false;
|
|
36092
|
+
if (!isNSInstructionAssign(instruction.iterator)) return false;
|
|
36093
|
+
return isNSInstructions(instruction.instructions);
|
|
36094
|
+
}
|
|
36095
|
+
function isNSInstructionForEach(value) {
|
|
36096
|
+
const instruction = value;
|
|
36097
|
+
if (instruction?.type !== "forEach" /* forEach */) return false;
|
|
36098
|
+
if (!isNSLoopBinding(instruction.binding)) return false;
|
|
36099
|
+
if (!isNSPointer(instruction.collectionPointer)) return false;
|
|
36100
|
+
if (!isNSTypeInfoCollection(instruction.collectionTypeInfo) && !isNSTypeInfoLookup(instruction.collectionTypeInfo)) {
|
|
36101
|
+
return false;
|
|
36102
|
+
}
|
|
36103
|
+
if (instruction.collectionTypeInfo.required !== true) return false;
|
|
36104
|
+
return isNSInstructions(instruction.instructions);
|
|
36105
|
+
}
|
|
36106
|
+
function isNSInstructionBreak(value) {
|
|
36107
|
+
const instruction = value;
|
|
36108
|
+
return instruction?.type === "break" /* break */;
|
|
36109
|
+
}
|
|
36110
|
+
function isNSInstructionContinue(value) {
|
|
36111
|
+
const instruction = value;
|
|
36112
|
+
return instruction?.type === "continue" /* continue */;
|
|
36113
|
+
}
|
|
36114
|
+
function isNSSwitchSelectorType(value) {
|
|
36115
|
+
if (!isNSTypeInfo(value)) return false;
|
|
36116
|
+
return value.type === 2 /* Int */ || value.type === 3 /* String */ || value.type === 1 /* Bool */ || value.type === 8 /* Enum */;
|
|
36117
|
+
}
|
|
36118
|
+
function nsSwitchLabelKey(value, selectorTypeInfo) {
|
|
36119
|
+
if (!isNSValue(value)) return null;
|
|
36120
|
+
if (value.typeInfo.required !== true) return null;
|
|
36121
|
+
if (value.typeInfo.type === 0 /* Null */) {
|
|
36122
|
+
return value.value === null && selectorTypeInfo.required === false ? "null" : null;
|
|
36123
|
+
}
|
|
36124
|
+
if (value.typeInfo.type !== selectorTypeInfo.type) return null;
|
|
36125
|
+
switch (value.typeInfo.type) {
|
|
36126
|
+
case 2 /* Int */:
|
|
36127
|
+
return typeof value.value === "number" && Number.isSafeInteger(value.value) ? `int:${String(value.value)}` : null;
|
|
36128
|
+
case 3 /* String */:
|
|
36129
|
+
return typeof value.value === "string" ? `string:${JSON.stringify(value.value)}` : null;
|
|
36130
|
+
case 1 /* Bool */:
|
|
36131
|
+
return typeof value.value === "boolean" ? `bool:${String(value.value)}` : null;
|
|
36132
|
+
case 8 /* Enum */:
|
|
36133
|
+
if (selectorTypeInfo.type !== 8 /* Enum */ || value.typeInfo.enumId !== selectorTypeInfo.enumId || !Array.isArray(value.value) || value.value.length !== 1 || typeof value.value[0] !== "string" || value.value[0].length === 0) {
|
|
36134
|
+
return null;
|
|
36135
|
+
}
|
|
36136
|
+
return `enum:${value.typeInfo.enumId}:${JSON.stringify(value.value[0])}`;
|
|
36137
|
+
default:
|
|
36138
|
+
return null;
|
|
36139
|
+
}
|
|
36140
|
+
}
|
|
36141
|
+
function isNSSwitchSection(value, selectorTypeInfo, seenLabels) {
|
|
36142
|
+
const section = value;
|
|
36143
|
+
if (typeof section !== "object" || section === null) return false;
|
|
36144
|
+
if (!Array.isArray(section.labels) || section.labels.length === 0) {
|
|
36145
|
+
return false;
|
|
36146
|
+
}
|
|
36147
|
+
for (const label of section.labels) {
|
|
36148
|
+
const key = nsSwitchLabelKey(label, selectorTypeInfo);
|
|
36149
|
+
if (key === null || seenLabels.has(key)) return false;
|
|
36150
|
+
seenLabels.add(key);
|
|
36151
|
+
}
|
|
36152
|
+
return isNSInstructions(section.instructions);
|
|
36153
|
+
}
|
|
36154
|
+
function isNSInstructionSwitch(value) {
|
|
36155
|
+
const instruction = value;
|
|
36156
|
+
if (instruction?.type !== "switch" /* switch */) return false;
|
|
36157
|
+
if (!isNSPointer(instruction.selector)) return false;
|
|
36158
|
+
if (!isNSSwitchSelectorType(instruction.selectorTypeInfo)) return false;
|
|
36159
|
+
if (!Array.isArray(instruction.sections)) return false;
|
|
36160
|
+
const selectorTypeInfo = instruction.selectorTypeInfo;
|
|
36161
|
+
const seenLabels = /* @__PURE__ */ new Set();
|
|
36162
|
+
if (!instruction.sections.every(
|
|
36163
|
+
(section) => isNSSwitchSection(section, selectorTypeInfo, seenLabels)
|
|
36164
|
+
)) {
|
|
36165
|
+
return false;
|
|
36166
|
+
}
|
|
36167
|
+
return instruction.defaultInstructions === void 0 || instruction.defaultInstructions === null || isNSInstructions(instruction.defaultInstructions);
|
|
36168
|
+
}
|
|
36169
|
+
function isNSCatchClause(value, bindingIds, isFinal) {
|
|
36170
|
+
const clause = value;
|
|
36171
|
+
if (typeof clause !== "object" || clause === null) return false;
|
|
36172
|
+
const binding = clause.binding;
|
|
36173
|
+
if (typeof binding !== "object" || binding === null) return false;
|
|
36174
|
+
if (typeof binding.id !== "string" || binding.id.length === 0) return false;
|
|
36175
|
+
if (bindingIds.has(binding.id)) return false;
|
|
36176
|
+
bindingIds.add(binding.id);
|
|
36177
|
+
if (!isNSTypeInfo(binding.typeInfo)) return false;
|
|
36178
|
+
if (binding.typeInfo.type !== 3 /* String */) return false;
|
|
36179
|
+
if (binding.typeInfo.required !== true) return false;
|
|
36180
|
+
if (binding.readonly !== true) return false;
|
|
36181
|
+
const hasFilter = clause.filter !== void 0 && clause.filter !== null;
|
|
36182
|
+
if (!hasFilter && !isFinal) return false;
|
|
36183
|
+
if (hasFilter && !isNSBooleanExpression(clause.filter)) return false;
|
|
36184
|
+
return isNSInstructions(clause.instructions);
|
|
36185
|
+
}
|
|
36186
|
+
function isNSInstructionTry(value) {
|
|
36187
|
+
const instruction = value;
|
|
36188
|
+
if (instruction?.type !== "try" /* try */) return false;
|
|
36189
|
+
if (!isNSInstructions(instruction.instructions)) return false;
|
|
36190
|
+
if (!Array.isArray(instruction.catches)) return false;
|
|
36191
|
+
if (instruction.catches.length === 0) return false;
|
|
36192
|
+
const bindingIds = /* @__PURE__ */ new Set();
|
|
36193
|
+
for (let index = 0; index < instruction.catches.length; index += 1) {
|
|
36194
|
+
const clause = instruction.catches[index];
|
|
36195
|
+
if (!isNSCatchClause(
|
|
36196
|
+
clause,
|
|
36197
|
+
bindingIds,
|
|
36198
|
+
index === instruction.catches.length - 1
|
|
36199
|
+
)) {
|
|
36200
|
+
return false;
|
|
36201
|
+
}
|
|
36202
|
+
}
|
|
36203
|
+
return true;
|
|
36204
|
+
}
|
|
34041
36205
|
function isNSInstruction(value) {
|
|
34042
|
-
return isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value);
|
|
36206
|
+
return isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value) || isNSInstructionFor(value) || isNSInstructionForEach(value) || isNSInstructionBreak(value) || isNSInstructionContinue(value) || isNSInstructionSwitch(value) || isNSInstructionTry(value);
|
|
36207
|
+
}
|
|
36208
|
+
function nsInstructionsContainType(instructions, type) {
|
|
36209
|
+
return instructions.some((instruction) => {
|
|
36210
|
+
if (instruction.type === type) return true;
|
|
36211
|
+
if (instruction.type === "if" /* if */) {
|
|
36212
|
+
return instruction.branches.some(
|
|
36213
|
+
(branch) => nsInstructionsContainType(branch.instructions, type)
|
|
36214
|
+
) || instruction.else !== null && instruction.else !== void 0 && nsInstructionsContainType(instruction.else, type);
|
|
36215
|
+
}
|
|
36216
|
+
if (instruction.type === "for" /* for */ || instruction.type === "forEach" /* forEach */) {
|
|
36217
|
+
return nsInstructionsContainType(instruction.instructions, type);
|
|
36218
|
+
}
|
|
36219
|
+
if (instruction.type === "switch" /* switch */) {
|
|
36220
|
+
return instruction.sections.some(
|
|
36221
|
+
(section) => nsInstructionsContainType(section.instructions, type)
|
|
36222
|
+
) || instruction.defaultInstructions !== null && instruction.defaultInstructions !== void 0 && nsInstructionsContainType(instruction.defaultInstructions, type);
|
|
36223
|
+
}
|
|
36224
|
+
if (instruction.type === "try" /* try */) {
|
|
36225
|
+
return nsInstructionsContainType(instruction.instructions, type) || instruction.catches.some(
|
|
36226
|
+
(clause) => nsInstructionsContainType(clause.instructions, type)
|
|
36227
|
+
);
|
|
36228
|
+
}
|
|
36229
|
+
return false;
|
|
36230
|
+
});
|
|
34043
36231
|
}
|
|
34044
36232
|
function isNSInstructions(value) {
|
|
34045
36233
|
return Array.isArray(value) && value.every(isNSInstruction);
|
|
@@ -48711,6 +50899,7 @@ function drainValueReferenceObligationsV4(state, manifest, options) {
|
|
|
48711
50899
|
for (const recorded of options.registry.loweringFailures) {
|
|
48712
50900
|
failures.push({
|
|
48713
50901
|
message: recorded.message,
|
|
50902
|
+
...recorded.code === void 0 ? {} : { code: recorded.code },
|
|
48714
50903
|
...composeReferenceSitePosition(recorded.site, options.sourceTextByUri)
|
|
48715
50904
|
});
|
|
48716
50905
|
}
|
|
@@ -50146,59 +52335,96 @@ function resolveConstructorProjectionArguments(context, schemaClass2, expression
|
|
|
50146
52335
|
context.classes,
|
|
50147
52336
|
schemaClass2.id
|
|
50148
52337
|
);
|
|
52338
|
+
const signature = describeProjectedConstructor(schemaClass2.name, projections);
|
|
50149
52339
|
if (projections.length === 0) {
|
|
50150
52340
|
if (expression.args.length > 0) {
|
|
50151
|
-
|
|
50152
|
-
`Class ${schemaClass2.name}
|
|
50153
|
-
|
|
52341
|
+
context.loweringFailures.push({
|
|
52342
|
+
message: `Class '${schemaClass2.name}' projects no constructor parameters, so '${schemaClass2.name}()' takes no arguments \u2014 this call passes ${expression.args.length}.`,
|
|
52343
|
+
code: "unbound-constructor-argument",
|
|
52344
|
+
site: referenceSite(source, expression)
|
|
52345
|
+
});
|
|
50154
52346
|
}
|
|
50155
52347
|
return [];
|
|
50156
52348
|
}
|
|
50157
52349
|
const names = expression.argumentNames ?? [];
|
|
50158
|
-
if (expression.args.length !== projections.length
|
|
50159
|
-
|
|
50160
|
-
`
|
|
50161
|
-
|
|
52350
|
+
if (expression.args.length !== projections.length) {
|
|
52351
|
+
context.loweringFailures.push({
|
|
52352
|
+
message: `Constructor '${signature}' takes ${projections.length} argument${projections.length === 1 ? "" : "s"}, but this call passes ${expression.args.length} \u2014 parameters: ${describeQuotedNames(projections.map((entry) => entry.parameterName))}.`,
|
|
52353
|
+
code: "missing-constructor-argument",
|
|
52354
|
+
site: referenceSite(source, expression)
|
|
52355
|
+
});
|
|
52356
|
+
return [];
|
|
52357
|
+
}
|
|
52358
|
+
if (names.length !== projections.length) {
|
|
52359
|
+
context.loweringFailures.push({
|
|
52360
|
+
message: `Constructor '${signature}' takes named arguments only, so this call must name all ${projections.length} of them and names ${names.length} \u2014 parameters: ${describeQuotedNames(projections.map((entry) => entry.parameterName))}.`,
|
|
52361
|
+
code: "unbound-constructor-argument",
|
|
52362
|
+
site: referenceSite(source, expression)
|
|
52363
|
+
});
|
|
52364
|
+
return [];
|
|
50162
52365
|
}
|
|
50163
52366
|
const seen = /* @__PURE__ */ new Set();
|
|
50164
52367
|
const resolved = [];
|
|
50165
52368
|
for (let index = 0; index < expression.args.length; index++) {
|
|
52369
|
+
const argument2 = expression.args[index];
|
|
50166
52370
|
const parameterName = names[index];
|
|
50167
52371
|
if (parameterName === null || parameterName === void 0) {
|
|
50168
|
-
|
|
50169
|
-
`
|
|
50170
|
-
|
|
52372
|
+
context.loweringFailures.push({
|
|
52373
|
+
message: `Constructor '${signature}' takes named arguments only, so the argument at position ${index + 1} binds to no parameter \u2014 candidates: ${describeQuotedNames(projections.map((entry) => entry.parameterName))}.`,
|
|
52374
|
+
code: "unbound-constructor-argument",
|
|
52375
|
+
site: referenceSite(source, argument2)
|
|
52376
|
+
});
|
|
52377
|
+
continue;
|
|
50171
52378
|
}
|
|
50172
52379
|
if (seen.has(parameterName)) {
|
|
50173
|
-
|
|
50174
|
-
`
|
|
50175
|
-
|
|
52380
|
+
context.loweringFailures.push({
|
|
52381
|
+
message: `Constructor '${signature}' is passed '${parameterName}' more than once, and each parameter binds exactly one argument.`,
|
|
52382
|
+
code: "duplicate-constructor-argument",
|
|
52383
|
+
site: namedArgumentSite(source, argument2, parameterName)
|
|
52384
|
+
});
|
|
52385
|
+
continue;
|
|
50176
52386
|
}
|
|
50177
52387
|
seen.add(parameterName);
|
|
50178
52388
|
const projection = projections.find(
|
|
50179
52389
|
(candidate) => candidate.parameterName === parameterName
|
|
50180
52390
|
);
|
|
50181
52391
|
if (projection === void 0) {
|
|
50182
|
-
|
|
50183
|
-
`
|
|
50184
|
-
|
|
52392
|
+
context.loweringFailures.push({
|
|
52393
|
+
message: `Constructor '${signature}' has no parameter '${parameterName}' \u2014 candidates: ${describeQuotedNames(projections.map((entry) => entry.parameterName))}.`,
|
|
52394
|
+
code: "unknown-constructor-argument",
|
|
52395
|
+
site: namedArgumentSite(source, argument2, parameterName)
|
|
52396
|
+
});
|
|
52397
|
+
continue;
|
|
50185
52398
|
}
|
|
50186
|
-
const argument2 = expression.args[index];
|
|
50187
52399
|
if (argument2.kind !== "litString") {
|
|
50188
|
-
|
|
50189
|
-
`
|
|
50190
|
-
|
|
52400
|
+
context.loweringFailures.push({
|
|
52401
|
+
message: `Constructor '${signature}' parameter '${parameterName}' projects a row identity, so its argument must be a value-row id string literal.`,
|
|
52402
|
+
code: "non-literal-constructor-argument",
|
|
52403
|
+
site: referenceSite(source, argument2)
|
|
52404
|
+
});
|
|
52405
|
+
continue;
|
|
50191
52406
|
}
|
|
50192
52407
|
const schemaKey = inheritedProjectionSchemaKey(
|
|
50193
52408
|
context.classes,
|
|
50194
52409
|
schemaClass2.id,
|
|
50195
52410
|
projection.memberId
|
|
50196
52411
|
);
|
|
52412
|
+
if (schemaKey === null) {
|
|
52413
|
+
context.loweringFailures.push({
|
|
52414
|
+
message: `Constructor '${signature}' parameter '${parameterName}' projects member ${projection.memberId}, which no class in '${schemaClass2.name}'s inheritance chain declares under a schema key.`,
|
|
52415
|
+
code: "unprojectable-constructor-argument",
|
|
52416
|
+
site: referenceSite(source, argument2)
|
|
52417
|
+
});
|
|
52418
|
+
continue;
|
|
52419
|
+
}
|
|
50197
52420
|
const projectedMember = context.members.get(projection.memberId);
|
|
50198
|
-
if (
|
|
50199
|
-
|
|
50200
|
-
`
|
|
50201
|
-
|
|
52421
|
+
if (projectedMember?.kind !== "lookup") {
|
|
52422
|
+
context.loweringFailures.push({
|
|
52423
|
+
message: `Constructor '${signature}' parameter '${parameterName}' projects member ${projection.memberId}, which is ${projectedMember === void 0 ? "in no member of this project" : `a ${projectedMember.kind} member`} and not the Lookup field a projection writes.`,
|
|
52424
|
+
code: "unprojectable-constructor-argument",
|
|
52425
|
+
site: referenceSite(source, argument2)
|
|
52426
|
+
});
|
|
52427
|
+
continue;
|
|
50202
52428
|
}
|
|
50203
52429
|
context.referenceObligations.push({
|
|
50204
52430
|
kind: "constructorProjection",
|
|
@@ -50219,6 +52445,14 @@ function resolveConstructorProjectionArguments(context, schemaClass2, expression
|
|
|
50219
52445
|
}
|
|
50220
52446
|
return resolved;
|
|
50221
52447
|
}
|
|
52448
|
+
function describeProjectedConstructor(className, projections) {
|
|
52449
|
+
return `${className}(${projections.map((entry) => entry.parameterName).join(", ")})`;
|
|
52450
|
+
}
|
|
52451
|
+
function describeQuotedNames(names) {
|
|
52452
|
+
const quoted = names.map((name) => `'${name}'`);
|
|
52453
|
+
if (quoted.length <= 1) return quoted.join("") || "none";
|
|
52454
|
+
return `${quoted.slice(0, -1).join(", ")} and ${quoted.at(-1)}`;
|
|
52455
|
+
}
|
|
50222
52456
|
function validateAnimationProjectionBinding(context, schemaClass2, environment, targetValueId) {
|
|
50223
52457
|
if (schemaClass2.system?.worldKind !== "animationChildOverride") return;
|
|
50224
52458
|
const parameter3 = schemaClass2.genericParameters[0];
|
|
@@ -50653,16 +52887,42 @@ function lowerReferences(context, member, expression, ownerValueId, source) {
|
|
|
50653
52887
|
return ids;
|
|
50654
52888
|
}
|
|
50655
52889
|
function referenceSite(source, expression) {
|
|
52890
|
+
return referenceSiteAt(source, expressionAnchorPos(expression));
|
|
52891
|
+
}
|
|
52892
|
+
function referenceSiteAt(source, pos) {
|
|
50656
52893
|
return {
|
|
50657
52894
|
uri: source.source.uri,
|
|
50658
52895
|
declarationStart: source.source.range.start,
|
|
50659
|
-
expression: {
|
|
50660
|
-
initializer: source.initializer,
|
|
50661
|
-
pos: expressionAnchorPos(expression)
|
|
50662
|
-
},
|
|
52896
|
+
expression: { initializer: source.initializer, pos },
|
|
50663
52897
|
label: source.label
|
|
50664
52898
|
};
|
|
50665
52899
|
}
|
|
52900
|
+
function namedArgumentSite(source, argument2, name) {
|
|
52901
|
+
const value = expressionAnchorPos(argument2);
|
|
52902
|
+
const text = source.initializer;
|
|
52903
|
+
const prefix = text.slice(0, initializerOffsetAtPos(text, value));
|
|
52904
|
+
const colon = prefix.lastIndexOf(":");
|
|
52905
|
+
if (colon < 0) return referenceSiteAt(source, value);
|
|
52906
|
+
if (prefix.slice(colon + 1).trim().length > 0) {
|
|
52907
|
+
return referenceSiteAt(source, value);
|
|
52908
|
+
}
|
|
52909
|
+
const beforeColon = prefix.slice(0, colon).trimEnd();
|
|
52910
|
+
if (!beforeColon.endsWith(name)) return referenceSiteAt(source, value);
|
|
52911
|
+
const start = sourcePositionAtOffset(text, beforeColon.length - name.length);
|
|
52912
|
+
return referenceSiteAt(source, {
|
|
52913
|
+
line: start.line + 1,
|
|
52914
|
+
column: start.character + 1
|
|
52915
|
+
});
|
|
52916
|
+
}
|
|
52917
|
+
function initializerOffsetAtPos(text, pos) {
|
|
52918
|
+
let offset = 0;
|
|
52919
|
+
for (let line = 1; line < pos.line; line += 1) {
|
|
52920
|
+
const next = text.indexOf("\n", offset);
|
|
52921
|
+
if (next < 0) return text.length;
|
|
52922
|
+
offset = next + 1;
|
|
52923
|
+
}
|
|
52924
|
+
return Math.min(offset + pos.column - 1, text.length);
|
|
52925
|
+
}
|
|
50666
52926
|
function expressionAnchorPos(expression) {
|
|
50667
52927
|
if (expression.kind === "annotated") {
|
|
50668
52928
|
return expressionAnchorPos(expression.expression);
|
|
@@ -50874,7 +53134,14 @@ function validateReferenceContract(context, member, target, ownerValueId) {
|
|
|
50874
53134
|
const actualClassId = targetData && typeof targetData.classId === "string" ? targetData.classId : null;
|
|
50875
53135
|
if (expectedClassId && actualClassId && !classAssignableToClass(context, actualClassId, expectedClassId)) {
|
|
50876
53136
|
throw new Error(
|
|
50877
|
-
`Lookup member ${member.name} target ${target.id} is ${context
|
|
53137
|
+
`Lookup member ${member.name} target ${target.id} is ${classDisplayName(context, actualClassId)}, but collection ${collectionMemberName(context, member)} holds ${classDisplayName(context, expectedClassId)}.`
|
|
53138
|
+
);
|
|
53139
|
+
}
|
|
53140
|
+
const declaredClassId = lookupDeclaredClassId(context, member);
|
|
53141
|
+
if (declaredClassId && actualClassId && !classAssignableToClass(context, actualClassId, declaredClassId)) {
|
|
53142
|
+
const narrowedFrom = expectedClassId === null ? `the collection ${collectionMemberName(context, member)}` : `the collection ${collectionMemberName(context, member)} holds ${classDisplayName(context, expectedClassId)}`;
|
|
53143
|
+
throw new Error(
|
|
53144
|
+
`Lookup member ${member.name} target ${target.id} is ${classDisplayName(context, actualClassId)}, but the member declares ${classDisplayName(context, declaredClassId)} \u2014 ${narrowedFrom} and this member narrows it. Point it at a ${classDisplayName(context, declaredClassId)} row.`
|
|
50878
53145
|
);
|
|
50879
53146
|
}
|
|
50880
53147
|
if (target.genericTypeName && expectedTypeName && !referenceTypeNameAssignable(
|
|
@@ -50901,6 +53168,15 @@ function validateReferenceContract(context, member, target, ownerValueId) {
|
|
|
50901
53168
|
}
|
|
50902
53169
|
validateDialogueEligibility(context, member, target.id);
|
|
50903
53170
|
}
|
|
53171
|
+
function lookupDeclaredClassId(context, member) {
|
|
53172
|
+
const declared = member.declaredType;
|
|
53173
|
+
if (declared === null) return null;
|
|
53174
|
+
if (declared.kind !== "class") return null;
|
|
53175
|
+
return context.classes.has(declared.classId) ? declared.classId : null;
|
|
53176
|
+
}
|
|
53177
|
+
function classDisplayName(context, classId) {
|
|
53178
|
+
return context.classes.get(classId)?.name ?? classId;
|
|
53179
|
+
}
|
|
50904
53180
|
function lookupEntryTypeName(context, member) {
|
|
50905
53181
|
const collection = context.members.get(member.collectionMemberId);
|
|
50906
53182
|
const entry = collection?.kind === "list" || collection?.kind === "dictionary" ? context.members.get(collection.entryMemberId) : null;
|
|
@@ -56756,12 +59032,14 @@ function computeWorkspaceStatus(workspace, options = {}) {
|
|
|
56756
59032
|
}
|
|
56757
59033
|
);
|
|
56758
59034
|
for (const failure of referenceFailures) {
|
|
59035
|
+
const message = failure.code === void 0 ? failure.message : `${failure.code}: ${failure.message}`;
|
|
56759
59036
|
parseErrors.push(
|
|
56760
59037
|
new SchemaSourceError(
|
|
56761
|
-
|
|
59038
|
+
message,
|
|
56762
59039
|
failure.file,
|
|
56763
59040
|
failure.line,
|
|
56764
|
-
failure.column
|
|
59041
|
+
failure.column,
|
|
59042
|
+
failure.code
|
|
56765
59043
|
)
|
|
56766
59044
|
);
|
|
56767
59045
|
}
|
|
@@ -62513,6 +64791,20 @@ function memberToSymbol(record3, schemaKey, containingClass2, ownerClassId2, ind
|
|
|
62513
64791
|
writability: writable ? "setter" : "readOnly"
|
|
62514
64792
|
};
|
|
62515
64793
|
}
|
|
64794
|
+
if (isMemberNSPropertyContractBase(resolved)) {
|
|
64795
|
+
return {
|
|
64796
|
+
...common,
|
|
64797
|
+
kind: "property",
|
|
64798
|
+
type: toLanguageType(
|
|
64799
|
+
resolved.returnTypeInfo,
|
|
64800
|
+
context,
|
|
64801
|
+
genericEnvironment
|
|
64802
|
+
),
|
|
64803
|
+
writable: false,
|
|
64804
|
+
computed: true,
|
|
64805
|
+
writability: "readOnly"
|
|
64806
|
+
};
|
|
64807
|
+
}
|
|
62516
64808
|
if (isMemberLookupBase(resolved)) {
|
|
62517
64809
|
const collectionWritability = uniqueEffectiveWritability(
|
|
62518
64810
|
resolved.collectionMemberId,
|
|
@@ -62861,27 +65153,26 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
|
|
|
62861
65153
|
}
|
|
62862
65154
|
case 9 /* Lookup */: {
|
|
62863
65155
|
if (!isMemberLookupBase(member)) return UNKNOWN_TYPE2;
|
|
62864
|
-
const
|
|
62865
|
-
|
|
62866
|
-
|
|
62867
|
-
|
|
62868
|
-
if (!collection) return UNKNOWN_TYPE2;
|
|
62869
|
-
const collectionType = memberRuntimeType(
|
|
62870
|
-
collection,
|
|
65156
|
+
const declared = member.declaredTypeInfo ? toLanguageType(member.declaredTypeInfo, context, genericEnvironment) : null;
|
|
65157
|
+
const narrowed = declared && !isUnknownType3(declared) ? declared : null;
|
|
65158
|
+
const entry = narrowed ?? lookupCollectionEntryType(
|
|
65159
|
+
member.collectionMemberId,
|
|
62871
65160
|
context,
|
|
62872
65161
|
nextSeen,
|
|
62873
65162
|
genericEnvironment
|
|
62874
65163
|
);
|
|
62875
|
-
const entry = collectionEntryType(collectionType);
|
|
62876
65164
|
if (!entry) return UNKNOWN_TYPE2;
|
|
62877
65165
|
return member.multiselect ? {
|
|
62878
65166
|
kind: "set",
|
|
62879
65167
|
elementType: requiredType(entry),
|
|
62880
65168
|
nullable: !required2
|
|
62881
|
-
} : { ...entry, nullable:
|
|
65169
|
+
} : { ...entry, nullable: !required2 };
|
|
62882
65170
|
}
|
|
62883
65171
|
case 10 /* NSProperty */:
|
|
62884
|
-
|
|
65172
|
+
if (isMemberNSPropertyBase(member) || isMemberNSPropertyContractBase(member)) {
|
|
65173
|
+
return toLanguageType(member.returnTypeInfo, context);
|
|
65174
|
+
}
|
|
65175
|
+
return UNKNOWN_TYPE2;
|
|
62885
65176
|
case 13 /* Function */:
|
|
62886
65177
|
case 23 /* NSFunction */:
|
|
62887
65178
|
return primitive2("void", true);
|
|
@@ -63132,6 +65423,16 @@ function collectionEntryType(type) {
|
|
|
63132
65423
|
if (type.kind === "dictionary") return type.valueType;
|
|
63133
65424
|
return null;
|
|
63134
65425
|
}
|
|
65426
|
+
function lookupCollectionEntryType(collectionMemberId, context, seen, genericEnvironment) {
|
|
65427
|
+
const collection = analyzerMemberById(context.vm, collectionMemberId);
|
|
65428
|
+
if (!collection) return null;
|
|
65429
|
+
return collectionEntryType(
|
|
65430
|
+
memberRuntimeType(collection, context, new Set(seen), genericEnvironment)
|
|
65431
|
+
);
|
|
65432
|
+
}
|
|
65433
|
+
function isUnknownType3(type) {
|
|
65434
|
+
return type.kind === "primitive" && type.name === "unknown";
|
|
65435
|
+
}
|
|
63135
65436
|
function projectLanguageVersion(context) {
|
|
63136
65437
|
const records2 = [
|
|
63137
65438
|
...context.vm.classes,
|
|
@@ -63495,6 +65796,165 @@ var init_compiler_adapter = __esm({
|
|
|
63495
65796
|
}
|
|
63496
65797
|
});
|
|
63497
65798
|
|
|
65799
|
+
// src/commands/push-body-diagnostics.ts
|
|
65800
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
|
|
65801
|
+
import { join as join12 } from "node:path";
|
|
65802
|
+
function createNeoScriptBodySourceLocator(workspace, status) {
|
|
65803
|
+
const textByFile = /* @__PURE__ */ new Map();
|
|
65804
|
+
return {
|
|
65805
|
+
originOf(site) {
|
|
65806
|
+
if (site.recordKind === "script") return null;
|
|
65807
|
+
const record3 = status.reconstructed.get(
|
|
65808
|
+
recordStateKey(site.recordKind, site.recordId)
|
|
65809
|
+
);
|
|
65810
|
+
if (record3 === void 0) return null;
|
|
65811
|
+
const span2 = record3.sourceSpan;
|
|
65812
|
+
if (span2 === void 0) {
|
|
65813
|
+
return {
|
|
65814
|
+
file: record3.file,
|
|
65815
|
+
declarationStart: { line: record3.line - 1, character: 0 }
|
|
65816
|
+
};
|
|
65817
|
+
}
|
|
65818
|
+
return { file: span2.path, declarationStart: span2.start };
|
|
65819
|
+
},
|
|
65820
|
+
textOf(file) {
|
|
65821
|
+
const cached = textByFile.get(file);
|
|
65822
|
+
if (cached !== void 0) return cached;
|
|
65823
|
+
const path = join12(workspace.root, file);
|
|
65824
|
+
const text = existsSync11(path) ? readFileSync10(path, "utf8") : null;
|
|
65825
|
+
textByFile.set(file, text);
|
|
65826
|
+
return text;
|
|
65827
|
+
}
|
|
65828
|
+
};
|
|
65829
|
+
}
|
|
65830
|
+
function compileNeoScriptBodyOrThrow(compile, site, locator) {
|
|
65831
|
+
try {
|
|
65832
|
+
return compile();
|
|
65833
|
+
} catch (error) {
|
|
65834
|
+
if (!(error instanceof CompileError)) throw error;
|
|
65835
|
+
throw positionNeoScriptBodyCompileError(error, site, locator);
|
|
65836
|
+
}
|
|
65837
|
+
}
|
|
65838
|
+
function describeBodySite(site) {
|
|
65839
|
+
if (site.recordKind === "script") {
|
|
65840
|
+
return `${unitLabel(site.unit)} compiled from ${site.origin}`;
|
|
65841
|
+
}
|
|
65842
|
+
if (site.recordKind === "migration") {
|
|
65843
|
+
if (site.ownerName === null) return `Migration '${site.memberName}'`;
|
|
65844
|
+
return `Migration '${site.memberName}' (target ${site.ownerName})`;
|
|
65845
|
+
}
|
|
65846
|
+
const unit = unitLabel(site.unit);
|
|
65847
|
+
if (site.ownerName === null) {
|
|
65848
|
+
return `${unit} '${site.memberName}' (member ${site.recordId}, whose declaring class is not in this push's schema)`;
|
|
65849
|
+
}
|
|
65850
|
+
return `${unit} '${site.ownerName}.${site.memberName}'`;
|
|
65851
|
+
}
|
|
65852
|
+
function unitLabel(unit) {
|
|
65853
|
+
return `${unit.charAt(0).toUpperCase()}${unit.slice(1)}`;
|
|
65854
|
+
}
|
|
65855
|
+
function positionNeoScriptBodyCompileError(error, site, locator) {
|
|
65856
|
+
const subject = describeBodySite(site);
|
|
65857
|
+
const message = error.message.replace(`${error.line}:${error.column}: `, "");
|
|
65858
|
+
const inBody = `at ${error.line}:${error.column} in the compiled body`;
|
|
65859
|
+
if (site.recordKind === "script") {
|
|
65860
|
+
return new NeoScriptBodyCompileError(
|
|
65861
|
+
`${subject}: ${message} (${inBody}; this body was handed to the CLI directly, so no declaring file exists to map it to)`,
|
|
65862
|
+
error.line,
|
|
65863
|
+
error.column
|
|
65864
|
+
);
|
|
65865
|
+
}
|
|
65866
|
+
if (locator === null) {
|
|
65867
|
+
return new NeoScriptBodyCompileError(
|
|
65868
|
+
`${subject}: ${message} (${inBody}; no workspace source locator was supplied for this compile, so it could not be mapped to a file)`,
|
|
65869
|
+
error.line,
|
|
65870
|
+
error.column
|
|
65871
|
+
);
|
|
65872
|
+
}
|
|
65873
|
+
const origin = locator.originOf(site);
|
|
65874
|
+
if (origin === null) {
|
|
65875
|
+
return new NeoScriptBodyCompileError(
|
|
65876
|
+
`${subject}: ${message} (${inBody}; this body has no reconstructed source record in the workspace, so it could not be mapped to a file)`,
|
|
65877
|
+
error.line,
|
|
65878
|
+
error.column
|
|
65879
|
+
);
|
|
65880
|
+
}
|
|
65881
|
+
const text = locator.textOf(origin.file);
|
|
65882
|
+
if (text === null) {
|
|
65883
|
+
return new SchemaSourceError(
|
|
65884
|
+
`${subject}: ${message} (${inBody}; ${origin.file} could not be read, so the position is the declaration's)`,
|
|
65885
|
+
origin.file,
|
|
65886
|
+
origin.declarationStart.line + 1,
|
|
65887
|
+
origin.declarationStart.character + 1
|
|
65888
|
+
);
|
|
65889
|
+
}
|
|
65890
|
+
const composed = composeBodyPosition(text, origin, site.code, error);
|
|
65891
|
+
if (composed === null) {
|
|
65892
|
+
return new SchemaSourceError(
|
|
65893
|
+
`${subject}: ${message} (${inBody}; the authored body text was not found in ${origin.file}, so the position is the declaration's)`,
|
|
65894
|
+
origin.file,
|
|
65895
|
+
origin.declarationStart.line + 1,
|
|
65896
|
+
origin.declarationStart.character + 1
|
|
65897
|
+
);
|
|
65898
|
+
}
|
|
65899
|
+
return new SchemaSourceError(
|
|
65900
|
+
`${subject}: ${message}`,
|
|
65901
|
+
origin.file,
|
|
65902
|
+
composed.line,
|
|
65903
|
+
composed.column
|
|
65904
|
+
);
|
|
65905
|
+
}
|
|
65906
|
+
function composeBodyPosition(text, origin, code, error) {
|
|
65907
|
+
const declarationOffset = offsetAtPosition(text, origin.declarationStart);
|
|
65908
|
+
if (declarationOffset === null) return null;
|
|
65909
|
+
const codeOffset = text.indexOf(code, declarationOffset);
|
|
65910
|
+
if (codeOffset < 0) return null;
|
|
65911
|
+
const start = positionAtOffset(text, codeOffset);
|
|
65912
|
+
if (error.line === 1) {
|
|
65913
|
+
return { line: start.line + 1, column: start.character + error.column };
|
|
65914
|
+
}
|
|
65915
|
+
return { line: start.line + error.line, column: error.column };
|
|
65916
|
+
}
|
|
65917
|
+
function offsetAtPosition(text, position) {
|
|
65918
|
+
let offset = 0;
|
|
65919
|
+
for (let line = 0; line < position.line; line += 1) {
|
|
65920
|
+
const next = text.indexOf("\n", offset);
|
|
65921
|
+
if (next < 0) return null;
|
|
65922
|
+
offset = next + 1;
|
|
65923
|
+
}
|
|
65924
|
+
const candidate = offset + position.character;
|
|
65925
|
+
return candidate > text.length ? null : candidate;
|
|
65926
|
+
}
|
|
65927
|
+
function positionAtOffset(text, offset) {
|
|
65928
|
+
let line = 0;
|
|
65929
|
+
let lineStart = 0;
|
|
65930
|
+
for (let cursor = 0; cursor < offset; cursor += 1) {
|
|
65931
|
+
if (text[cursor] === "\n") {
|
|
65932
|
+
line += 1;
|
|
65933
|
+
lineStart = cursor + 1;
|
|
65934
|
+
}
|
|
65935
|
+
}
|
|
65936
|
+
return { line, character: offset - lineStart };
|
|
65937
|
+
}
|
|
65938
|
+
var NeoScriptBodyCompileError;
|
|
65939
|
+
var init_push_body_diagnostics = __esm({
|
|
65940
|
+
"src/commands/push-body-diagnostics.ts"() {
|
|
65941
|
+
"use strict";
|
|
65942
|
+
init_compile_error();
|
|
65943
|
+
init_source_diagnostics();
|
|
65944
|
+
init_workspace();
|
|
65945
|
+
NeoScriptBodyCompileError = class extends Error {
|
|
65946
|
+
constructor(message, line, column) {
|
|
65947
|
+
super(message);
|
|
65948
|
+
this.line = line;
|
|
65949
|
+
this.column = column;
|
|
65950
|
+
this.name = "NeoScriptBodyCompileError";
|
|
65951
|
+
}
|
|
65952
|
+
line;
|
|
65953
|
+
column;
|
|
65954
|
+
};
|
|
65955
|
+
}
|
|
65956
|
+
});
|
|
65957
|
+
|
|
63498
65958
|
// ../src/view-models/neoscript-evaluator/NSGetterRuntimeError.ts
|
|
63499
65959
|
var NSGetterRuntimeError;
|
|
63500
65960
|
var init_NSGetterRuntimeError = __esm({
|
|
@@ -63744,6 +66204,9 @@ var init_decimal = __esm({
|
|
|
63744
66204
|
});
|
|
63745
66205
|
|
|
63746
66206
|
// ../src/view-models/neoscript-evaluator/evaluateNSGetter.ts
|
|
66207
|
+
function isCatchableNSRuntimeError(error) {
|
|
66208
|
+
return error instanceof NSGetterRuntimeError && !(error instanceof NonCatchableNSGetterRuntimeError);
|
|
66209
|
+
}
|
|
63747
66210
|
function makeEvaluatorLookups(members, values) {
|
|
63748
66211
|
const attrMap = /* @__PURE__ */ new Map();
|
|
63749
66212
|
for (const a of members) attrMap.set(a.id, a);
|
|
@@ -63958,6 +66421,7 @@ function evaluateNSGetterWithEffects(getter, ctx, argumentValues = []) {
|
|
|
63958
66421
|
);
|
|
63959
66422
|
return createdSessionValues.length === 0 ? { value: result.value, writes } : { value: result.value, writes, createdSessionValues };
|
|
63960
66423
|
}
|
|
66424
|
+
rejectEscapedLoopTransfer(result, "NeoScript getter");
|
|
63961
66425
|
} catch (error) {
|
|
63962
66426
|
finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
|
|
63963
66427
|
throw error;
|
|
@@ -63983,6 +66447,7 @@ function evaluateNSAction(action, ctx) {
|
|
|
63983
66447
|
);
|
|
63984
66448
|
}
|
|
63985
66449
|
}
|
|
66450
|
+
rejectEscapedLoopTransfer(result, "NeoScript action");
|
|
63986
66451
|
finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
|
|
63987
66452
|
} catch (error) {
|
|
63988
66453
|
finalizeConstructorAllocations(runtimeCtx, void 0, ownsInvocationState);
|
|
@@ -64052,7 +66517,8 @@ function withEvaluationRuntime(ctx, writes) {
|
|
|
64052
66517
|
functionStack: [],
|
|
64053
66518
|
constructorGroups: /* @__PURE__ */ new Map(),
|
|
64054
66519
|
ownedValueAttachments: /* @__PURE__ */ new Map(),
|
|
64055
|
-
constructionStack: []
|
|
66520
|
+
constructionStack: [],
|
|
66521
|
+
loopIterations: 0
|
|
64056
66522
|
},
|
|
64057
66523
|
__indexes: ctx.__executionState === void 0 || ctx.__valueOverlay === void 0 ? void 0 : ctx.__indexes
|
|
64058
66524
|
};
|
|
@@ -64302,6 +66768,7 @@ function evaluateNSSetter(setter, ctx, value, writes) {
|
|
|
64302
66768
|
`Setter returned a non-null value: ${String(result.value)}`
|
|
64303
66769
|
);
|
|
64304
66770
|
}
|
|
66771
|
+
rejectEscapedLoopTransfer(result, "NeoScript setter");
|
|
64305
66772
|
}
|
|
64306
66773
|
function executeCompiledFunction(action, ctx, args, allowFallthroughReturn, runtimeSignature) {
|
|
64307
66774
|
if (action.parameters.length < 2) {
|
|
@@ -64345,6 +66812,7 @@ function executeCompiledFunction(action, ctx, args, allowFallthroughReturn, runt
|
|
|
64345
66812
|
"NSFunction ended without returning its declared value."
|
|
64346
66813
|
);
|
|
64347
66814
|
}
|
|
66815
|
+
rejectEscapedLoopTransfer(result, "NeoScript function");
|
|
64348
66816
|
let value = result.value;
|
|
64349
66817
|
const runtimeReturnType = runtimeSignature?.returnTypeInfo ?? action.typeInfo;
|
|
64350
66818
|
if (runtimeReturnType.type === NS_TYPE_VOID) {
|
|
@@ -64365,6 +66833,40 @@ function executeCompiledFunction(action, ctx, args, allowFallthroughReturn, runt
|
|
|
64365
66833
|
}
|
|
64366
66834
|
return value;
|
|
64367
66835
|
}
|
|
66836
|
+
function createChildScope(parent) {
|
|
66837
|
+
const child = new Map(parent);
|
|
66838
|
+
const inherited = readonlyBindingErrorsByScope.get(parent);
|
|
66839
|
+
if (inherited !== void 0 && inherited.size > 0) {
|
|
66840
|
+
readonlyBindingErrorsByScope.set(child, new Map(inherited));
|
|
66841
|
+
}
|
|
66842
|
+
return child;
|
|
66843
|
+
}
|
|
66844
|
+
function markReadonlyBinding(scope, bindingId, errorMessage3) {
|
|
66845
|
+
const existing = readonlyBindingErrorsByScope.get(scope);
|
|
66846
|
+
if (existing === void 0) {
|
|
66847
|
+
readonlyBindingErrorsByScope.set(
|
|
66848
|
+
scope,
|
|
66849
|
+
/* @__PURE__ */ new Map([[bindingId, errorMessage3]])
|
|
66850
|
+
);
|
|
66851
|
+
return;
|
|
66852
|
+
}
|
|
66853
|
+
readonlyBindingErrorsByScope.set(
|
|
66854
|
+
scope,
|
|
66855
|
+
new Map([...existing, [bindingId, errorMessage3]])
|
|
66856
|
+
);
|
|
66857
|
+
}
|
|
66858
|
+
function assertVariableBindingWritable(scope, bindingId) {
|
|
66859
|
+
const errorMessage3 = readonlyBindingErrorsByScope.get(scope)?.get(bindingId);
|
|
66860
|
+
if (errorMessage3 === void 0) return;
|
|
66861
|
+
throw new NSGetterRuntimeError(errorMessage3);
|
|
66862
|
+
}
|
|
66863
|
+
function rejectEscapedLoopTransfer(result, bodyLabel) {
|
|
66864
|
+
if (result.kind === "break" || result.kind === "continue") {
|
|
66865
|
+
throw new NSGetterRuntimeError(
|
|
66866
|
+
`Corrupt ${bodyLabel} IR: '${result.kind}' escaped the nearest loop.`
|
|
66867
|
+
);
|
|
66868
|
+
}
|
|
66869
|
+
}
|
|
64368
66870
|
function evaluationOptions(ctx, allowFallthroughReturn) {
|
|
64369
66871
|
const writes = ctx.__executionState?.writes;
|
|
64370
66872
|
if (writes === void 0) {
|
|
@@ -64387,6 +66889,186 @@ function createTopLevelScope(ctx) {
|
|
|
64387
66889
|
);
|
|
64388
66890
|
return scope;
|
|
64389
66891
|
}
|
|
66892
|
+
function consumeLoopIteration(ctx) {
|
|
66893
|
+
const state = ctx.__executionState;
|
|
66894
|
+
if (state === void 0) {
|
|
66895
|
+
throw new NSGetterRuntimeError(
|
|
66896
|
+
"NeoScript loop executed without a shared execution state."
|
|
66897
|
+
);
|
|
66898
|
+
}
|
|
66899
|
+
if (state.loopIterations >= MAX_LOOP_ITERATIONS) {
|
|
66900
|
+
throw new NSGetterRuntimeError(
|
|
66901
|
+
`NeoScript loop iteration limit of ${MAX_LOOP_ITERATIONS} exceeded.`
|
|
66902
|
+
);
|
|
66903
|
+
}
|
|
66904
|
+
state.loopIterations += 1;
|
|
66905
|
+
}
|
|
66906
|
+
function synchronizeExistingBindings(parent, child, bindingIds) {
|
|
66907
|
+
for (const bindingId of bindingIds) {
|
|
66908
|
+
parent.set(bindingId, child.get(bindingId));
|
|
66909
|
+
}
|
|
66910
|
+
}
|
|
66911
|
+
function evalInstructionsInChildScope(instructions, parent, ctx, options) {
|
|
66912
|
+
const parentBindingIds = [...parent.keys()];
|
|
66913
|
+
const child = createChildScope(parent);
|
|
66914
|
+
try {
|
|
66915
|
+
return evalInstructions(instructions, child, ctx, options);
|
|
66916
|
+
} finally {
|
|
66917
|
+
synchronizeExistingBindings(parent, child, parentBindingIds);
|
|
66918
|
+
}
|
|
66919
|
+
}
|
|
66920
|
+
function switchSelectorTypeSupported(typeInfo) {
|
|
66921
|
+
return typeInfo.type === 2 /* Int */ || typeInfo.type === 3 /* String */ || typeInfo.type === 1 /* Bool */ || typeInfo.type === 8 /* Enum */;
|
|
66922
|
+
}
|
|
66923
|
+
function switchLabelKeyOrThrow(label, selectorTypeInfo) {
|
|
66924
|
+
if (label.typeInfo.required !== true) {
|
|
66925
|
+
throw new CorruptNeoScriptIRError(
|
|
66926
|
+
"Corrupt NeoScript switch IR: case labels must use canonical required type information."
|
|
66927
|
+
);
|
|
66928
|
+
}
|
|
66929
|
+
if (label.typeInfo.type === 0 /* Null */) {
|
|
66930
|
+
if (label.value !== null || selectorTypeInfo.required !== false) {
|
|
66931
|
+
throw new CorruptNeoScriptIRError(
|
|
66932
|
+
"Corrupt NeoScript switch IR: a null case label requires an optional selector."
|
|
66933
|
+
);
|
|
66934
|
+
}
|
|
66935
|
+
return "null";
|
|
66936
|
+
}
|
|
66937
|
+
if (label.typeInfo.type !== selectorTypeInfo.type) {
|
|
66938
|
+
throw new CorruptNeoScriptIRError(
|
|
66939
|
+
"Corrupt NeoScript switch IR: case label type does not match the selector type."
|
|
66940
|
+
);
|
|
66941
|
+
}
|
|
66942
|
+
switch (label.typeInfo.type) {
|
|
66943
|
+
case 2 /* Int */:
|
|
66944
|
+
if (typeof label.value !== "number" || !Number.isSafeInteger(label.value)) {
|
|
66945
|
+
throw new CorruptNeoScriptIRError(
|
|
66946
|
+
"Corrupt NeoScript switch IR: int case label is not a safe integer."
|
|
66947
|
+
);
|
|
66948
|
+
}
|
|
66949
|
+
return `int:${String(label.value)}`;
|
|
66950
|
+
case 3 /* String */:
|
|
66951
|
+
if (typeof label.value !== "string") {
|
|
66952
|
+
throw new CorruptNeoScriptIRError(
|
|
66953
|
+
"Corrupt NeoScript switch IR: string case label has a non-string value."
|
|
66954
|
+
);
|
|
66955
|
+
}
|
|
66956
|
+
return `string:${JSON.stringify(label.value)}`;
|
|
66957
|
+
case 1 /* Bool */:
|
|
66958
|
+
if (typeof label.value !== "boolean") {
|
|
66959
|
+
throw new CorruptNeoScriptIRError(
|
|
66960
|
+
"Corrupt NeoScript switch IR: bool case label has a non-boolean value."
|
|
66961
|
+
);
|
|
66962
|
+
}
|
|
66963
|
+
return `bool:${String(label.value)}`;
|
|
66964
|
+
case 8 /* Enum */:
|
|
66965
|
+
if (selectorTypeInfo.type !== 8 /* Enum */ || label.typeInfo.enumId !== selectorTypeInfo.enumId || !Array.isArray(label.value) || label.value.length !== 1 || typeof label.value[0] !== "string" || label.value[0].length === 0) {
|
|
66966
|
+
throw new CorruptNeoScriptIRError(
|
|
66967
|
+
"Corrupt NeoScript switch IR: enum case label does not match the selector enum."
|
|
66968
|
+
);
|
|
66969
|
+
}
|
|
66970
|
+
return `enum:${label.typeInfo.enumId}:${JSON.stringify(label.value[0])}`;
|
|
66971
|
+
default:
|
|
66972
|
+
throw new CorruptNeoScriptIRError(
|
|
66973
|
+
"Corrupt NeoScript switch IR: unsupported case label type."
|
|
66974
|
+
);
|
|
66975
|
+
}
|
|
66976
|
+
}
|
|
66977
|
+
function validateSwitchInstruction(instruction) {
|
|
66978
|
+
if (!switchSelectorTypeSupported(instruction.selectorTypeInfo)) {
|
|
66979
|
+
throw new CorruptNeoScriptIRError(
|
|
66980
|
+
"Corrupt NeoScript switch IR: selector type must be int, string, bool, or enum."
|
|
66981
|
+
);
|
|
66982
|
+
}
|
|
66983
|
+
const labels = /* @__PURE__ */ new Set();
|
|
66984
|
+
for (const section of instruction.sections) {
|
|
66985
|
+
if (section.labels.length === 0) {
|
|
66986
|
+
throw new CorruptNeoScriptIRError(
|
|
66987
|
+
"Corrupt NeoScript switch IR: case section has no labels."
|
|
66988
|
+
);
|
|
66989
|
+
}
|
|
66990
|
+
for (const label of section.labels) {
|
|
66991
|
+
const key = switchLabelKeyOrThrow(label, instruction.selectorTypeInfo);
|
|
66992
|
+
if (labels.has(key)) {
|
|
66993
|
+
throw new CorruptNeoScriptIRError(
|
|
66994
|
+
"Corrupt NeoScript switch IR: duplicate case label."
|
|
66995
|
+
);
|
|
66996
|
+
}
|
|
66997
|
+
labels.add(key);
|
|
66998
|
+
}
|
|
66999
|
+
}
|
|
67000
|
+
}
|
|
67001
|
+
function assertSwitchSelectorValue(value, typeInfo, ctx) {
|
|
67002
|
+
const matchesType = runtimeValueMatchesType(value, typeInfo, ctx) && (typeInfo.type !== 2 /* Int */ || value === null || typeof value === "number" && Number.isSafeInteger(value));
|
|
67003
|
+
if (!matchesType) {
|
|
67004
|
+
throw new NSGetterRuntimeError(
|
|
67005
|
+
"NeoScript switch selector does not match its compiled type."
|
|
67006
|
+
);
|
|
67007
|
+
}
|
|
67008
|
+
}
|
|
67009
|
+
function validateTryInstruction(instruction) {
|
|
67010
|
+
if (!Array.isArray(instruction.catches)) {
|
|
67011
|
+
throw new CorruptNeoScriptIRError(
|
|
67012
|
+
"Corrupt NeoScript try IR: catch clause list is missing."
|
|
67013
|
+
);
|
|
67014
|
+
}
|
|
67015
|
+
if (instruction.catches.length === 0) {
|
|
67016
|
+
throw new CorruptNeoScriptIRError(
|
|
67017
|
+
"Corrupt NeoScript try IR: at least one catch clause is required."
|
|
67018
|
+
);
|
|
67019
|
+
}
|
|
67020
|
+
const bindingIds = /* @__PURE__ */ new Set();
|
|
67021
|
+
for (let index = 0; index < instruction.catches.length; index += 1) {
|
|
67022
|
+
const clause = instruction.catches[index];
|
|
67023
|
+
if (typeof clause?.binding !== "object" || clause.binding === null) {
|
|
67024
|
+
throw new CorruptNeoScriptIRError(
|
|
67025
|
+
"Corrupt NeoScript try IR: catch clause is missing its binding."
|
|
67026
|
+
);
|
|
67027
|
+
}
|
|
67028
|
+
if (typeof clause.binding.id !== "string") {
|
|
67029
|
+
throw new CorruptNeoScriptIRError(
|
|
67030
|
+
"Corrupt NeoScript try IR: catch binding id must be a string."
|
|
67031
|
+
);
|
|
67032
|
+
}
|
|
67033
|
+
if (clause.binding.id.length === 0) {
|
|
67034
|
+
throw new CorruptNeoScriptIRError(
|
|
67035
|
+
"Corrupt NeoScript try IR: catch binding id must be non-empty."
|
|
67036
|
+
);
|
|
67037
|
+
}
|
|
67038
|
+
if (bindingIds.has(clause.binding.id)) {
|
|
67039
|
+
throw new CorruptNeoScriptIRError(
|
|
67040
|
+
"Corrupt NeoScript try IR: catch binding ids must be unique."
|
|
67041
|
+
);
|
|
67042
|
+
}
|
|
67043
|
+
bindingIds.add(clause.binding.id);
|
|
67044
|
+
if (clause.binding.typeInfo?.type !== 3 /* String */) {
|
|
67045
|
+
throw new CorruptNeoScriptIRError(
|
|
67046
|
+
"Corrupt NeoScript try IR: catch binding type must be string."
|
|
67047
|
+
);
|
|
67048
|
+
}
|
|
67049
|
+
if (clause.binding.typeInfo.required !== true) {
|
|
67050
|
+
throw new CorruptNeoScriptIRError(
|
|
67051
|
+
"Corrupt NeoScript try IR: catch binding string must be required."
|
|
67052
|
+
);
|
|
67053
|
+
}
|
|
67054
|
+
if (clause.binding.readonly !== true) {
|
|
67055
|
+
throw new CorruptNeoScriptIRError(
|
|
67056
|
+
"Corrupt NeoScript try IR: catch binding must be read-only."
|
|
67057
|
+
);
|
|
67058
|
+
}
|
|
67059
|
+
const unfiltered = clause.filter === void 0 || clause.filter === null;
|
|
67060
|
+
if (unfiltered && index !== instruction.catches.length - 1) {
|
|
67061
|
+
throw new CorruptNeoScriptIRError(
|
|
67062
|
+
"Corrupt NeoScript try IR: an unfiltered catch must be the final clause."
|
|
67063
|
+
);
|
|
67064
|
+
}
|
|
67065
|
+
}
|
|
67066
|
+
if (!isNSInstructionTry(instruction)) {
|
|
67067
|
+
throw new CorruptNeoScriptIRError(
|
|
67068
|
+
"Corrupt NeoScript try IR: catch filter or child instructions are malformed."
|
|
67069
|
+
);
|
|
67070
|
+
}
|
|
67071
|
+
}
|
|
64390
67072
|
function evalInstructions(instructions, scope, ctx, options) {
|
|
64391
67073
|
for (const ins of instructions) {
|
|
64392
67074
|
switch (ins.type) {
|
|
@@ -64409,13 +67091,13 @@ function evalInstructions(instructions, scope, ctx, options) {
|
|
|
64409
67091
|
ctx,
|
|
64410
67092
|
options
|
|
64411
67093
|
);
|
|
64412
|
-
if (r.kind
|
|
67094
|
+
if (r.kind !== "fallthrough") return r;
|
|
64413
67095
|
break;
|
|
64414
67096
|
}
|
|
64415
67097
|
}
|
|
64416
67098
|
if (!matched && ins.else) {
|
|
64417
67099
|
const r = evalInstructions(ins.else, scope, ctx, options);
|
|
64418
|
-
if (r.kind
|
|
67100
|
+
if (r.kind !== "fallthrough") return r;
|
|
64419
67101
|
}
|
|
64420
67102
|
break;
|
|
64421
67103
|
}
|
|
@@ -64433,6 +67115,7 @@ function evalInstructions(instructions, scope, ctx, options) {
|
|
|
64433
67115
|
value = coerceDecimalOperand(value, "assignment");
|
|
64434
67116
|
}
|
|
64435
67117
|
if (ins.target.pointer.type === "variable" /* variable */) {
|
|
67118
|
+
assertVariableBindingWritable(scope, ins.target.pointer.variableId);
|
|
64436
67119
|
scope.set(ins.target.pointer.variableId, value);
|
|
64437
67120
|
break;
|
|
64438
67121
|
}
|
|
@@ -64473,6 +67156,174 @@ function evalInstructions(instructions, scope, ctx, options) {
|
|
|
64473
67156
|
evalPointer(ins.call, scope, ctx);
|
|
64474
67157
|
break;
|
|
64475
67158
|
}
|
|
67159
|
+
case "for" /* for */: {
|
|
67160
|
+
const parentBindingIds = [...scope.keys()];
|
|
67161
|
+
const loopScope = createChildScope(scope);
|
|
67162
|
+
try {
|
|
67163
|
+
const initialValue = coerceAssignmentValue(
|
|
67164
|
+
evalPointer(ins.initializer.pointer, loopScope, ctx),
|
|
67165
|
+
ins.initializer.typeInfo,
|
|
67166
|
+
"variable initialization"
|
|
67167
|
+
);
|
|
67168
|
+
loopScope.set(ins.initializer.id, initialValue);
|
|
67169
|
+
while (evalBooleanExpression(ins.condition, loopScope, ctx)) {
|
|
67170
|
+
consumeLoopIteration(ctx);
|
|
67171
|
+
const result = evalInstructionsInChildScope(
|
|
67172
|
+
ins.instructions,
|
|
67173
|
+
loopScope,
|
|
67174
|
+
ctx,
|
|
67175
|
+
options
|
|
67176
|
+
);
|
|
67177
|
+
if (result.kind === "return") return result;
|
|
67178
|
+
if (result.kind === "break") break;
|
|
67179
|
+
evalInstructions([ins.iterator], loopScope, ctx, options);
|
|
67180
|
+
}
|
|
67181
|
+
} finally {
|
|
67182
|
+
synchronizeExistingBindings(scope, loopScope, parentBindingIds);
|
|
67183
|
+
}
|
|
67184
|
+
break;
|
|
67185
|
+
}
|
|
67186
|
+
case "forEach" /* forEach */: {
|
|
67187
|
+
const collection = evalPointer(ins.collectionPointer, scope, ctx);
|
|
67188
|
+
const membership = snapshotCollectionMembership(collection);
|
|
67189
|
+
const parentBindingIds = [...scope.keys()];
|
|
67190
|
+
const loopScope = createChildScope(scope);
|
|
67191
|
+
markReadonlyBinding(
|
|
67192
|
+
loopScope,
|
|
67193
|
+
ins.binding.id,
|
|
67194
|
+
READONLY_FOREACH_BINDING_ERROR
|
|
67195
|
+
);
|
|
67196
|
+
try {
|
|
67197
|
+
for (const rawEntry of membership) {
|
|
67198
|
+
consumeLoopIteration(ctx);
|
|
67199
|
+
loopScope.set(
|
|
67200
|
+
ins.binding.id,
|
|
67201
|
+
coerceAssignmentValue(
|
|
67202
|
+
resolveValueIfId(rawEntry, ctx),
|
|
67203
|
+
ins.binding.typeInfo,
|
|
67204
|
+
"foreach binding"
|
|
67205
|
+
)
|
|
67206
|
+
);
|
|
67207
|
+
const result = evalInstructionsInChildScope(
|
|
67208
|
+
ins.instructions,
|
|
67209
|
+
loopScope,
|
|
67210
|
+
ctx,
|
|
67211
|
+
options
|
|
67212
|
+
);
|
|
67213
|
+
if (result.kind === "return") return result;
|
|
67214
|
+
if (result.kind === "break") break;
|
|
67215
|
+
}
|
|
67216
|
+
} finally {
|
|
67217
|
+
synchronizeExistingBindings(scope, loopScope, parentBindingIds);
|
|
67218
|
+
}
|
|
67219
|
+
break;
|
|
67220
|
+
}
|
|
67221
|
+
case "switch" /* switch */: {
|
|
67222
|
+
validateSwitchInstruction(ins);
|
|
67223
|
+
const selector = evalPointer(ins.selector, scope, ctx);
|
|
67224
|
+
assertSwitchSelectorValue(selector, ins.selectorTypeInfo, ctx);
|
|
67225
|
+
const matchedSection = ins.sections.find(
|
|
67226
|
+
(section) => section.labels.some((label) => jsEqual(selector, label.value))
|
|
67227
|
+
);
|
|
67228
|
+
const selectedInstructions = matchedSection?.instructions ?? ins.defaultInstructions;
|
|
67229
|
+
if (selectedInstructions === void 0 || selectedInstructions === null) {
|
|
67230
|
+
break;
|
|
67231
|
+
}
|
|
67232
|
+
const parentBindingIds = [...scope.keys()];
|
|
67233
|
+
const sectionScope = createChildScope(scope);
|
|
67234
|
+
const result = (() => {
|
|
67235
|
+
try {
|
|
67236
|
+
return evalInstructions(
|
|
67237
|
+
selectedInstructions,
|
|
67238
|
+
sectionScope,
|
|
67239
|
+
ctx,
|
|
67240
|
+
options
|
|
67241
|
+
);
|
|
67242
|
+
} finally {
|
|
67243
|
+
synchronizeExistingBindings(scope, sectionScope, parentBindingIds);
|
|
67244
|
+
}
|
|
67245
|
+
})();
|
|
67246
|
+
if (result.kind === "break") break;
|
|
67247
|
+
if (result.kind === "fallthrough") {
|
|
67248
|
+
throw new CorruptNeoScriptIRError(
|
|
67249
|
+
"Corrupt NeoScript switch IR: selected section reached its end."
|
|
67250
|
+
);
|
|
67251
|
+
}
|
|
67252
|
+
return result;
|
|
67253
|
+
}
|
|
67254
|
+
case "try" /* try */: {
|
|
67255
|
+
validateTryInstruction(ins);
|
|
67256
|
+
const parentBindingIds = [...scope.keys()];
|
|
67257
|
+
const tryScope = createChildScope(scope);
|
|
67258
|
+
let tryResult;
|
|
67259
|
+
try {
|
|
67260
|
+
try {
|
|
67261
|
+
tryResult = evalInstructions(
|
|
67262
|
+
ins.instructions,
|
|
67263
|
+
tryScope,
|
|
67264
|
+
ctx,
|
|
67265
|
+
options
|
|
67266
|
+
);
|
|
67267
|
+
} finally {
|
|
67268
|
+
synchronizeExistingBindings(scope, tryScope, parentBindingIds);
|
|
67269
|
+
}
|
|
67270
|
+
} catch (error) {
|
|
67271
|
+
if (!isCatchableNSRuntimeError(error)) throw error;
|
|
67272
|
+
let matched = false;
|
|
67273
|
+
for (const clause of ins.catches) {
|
|
67274
|
+
const catchScope = createChildScope(scope);
|
|
67275
|
+
catchScope.set(clause.binding.id, error.message);
|
|
67276
|
+
markReadonlyBinding(
|
|
67277
|
+
catchScope,
|
|
67278
|
+
clause.binding.id,
|
|
67279
|
+
READONLY_CATCH_BINDING_ERROR
|
|
67280
|
+
);
|
|
67281
|
+
const filter = clause.filter;
|
|
67282
|
+
let filterMatches = filter === null || filter === void 0;
|
|
67283
|
+
if (filter !== null && filter !== void 0) {
|
|
67284
|
+
try {
|
|
67285
|
+
filterMatches = evalBooleanExpression(filter, catchScope, ctx);
|
|
67286
|
+
} catch (filterError) {
|
|
67287
|
+
if (!isCatchableNSRuntimeError(filterError)) {
|
|
67288
|
+
synchronizeExistingBindings(
|
|
67289
|
+
scope,
|
|
67290
|
+
catchScope,
|
|
67291
|
+
parentBindingIds
|
|
67292
|
+
);
|
|
67293
|
+
throw filterError;
|
|
67294
|
+
}
|
|
67295
|
+
filterMatches = false;
|
|
67296
|
+
}
|
|
67297
|
+
}
|
|
67298
|
+
if (!filterMatches) {
|
|
67299
|
+
synchronizeExistingBindings(scope, catchScope, parentBindingIds);
|
|
67300
|
+
continue;
|
|
67301
|
+
}
|
|
67302
|
+
matched = true;
|
|
67303
|
+
let catchResult;
|
|
67304
|
+
try {
|
|
67305
|
+
catchResult = evalInstructions(
|
|
67306
|
+
clause.instructions,
|
|
67307
|
+
catchScope,
|
|
67308
|
+
ctx,
|
|
67309
|
+
options
|
|
67310
|
+
);
|
|
67311
|
+
} finally {
|
|
67312
|
+
synchronizeExistingBindings(scope, catchScope, parentBindingIds);
|
|
67313
|
+
}
|
|
67314
|
+
if (catchResult.kind !== "fallthrough") return catchResult;
|
|
67315
|
+
break;
|
|
67316
|
+
}
|
|
67317
|
+
if (!matched) throw error;
|
|
67318
|
+
break;
|
|
67319
|
+
}
|
|
67320
|
+
if (tryResult.kind !== "fallthrough") return tryResult;
|
|
67321
|
+
break;
|
|
67322
|
+
}
|
|
67323
|
+
case "break" /* break */:
|
|
67324
|
+
return { kind: "break" };
|
|
67325
|
+
case "continue" /* continue */:
|
|
67326
|
+
return { kind: "continue" };
|
|
64476
67327
|
}
|
|
64477
67328
|
void touchUnknown(ins);
|
|
64478
67329
|
}
|
|
@@ -66226,6 +69077,9 @@ function applyArithmetic(op, operands, decimal2) {
|
|
|
66226
69077
|
});
|
|
66227
69078
|
}
|
|
66228
69079
|
}
|
|
69080
|
+
function coerceAssignmentValue(value, targetTypeInfo, context) {
|
|
69081
|
+
return targetTypeInfo.type === 20 /* Decimal */ && typeof value === "number" ? coerceDecimalOperand(value, context) : value;
|
|
69082
|
+
}
|
|
66229
69083
|
function coerceDecimalOperand(value, context) {
|
|
66230
69084
|
if (typeof value === "string") {
|
|
66231
69085
|
if (!isDecimalString(value)) {
|
|
@@ -68488,33 +71342,55 @@ function collectionLength(c) {
|
|
|
68488
71342
|
`Cannot Count() ${typeof c}; expected list, dictionary, or string`
|
|
68489
71343
|
);
|
|
68490
71344
|
}
|
|
68491
|
-
function
|
|
68492
|
-
|
|
68493
|
-
|
|
68494
|
-
|
|
68495
|
-
|
|
68496
|
-
|
|
68497
|
-
|
|
68498
|
-
|
|
71345
|
+
function snapshotCollectionMembership(collection) {
|
|
71346
|
+
const membership = [];
|
|
71347
|
+
const valid = forEachRawCollectionEntry(collection, ({ raw }) => {
|
|
71348
|
+
membership.push(raw);
|
|
71349
|
+
});
|
|
71350
|
+
if (valid) return membership;
|
|
71351
|
+
throw new NSGetterRuntimeError(
|
|
71352
|
+
"NeoScript foreach requires a present collection."
|
|
71353
|
+
);
|
|
68499
71354
|
}
|
|
68500
|
-
function
|
|
71355
|
+
function forEachRawCollectionEntry(c, callback) {
|
|
68501
71356
|
if (Array.isArray(c)) {
|
|
68502
|
-
for (let
|
|
68503
|
-
const raw = c[
|
|
68504
|
-
|
|
68505
|
-
|
|
71357
|
+
for (let key = 0; key < c.length; key += 1) {
|
|
71358
|
+
const raw = c[key];
|
|
71359
|
+
callback({
|
|
71360
|
+
raw,
|
|
71361
|
+
key,
|
|
71362
|
+
valueId: typeof raw === "string" ? raw : null
|
|
71363
|
+
});
|
|
68506
71364
|
}
|
|
68507
|
-
return;
|
|
71365
|
+
return true;
|
|
68508
71366
|
}
|
|
68509
71367
|
if (typeof c === "object" && c !== null) {
|
|
68510
|
-
for (const [
|
|
68511
|
-
|
|
68512
|
-
|
|
71368
|
+
for (const [key, raw] of Object.entries(c)) {
|
|
71369
|
+
callback({
|
|
71370
|
+
raw,
|
|
71371
|
+
key,
|
|
71372
|
+
valueId: typeof raw === "string" ? raw : null
|
|
71373
|
+
});
|
|
68513
71374
|
}
|
|
71375
|
+
return true;
|
|
68514
71376
|
}
|
|
71377
|
+
return false;
|
|
71378
|
+
}
|
|
71379
|
+
function collectionEntries(c, ctx) {
|
|
71380
|
+
const entries = [];
|
|
71381
|
+
forEachRawCollectionEntry(c, ({ raw }) => {
|
|
71382
|
+
entries.push(resolveValueIfId(raw, ctx));
|
|
71383
|
+
});
|
|
71384
|
+
return entries;
|
|
71385
|
+
}
|
|
71386
|
+
function iterateCollection(c, ctx, callback) {
|
|
71387
|
+
forEachRawCollectionEntry(c, ({ raw, key, valueId }) => {
|
|
71388
|
+
const entry = resolveValueIfId(raw, ctx);
|
|
71389
|
+
callback(entry, key, valueId);
|
|
71390
|
+
});
|
|
68515
71391
|
}
|
|
68516
71392
|
function pushParams(parent, parameters, positional, isList) {
|
|
68517
|
-
const child =
|
|
71393
|
+
const child = createChildScope(parent);
|
|
68518
71394
|
if (parameters.length === 1) {
|
|
68519
71395
|
child.set(parameters[0].id, positional[1]);
|
|
68520
71396
|
} else if (parameters.length === 2) {
|
|
@@ -68524,7 +71400,7 @@ function pushParams(parent, parameters, positional, isList) {
|
|
|
68524
71400
|
}
|
|
68525
71401
|
return child;
|
|
68526
71402
|
}
|
|
68527
|
-
var NativeFunctionDelegateUnavailableError, liveListIndexesByProject, MAX_CONSTRUCTION_DEPTH, LazyValueOverlay;
|
|
71403
|
+
var NonCatchableNSGetterRuntimeError, NativeFunctionDelegateUnavailableError, CorruptNeoScriptIRError, liveListIndexesByProject, MAX_CONSTRUCTION_DEPTH, MAX_LOOP_ITERATIONS, LazyValueOverlay, readonlyBindingErrorsByScope, READONLY_FOREACH_BINDING_ERROR, READONLY_CATCH_BINDING_ERROR;
|
|
68528
71404
|
var init_evaluateNSGetter = __esm({
|
|
68529
71405
|
"../src/view-models/neoscript-evaluator/evaluateNSGetter.ts"() {
|
|
68530
71406
|
"use strict";
|
|
@@ -68540,10 +71416,15 @@ var init_evaluateNSGetter = __esm({
|
|
|
68540
71416
|
init_core();
|
|
68541
71417
|
init_member_storage_key();
|
|
68542
71418
|
init_project2();
|
|
68543
|
-
|
|
71419
|
+
NonCatchableNSGetterRuntimeError = class extends NSGetterRuntimeError {
|
|
71420
|
+
};
|
|
71421
|
+
NativeFunctionDelegateUnavailableError = class extends NonCatchableNSGetterRuntimeError {
|
|
71422
|
+
};
|
|
71423
|
+
CorruptNeoScriptIRError = class extends NonCatchableNSGetterRuntimeError {
|
|
68544
71424
|
};
|
|
68545
71425
|
liveListIndexesByProject = /* @__PURE__ */ new WeakMap();
|
|
68546
71426
|
MAX_CONSTRUCTION_DEPTH = 64;
|
|
71427
|
+
MAX_LOOP_ITERATIONS = 1e4;
|
|
68547
71428
|
LazyValueOverlay = class extends Map {
|
|
68548
71429
|
constructor(vm) {
|
|
68549
71430
|
super();
|
|
@@ -68603,6 +71484,9 @@ var init_evaluateNSGetter = __esm({
|
|
|
68603
71484
|
return indexes;
|
|
68604
71485
|
}
|
|
68605
71486
|
};
|
|
71487
|
+
readonlyBindingErrorsByScope = /* @__PURE__ */ new WeakMap();
|
|
71488
|
+
READONLY_FOREACH_BINDING_ERROR = "Cannot assign to a read-only foreach iterator binding.";
|
|
71489
|
+
READONLY_CATCH_BINDING_ERROR = "Cannot assign to a read-only catch message binding.";
|
|
68606
71490
|
}
|
|
68607
71491
|
});
|
|
68608
71492
|
|
|
@@ -69354,6 +72238,49 @@ function stringifyReturnInstructions(instructions, sourceType) {
|
|
|
69354
72238
|
pointer: stringifyTextVariablePointer(instruction.pointer, sourceType)
|
|
69355
72239
|
};
|
|
69356
72240
|
}
|
|
72241
|
+
if (instruction.type === "switch" /* switch */) {
|
|
72242
|
+
return {
|
|
72243
|
+
...instruction,
|
|
72244
|
+
sections: instruction.sections.map((section) => ({
|
|
72245
|
+
...section,
|
|
72246
|
+
instructions: stringifyReturnInstructions(
|
|
72247
|
+
section.instructions,
|
|
72248
|
+
sourceType
|
|
72249
|
+
)
|
|
72250
|
+
})),
|
|
72251
|
+
...instruction.defaultInstructions === void 0 ? {} : {
|
|
72252
|
+
defaultInstructions: instruction.defaultInstructions === null ? null : stringifyReturnInstructions(
|
|
72253
|
+
instruction.defaultInstructions,
|
|
72254
|
+
sourceType
|
|
72255
|
+
)
|
|
72256
|
+
}
|
|
72257
|
+
};
|
|
72258
|
+
}
|
|
72259
|
+
if (instruction.type === "try" /* try */) {
|
|
72260
|
+
return {
|
|
72261
|
+
...instruction,
|
|
72262
|
+
instructions: stringifyReturnInstructions(
|
|
72263
|
+
instruction.instructions,
|
|
72264
|
+
sourceType
|
|
72265
|
+
),
|
|
72266
|
+
catches: instruction.catches.map((clause) => ({
|
|
72267
|
+
...clause,
|
|
72268
|
+
instructions: stringifyReturnInstructions(
|
|
72269
|
+
clause.instructions,
|
|
72270
|
+
sourceType
|
|
72271
|
+
)
|
|
72272
|
+
}))
|
|
72273
|
+
};
|
|
72274
|
+
}
|
|
72275
|
+
if (instruction.type === "for" /* for */ || instruction.type === "forEach" /* forEach */) {
|
|
72276
|
+
return {
|
|
72277
|
+
...instruction,
|
|
72278
|
+
instructions: stringifyReturnInstructions(
|
|
72279
|
+
instruction.instructions,
|
|
72280
|
+
sourceType
|
|
72281
|
+
)
|
|
72282
|
+
};
|
|
72283
|
+
}
|
|
69357
72284
|
if (instruction.type !== "if" /* if */) return instruction;
|
|
69358
72285
|
return {
|
|
69359
72286
|
...instruction,
|
|
@@ -69928,7 +72855,7 @@ __export(script_exports, {
|
|
|
69928
72855
|
readDocumentArrays: () => readDocumentArrays,
|
|
69929
72856
|
runScript: () => runScript
|
|
69930
72857
|
});
|
|
69931
|
-
import { readFileSync as
|
|
72858
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
69932
72859
|
function readDocumentArrays(raw) {
|
|
69933
72860
|
const arrayOf2 = (field) => {
|
|
69934
72861
|
const value = raw[field];
|
|
@@ -70440,9 +73367,9 @@ function buildRootValue(document) {
|
|
|
70440
73367
|
}
|
|
70441
73368
|
function readSource(options, fallback) {
|
|
70442
73369
|
if (options.source !== null) return options.source;
|
|
70443
|
-
if (options.file !== null) return
|
|
73370
|
+
if (options.file !== null) return readFileSync11(options.file, "utf8");
|
|
70444
73371
|
if (fallback !== void 0) return fallback;
|
|
70445
|
-
const stdin =
|
|
73372
|
+
const stdin = readFileSync11(0, "utf8");
|
|
70446
73373
|
if (stdin.trim().length === 0) {
|
|
70447
73374
|
throw new Error(
|
|
70448
73375
|
"Provide NeoScript source as an argument, --file, or stdin."
|
|
@@ -70450,6 +73377,20 @@ function readSource(options, fallback) {
|
|
|
70450
73377
|
}
|
|
70451
73378
|
return stdin;
|
|
70452
73379
|
}
|
|
73380
|
+
function describeScriptBodyOrigin(options, storedBodyMemberLabel) {
|
|
73381
|
+
if (options.source !== null) return "the inline source argument";
|
|
73382
|
+
if (options.file !== null) return `--file ${options.file}`;
|
|
73383
|
+
if (storedBodyMemberLabel !== null) {
|
|
73384
|
+
return `the stored body of ${storedBodyMemberLabel}`;
|
|
73385
|
+
}
|
|
73386
|
+
return "stdin";
|
|
73387
|
+
}
|
|
73388
|
+
function qualifiedMemberLabel(thisClass, member) {
|
|
73389
|
+
const memberName = String(member.name ?? member.id);
|
|
73390
|
+
if (thisClass === null) return `'${memberName}'`;
|
|
73391
|
+
if (typeof thisClass.name !== "string") return `'${memberName}'`;
|
|
73392
|
+
return `'${thisClass.name}.${memberName}'`;
|
|
73393
|
+
}
|
|
70453
73394
|
function analyzeWorkspaceProjectManifestV4(workspace) {
|
|
70454
73395
|
const status = computeWorkspaceStatus(workspace, {
|
|
70455
73396
|
skipProjectBinaryInspection: true
|
|
@@ -70532,6 +73473,14 @@ async function runScript(workspace, command, options, dependencies = {}) {
|
|
|
70532
73473
|
);
|
|
70533
73474
|
}
|
|
70534
73475
|
const thisClass = setterMember !== null ? resolveMemberContainingClass(setterMember, document) : nsFunctionTarget !== null ? nsFunctionMember?.isStatic === true ? null : nsFunctionTarget.receiverClass : resolveThisClass(options.thisRef, document);
|
|
73476
|
+
const storedBodyMemberLabel = nsFunctionDefinition === null || nsFunctionMember === null ? null : qualifiedMemberLabel(thisClass, nsFunctionMember);
|
|
73477
|
+
const bodyOrigin = describeScriptBodyOrigin(options, storedBodyMemberLabel);
|
|
73478
|
+
const bodySite = (unit) => ({
|
|
73479
|
+
recordKind: "script",
|
|
73480
|
+
origin: bodyOrigin,
|
|
73481
|
+
unit,
|
|
73482
|
+
code: source
|
|
73483
|
+
});
|
|
70535
73484
|
const compileContext = {
|
|
70536
73485
|
project: document.project,
|
|
70537
73486
|
members: document.members,
|
|
@@ -70565,53 +73514,76 @@ async function runScript(workspace, command, options, dependencies = {}) {
|
|
|
70565
73514
|
source,
|
|
70566
73515
|
sharedContext
|
|
70567
73516
|
);
|
|
70568
|
-
|
|
70569
|
-
(
|
|
73517
|
+
compileNeoScriptBodyOrThrow(
|
|
73518
|
+
() => {
|
|
73519
|
+
const diagnostic = diagnostics.find(
|
|
73520
|
+
(candidate) => candidate.severity === "error"
|
|
73521
|
+
);
|
|
73522
|
+
if (diagnostic === void 0) return;
|
|
73523
|
+
throw new CompileError(diagnostic.message, {
|
|
73524
|
+
line: diagnostic.range.start.line + 1,
|
|
73525
|
+
column: diagnostic.range.start.character + 1
|
|
73526
|
+
});
|
|
73527
|
+
},
|
|
73528
|
+
bodySite(sharedKind),
|
|
73529
|
+
null
|
|
70570
73530
|
);
|
|
70571
|
-
if (error !== void 0) {
|
|
70572
|
-
throw new CompileError(error.message, {
|
|
70573
|
-
line: error.range.start.line + 1,
|
|
70574
|
-
column: error.range.start.character + 1
|
|
70575
|
-
});
|
|
70576
|
-
}
|
|
70577
73531
|
}
|
|
70578
73532
|
if (options.mode === "nsfunction") {
|
|
70579
73533
|
if (nsFunctionMember === null || nsFunctionDefinition === null) {
|
|
70580
73534
|
throw new Error("NSFunction mode requires a function target.");
|
|
70581
73535
|
}
|
|
70582
|
-
compiled =
|
|
70583
|
-
|
|
70584
|
-
|
|
70585
|
-
|
|
70586
|
-
|
|
70587
|
-
|
|
70588
|
-
|
|
70589
|
-
|
|
70590
|
-
|
|
70591
|
-
|
|
70592
|
-
|
|
70593
|
-
|
|
73536
|
+
compiled = compileNeoScriptBodyOrThrow(
|
|
73537
|
+
() => compileNSFunction(source, {
|
|
73538
|
+
project: document.project,
|
|
73539
|
+
members: document.members,
|
|
73540
|
+
classes: document.classes,
|
|
73541
|
+
enums: document.enums,
|
|
73542
|
+
interfaces: document.interfaces,
|
|
73543
|
+
thisClass,
|
|
73544
|
+
returnTypeInfo: nsFunctionDefinition.returnTypeInfo,
|
|
73545
|
+
argumentTypes: nsFunctionDefinition.argumentTypes,
|
|
73546
|
+
deferred: nsFunctionDefinition.deferred,
|
|
73547
|
+
functionName: String(nsFunctionMember.name ?? nsFunctionMember.id)
|
|
73548
|
+
}),
|
|
73549
|
+
bodySite("function"),
|
|
73550
|
+
null
|
|
73551
|
+
);
|
|
70594
73552
|
} else if (options.mode === "setter") {
|
|
70595
73553
|
if (setterMember === null) {
|
|
70596
73554
|
throw new Error("Setter mode requires an NSProperty member target.");
|
|
70597
73555
|
}
|
|
70598
|
-
compiled =
|
|
70599
|
-
|
|
70600
|
-
|
|
70601
|
-
|
|
70602
|
-
|
|
70603
|
-
|
|
70604
|
-
|
|
70605
|
-
|
|
70606
|
-
|
|
70607
|
-
|
|
70608
|
-
|
|
70609
|
-
|
|
73556
|
+
compiled = compileNeoScriptBodyOrThrow(
|
|
73557
|
+
() => compileNSSetter(source, {
|
|
73558
|
+
project: document.project,
|
|
73559
|
+
members: document.members,
|
|
73560
|
+
classes: document.classes,
|
|
73561
|
+
enums: document.enums,
|
|
73562
|
+
interfaces: document.interfaces,
|
|
73563
|
+
thisClass,
|
|
73564
|
+
valueTypeInfo: resolveScriptPropertyReturnTypeInfo(
|
|
73565
|
+
setterMember,
|
|
73566
|
+
document.members
|
|
73567
|
+
)
|
|
73568
|
+
}),
|
|
73569
|
+
bodySite("setter"),
|
|
73570
|
+
null
|
|
73571
|
+
);
|
|
73572
|
+
} else if (command === "apply" || options.mode === "action") {
|
|
73573
|
+
compiled = compileNeoScriptBodyOrThrow(
|
|
73574
|
+
() => compileNSAction(source, compileContext),
|
|
73575
|
+
bodySite("action"),
|
|
73576
|
+
null
|
|
73577
|
+
);
|
|
70610
73578
|
} else {
|
|
70611
|
-
compiled =
|
|
73579
|
+
compiled = compileNeoScriptBodyOrThrow(
|
|
73580
|
+
() => compileNSGetter(source, compileContext),
|
|
73581
|
+
bodySite("getter"),
|
|
73582
|
+
null
|
|
73583
|
+
);
|
|
70612
73584
|
}
|
|
70613
73585
|
} catch (error) {
|
|
70614
|
-
if (error instanceof
|
|
73586
|
+
if (error instanceof NeoScriptBodyCompileError) {
|
|
70615
73587
|
const report2 = {
|
|
70616
73588
|
ok: false,
|
|
70617
73589
|
error: error.message,
|
|
@@ -70623,7 +73595,7 @@ async function runScript(workspace, command, options, dependencies = {}) {
|
|
|
70623
73595
|
};
|
|
70624
73596
|
const hint = options.returns === null && error.message.includes("not assignable to declared return type") ? '\n(eval defaults to an unknown return type \u2014 pass --returns, e.g. --returns "string[]")' : "";
|
|
70625
73597
|
console.log(
|
|
70626
|
-
options.json ? JSON.stringify(report2, null, 2) : `Compile error
|
|
73598
|
+
options.json ? JSON.stringify(report2, null, 2) : `Compile error \u2014 ${error.message}${hint}`
|
|
70627
73599
|
);
|
|
70628
73600
|
process.exitCode = 1;
|
|
70629
73601
|
return;
|
|
@@ -71482,6 +74454,7 @@ var init_script = __esm({
|
|
|
71482
74454
|
init_workspace_status();
|
|
71483
74455
|
init_source_diagnostics();
|
|
71484
74456
|
init_project_documents();
|
|
74457
|
+
init_push_body_diagnostics();
|
|
71485
74458
|
RETURN_SHORTHANDS = {
|
|
71486
74459
|
// MemberKind numerics: Bool=1, Int=2, String=3, Float=4.
|
|
71487
74460
|
bool: { type: 1, required: true },
|
|
@@ -71503,8 +74476,8 @@ __export(migrate_exports, {
|
|
|
71503
74476
|
runMigrate: () => runMigrate
|
|
71504
74477
|
});
|
|
71505
74478
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
71506
|
-
import { existsSync as
|
|
71507
|
-
import { join as
|
|
74479
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
74480
|
+
import { join as join13 } from "node:path";
|
|
71508
74481
|
async function runMigrate(workspace, subcommand, positional, targetRef, json, dependencies = {}) {
|
|
71509
74482
|
if (subcommand === "new") {
|
|
71510
74483
|
const name = positional[0];
|
|
@@ -71513,10 +74486,10 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71513
74486
|
"Usage: neo migrate new <name> [--target <ClassName|project>]"
|
|
71514
74487
|
);
|
|
71515
74488
|
}
|
|
71516
|
-
const migrationsDir =
|
|
74489
|
+
const migrationsDir = join13(workspace.root, "Migrations");
|
|
71517
74490
|
mkdirSync9(migrationsDir, { recursive: true });
|
|
71518
74491
|
let nextOrder = 1;
|
|
71519
|
-
if (
|
|
74492
|
+
if (existsSync12(migrationsDir)) {
|
|
71520
74493
|
for (const entry of readdirSync5(migrationsDir)) {
|
|
71521
74494
|
const match = /^(\d+)-/.exec(entry);
|
|
71522
74495
|
if (match !== null) {
|
|
@@ -71525,8 +74498,8 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71525
74498
|
}
|
|
71526
74499
|
}
|
|
71527
74500
|
const relPath = migrationFileName(nextOrder, name);
|
|
71528
|
-
const absolute =
|
|
71529
|
-
if (
|
|
74501
|
+
const absolute = join13(workspace.root, relPath);
|
|
74502
|
+
if (existsSync12(absolute)) {
|
|
71530
74503
|
throw new Error(`"${relPath}" already exists.`);
|
|
71531
74504
|
}
|
|
71532
74505
|
const target = targetRef ?? "project";
|
|
@@ -71573,12 +74546,17 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71573
74546
|
assertMigrationCheckWorkspaceIsValid(status);
|
|
71574
74547
|
const candidate = localMigrationCheckCandidate(status);
|
|
71575
74548
|
const compileAction = dependencies.compileAction ?? compileNSAction;
|
|
74549
|
+
const bodySourceLocator = createNeoScriptBodySourceLocator(
|
|
74550
|
+
workspace,
|
|
74551
|
+
status
|
|
74552
|
+
);
|
|
71576
74553
|
let failures = 0;
|
|
71577
74554
|
const results = [];
|
|
71578
|
-
for (const { data: migration, file } of candidate.migrations) {
|
|
74555
|
+
for (const { data: migration, file, recordId } of candidate.migrations) {
|
|
71579
74556
|
if (typeof migration.code !== "string" || migration.code.trim().length === 0) {
|
|
71580
74557
|
continue;
|
|
71581
74558
|
}
|
|
74559
|
+
const code = migration.code;
|
|
71582
74560
|
const thisClass = typeof migration.targetClassId === "string" ? candidate.classes.find(
|
|
71583
74561
|
(schemaClass2) => schemaClass2.id === migration.targetClassId
|
|
71584
74562
|
) ?? null : null;
|
|
@@ -71588,16 +74566,27 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71588
74566
|
`Migration targets unknown local class "${migration.targetClassId}". Fix its @target directive or restore the class declaration.`
|
|
71589
74567
|
);
|
|
71590
74568
|
}
|
|
71591
|
-
|
|
71592
|
-
|
|
71593
|
-
|
|
71594
|
-
|
|
71595
|
-
|
|
71596
|
-
|
|
71597
|
-
|
|
71598
|
-
|
|
71599
|
-
|
|
71600
|
-
|
|
74569
|
+
compileNeoScriptBodyOrThrow(
|
|
74570
|
+
() => compileAction(code, {
|
|
74571
|
+
project: raw.project,
|
|
74572
|
+
members: candidate.members,
|
|
74573
|
+
classes: candidate.classes,
|
|
74574
|
+
enums: candidate.enums,
|
|
74575
|
+
interfaces: candidate.interfaces,
|
|
74576
|
+
thisClass,
|
|
74577
|
+
dialogueContext: null,
|
|
74578
|
+
migrationContext: true
|
|
74579
|
+
}),
|
|
74580
|
+
{
|
|
74581
|
+
recordKind: "migration",
|
|
74582
|
+
recordId,
|
|
74583
|
+
ownerName: migrationTargetClassName(thisClass),
|
|
74584
|
+
memberName: String(migration.name ?? recordId),
|
|
74585
|
+
unit: "migration",
|
|
74586
|
+
code
|
|
74587
|
+
},
|
|
74588
|
+
bodySourceLocator
|
|
74589
|
+
);
|
|
71601
74590
|
results.push({
|
|
71602
74591
|
id: migration.id,
|
|
71603
74592
|
name: migration.name,
|
|
@@ -71606,7 +74595,7 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71606
74595
|
});
|
|
71607
74596
|
} catch (error) {
|
|
71608
74597
|
failures += 1;
|
|
71609
|
-
const message = error instanceof
|
|
74598
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71610
74599
|
results.push({
|
|
71611
74600
|
id: migration.id,
|
|
71612
74601
|
name: migration.name,
|
|
@@ -71708,7 +74697,11 @@ async function runMigrate(workspace, subcommand, positional, targetRef, json, de
|
|
|
71708
74697
|
}
|
|
71709
74698
|
function localMigrationCheckCandidate(status) {
|
|
71710
74699
|
const byKind = (kind) => [...status.reconstructed.values()].filter((record3) => record3.recordKind === kind).map((record3) => record3.fullData);
|
|
71711
|
-
const migrations = [...status.reconstructed.values()].filter((record3) => record3.recordKind === "migration").map((record3) => ({
|
|
74700
|
+
const migrations = [...status.reconstructed.values()].filter((record3) => record3.recordKind === "migration").map((record3) => ({
|
|
74701
|
+
data: record3.fullData,
|
|
74702
|
+
file: record3.file,
|
|
74703
|
+
recordId: record3.recordId
|
|
74704
|
+
})).sort(
|
|
71712
74705
|
(left, right) => Number(left.data.order ?? 0) - Number(right.data.order ?? 0)
|
|
71713
74706
|
);
|
|
71714
74707
|
return {
|
|
@@ -71896,6 +74889,11 @@ function resolveAssignTarget(write, instanceValue, migrationName) {
|
|
|
71896
74889
|
}
|
|
71897
74890
|
return { childKey };
|
|
71898
74891
|
}
|
|
74892
|
+
function migrationTargetClassName(thisClass) {
|
|
74893
|
+
if (thisClass === null) return null;
|
|
74894
|
+
if (typeof thisClass.name !== "string") return null;
|
|
74895
|
+
return thisClass.name;
|
|
74896
|
+
}
|
|
71899
74897
|
async function runPendingMigrations(workspace, client, raw, migrations, onlyRef, dryRunFlagUnused) {
|
|
71900
74898
|
void dryRunFlagUnused;
|
|
71901
74899
|
const dryRun = process.argv.includes("--dry-run");
|
|
@@ -71956,16 +74954,28 @@ async function runPendingMigrations(workspace, client, raw, migrations, onlyRef,
|
|
|
71956
74954
|
);
|
|
71957
74955
|
}
|
|
71958
74956
|
const pinned = migration.action;
|
|
71959
|
-
const
|
|
71960
|
-
|
|
71961
|
-
|
|
71962
|
-
|
|
71963
|
-
|
|
71964
|
-
|
|
71965
|
-
|
|
71966
|
-
|
|
71967
|
-
|
|
71968
|
-
|
|
74957
|
+
const migrationCode = migration.code;
|
|
74958
|
+
const compiled = pinned !== void 0 && pinned !== null ? pinned : compileNeoScriptBodyOrThrow(
|
|
74959
|
+
() => compileNSAction(migrationCode, {
|
|
74960
|
+
project: document.project,
|
|
74961
|
+
members: document.members,
|
|
74962
|
+
classes: document.classes,
|
|
74963
|
+
enums: document.enums,
|
|
74964
|
+
interfaces: document.interfaces,
|
|
74965
|
+
thisClass,
|
|
74966
|
+
dialogueContext: null,
|
|
74967
|
+
migrationContext: true
|
|
74968
|
+
}),
|
|
74969
|
+
{
|
|
74970
|
+
recordKind: "migration",
|
|
74971
|
+
recordId: String(migration.id),
|
|
74972
|
+
ownerName: migrationTargetClassName(thisClass),
|
|
74973
|
+
memberName: migrationName,
|
|
74974
|
+
unit: "migration",
|
|
74975
|
+
code: migrationCode
|
|
74976
|
+
},
|
|
74977
|
+
null
|
|
74978
|
+
);
|
|
71969
74979
|
const instances = targetClassId === null ? [null] : collectClassInstances(document, targetClassId, memberById, valueById);
|
|
71970
74980
|
let instanceEditCount = 0;
|
|
71971
74981
|
for (const instance of instances) {
|
|
@@ -72512,6 +75522,7 @@ var init_migrate = __esm({
|
|
|
72512
75522
|
init_workspace_status();
|
|
72513
75523
|
init_source_diagnostics();
|
|
72514
75524
|
init_compiler_adapter();
|
|
75525
|
+
init_push_body_diagnostics();
|
|
72515
75526
|
init_neoscript_evaluator();
|
|
72516
75527
|
init_script();
|
|
72517
75528
|
init_project_migration_created_values();
|
|
@@ -73928,7 +76939,7 @@ __export(content_exports, {
|
|
|
73928
76939
|
runRecords: () => runRecords,
|
|
73929
76940
|
runValues: () => runValues
|
|
73930
76941
|
});
|
|
73931
|
-
import { readFileSync as
|
|
76942
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
73932
76943
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
73933
76944
|
function versionPath(workspace, suffix) {
|
|
73934
76945
|
return `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/${suffix}`;
|
|
@@ -73936,9 +76947,9 @@ function versionPath(workspace, suffix) {
|
|
|
73936
76947
|
function readBatch(file) {
|
|
73937
76948
|
let raw = null;
|
|
73938
76949
|
if (file !== null) {
|
|
73939
|
-
raw =
|
|
76950
|
+
raw = readFileSync12(file, "utf8");
|
|
73940
76951
|
} else if (!process.stdin.isTTY) {
|
|
73941
|
-
raw =
|
|
76952
|
+
raw = readFileSync12(0, "utf8");
|
|
73942
76953
|
if (raw.trim().length === 0) raw = null;
|
|
73943
76954
|
}
|
|
73944
76955
|
if (raw === null) return null;
|
|
@@ -75095,7 +78106,7 @@ async function runFiles(context, subcommand, positional) {
|
|
|
75095
78106
|
"Usage: neo files texture-settings <fileId> --file <settings.json>"
|
|
75096
78107
|
);
|
|
75097
78108
|
}
|
|
75098
|
-
const payload = JSON.parse(
|
|
78109
|
+
const payload = JSON.parse(readFileSync12(payloadPath, "utf8"));
|
|
75099
78110
|
const result = await context.client.post(
|
|
75100
78111
|
versionPath(context.workspace, `files/${fileId}/unity-texture-settings`),
|
|
75101
78112
|
payload
|
|
@@ -75124,7 +78135,7 @@ __export(export_exports, {
|
|
|
75124
78135
|
runExportUnity: () => runExportUnity
|
|
75125
78136
|
});
|
|
75126
78137
|
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
75127
|
-
import { join as
|
|
78138
|
+
import { join as join14 } from "node:path";
|
|
75128
78139
|
async function runExportUnity(workspace, outDir) {
|
|
75129
78140
|
if (outDir === null) {
|
|
75130
78141
|
throw new Error(
|
|
@@ -75136,23 +78147,23 @@ async function runExportUnity(workspace, outDir) {
|
|
|
75136
78147
|
`/api/projects/${workspace.config.projectId}/export`,
|
|
75137
78148
|
{ versionId: workspace.config.versionId }
|
|
75138
78149
|
);
|
|
75139
|
-
const resourcesDir =
|
|
75140
|
-
const localizationDir =
|
|
75141
|
-
const scriptsDir =
|
|
78150
|
+
const resourcesDir = join14(outDir, "Resources", "Neo");
|
|
78151
|
+
const localizationDir = join14(resourcesDir, "Localization");
|
|
78152
|
+
const scriptsDir = join14(outDir, "Scripts", "Neo");
|
|
75142
78153
|
mkdirSync10(localizationDir, { recursive: true });
|
|
75143
78154
|
mkdirSync10(scriptsDir, { recursive: true });
|
|
75144
|
-
writeFileSync10(
|
|
78155
|
+
writeFileSync10(join14(resourcesDir, "project.json"), response.projectJson);
|
|
75145
78156
|
writeFileSync10(
|
|
75146
|
-
|
|
78157
|
+
join14(scriptsDir, "NeoGeneratedTypes.cs"),
|
|
75147
78158
|
response.generatedTypes
|
|
75148
78159
|
);
|
|
75149
78160
|
for (const file of response.localizationFiles ?? []) {
|
|
75150
|
-
writeFileSync10(
|
|
78161
|
+
writeFileSync10(join14(localizationDir, file.fileName), file.content);
|
|
75151
78162
|
}
|
|
75152
|
-
console.log(`wrote ${
|
|
75153
|
-
console.log(`wrote ${
|
|
78163
|
+
console.log(`wrote ${join14(resourcesDir, "project.json")}`);
|
|
78164
|
+
console.log(`wrote ${join14(scriptsDir, "NeoGeneratedTypes.cs")}`);
|
|
75154
78165
|
for (const file of response.localizationFiles ?? []) {
|
|
75155
|
-
console.log(`wrote ${
|
|
78166
|
+
console.log(`wrote ${join14(localizationDir, file.fileName)}`);
|
|
75156
78167
|
}
|
|
75157
78168
|
const diagnostics = response.diagnostics ?? [];
|
|
75158
78169
|
for (const diagnostic of diagnostics) {
|
|
@@ -75298,8 +78309,8 @@ var init_project_source_bundle = __esm({
|
|
|
75298
78309
|
});
|
|
75299
78310
|
|
|
75300
78311
|
// src/project-source/project-file-push.ts
|
|
75301
|
-
import { basename as basename2, join as
|
|
75302
|
-
import { readFileSync as
|
|
78312
|
+
import { basename as basename2, join as join15 } from "node:path";
|
|
78313
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
75303
78314
|
function ensureProjectFileBinaryChangesV4(args) {
|
|
75304
78315
|
for (const binary of args.binaryChanges) {
|
|
75305
78316
|
if (binary.action !== "upload") continue;
|
|
@@ -75344,8 +78355,8 @@ function prepareProjectFilePushesV4(args) {
|
|
|
75344
78355
|
`Project file ${recordId} has upload bytes but its source change has no record data.`
|
|
75345
78356
|
);
|
|
75346
78357
|
}
|
|
75347
|
-
const absolute =
|
|
75348
|
-
const bytes = new Uint8Array(
|
|
78358
|
+
const absolute = join15(args.workspace.root, binary.path);
|
|
78359
|
+
const bytes = new Uint8Array(readFileSync13(absolute));
|
|
75349
78360
|
const digest = sha256Bytes(bytes);
|
|
75350
78361
|
if (binary.localSha256 !== null && digest !== binary.localSha256) {
|
|
75351
78362
|
throw new Error(
|
|
@@ -75677,9 +78688,9 @@ var init_project_file_push = __esm({
|
|
|
75677
78688
|
// src/project-source/trusted-commit-verification.ts
|
|
75678
78689
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
75679
78690
|
import { tmpdir } from "node:os";
|
|
75680
|
-
import { join as
|
|
78691
|
+
import { join as join16 } from "node:path";
|
|
75681
78692
|
function verifyProjectSourceCommitAgainstStateV4(args) {
|
|
75682
|
-
const root =
|
|
78693
|
+
const root = join16(tmpdir(), `neo-source-verify-virtual-${randomUUID5()}`);
|
|
75683
78694
|
const workspace = {
|
|
75684
78695
|
root,
|
|
75685
78696
|
config: {
|
|
@@ -76166,10 +79177,10 @@ import {
|
|
|
76166
79177
|
mkdirSync as mkdirSync11,
|
|
76167
79178
|
writeFileSync as writeFileSync11,
|
|
76168
79179
|
rmSync as rmSync6,
|
|
76169
|
-
existsSync as
|
|
76170
|
-
readFileSync as
|
|
79180
|
+
existsSync as existsSync13,
|
|
79181
|
+
readFileSync as readFileSync14
|
|
76171
79182
|
} from "node:fs";
|
|
76172
|
-
import { dirname as dirname7, join as
|
|
79183
|
+
import { dirname as dirname7, join as join17, relative as relative5, sep as sep5 } from "node:path";
|
|
76173
79184
|
function assignPendingIds(changes, staticValueSeeds, reconstructed3) {
|
|
76174
79185
|
const assigned = /* @__PURE__ */ new Map();
|
|
76175
79186
|
const assign = (pendingId2) => {
|
|
@@ -76734,9 +79745,11 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
76734
79745
|
(change) => change.recordKind === "member" || change.recordKind === "class" || change.recordKind === "enum" || change.recordKind === "interface" || change.nextData !== void 0 && change.recordKind === "migration" && typeof change.nextData.code === "string" && change.nextData.code.trim().length > 0
|
|
76735
79746
|
);
|
|
76736
79747
|
const compileSchema = needsNeoScriptCompilation ? await buildPostPushCompileSchema(workspace, status) : null;
|
|
79748
|
+
const bodySourceLocator = createNeoScriptBodySourceLocator(workspace, status);
|
|
76737
79749
|
if (compileSchema !== null) {
|
|
76738
79750
|
prepareCompleteNeoScriptBodyChanges(workspace, status, compileSchema, {
|
|
76739
|
-
completeSweep: workspaceChangesRequireCompleteBodySweep(status.changes)
|
|
79751
|
+
completeSweep: workspaceChangesRequireCompleteBodySweep(status.changes),
|
|
79752
|
+
bodySourceLocator
|
|
76740
79753
|
});
|
|
76741
79754
|
}
|
|
76742
79755
|
const now = Date.now();
|
|
@@ -76745,19 +79758,25 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
76745
79758
|
if (change.recordKind === "migration" && typeof change.nextData.code === "string" && change.nextData.code.trim().length > 0) {
|
|
76746
79759
|
change.nextData = {
|
|
76747
79760
|
...change.nextData,
|
|
76748
|
-
action: compileMigrationAction(
|
|
79761
|
+
action: compileMigrationAction(
|
|
79762
|
+
compileSchema,
|
|
79763
|
+
change.nextData,
|
|
79764
|
+
bodySourceLocator
|
|
79765
|
+
)
|
|
76749
79766
|
};
|
|
76750
79767
|
}
|
|
76751
79768
|
if (change.recordKind === "member" && change.nextData.kind === 10) {
|
|
76752
79769
|
change.nextData = compileNSPropertyChange(
|
|
76753
79770
|
compileSchema,
|
|
76754
|
-
change.nextData
|
|
79771
|
+
change.nextData,
|
|
79772
|
+
bodySourceLocator
|
|
76755
79773
|
);
|
|
76756
79774
|
}
|
|
76757
79775
|
if (change.recordKind === "member" && change.nextData.kind === 23) {
|
|
76758
79776
|
change.nextData = compileNSFunctionChange(
|
|
76759
79777
|
compileSchema,
|
|
76760
|
-
change.nextData
|
|
79778
|
+
change.nextData,
|
|
79779
|
+
bodySourceLocator
|
|
76761
79780
|
);
|
|
76762
79781
|
}
|
|
76763
79782
|
if (change.kind === "create") {
|
|
@@ -77672,13 +80691,13 @@ ${finalErrors.map(
|
|
|
77672
80691
|
for (const recordState of Object.values(workspace.state.records)) {
|
|
77673
80692
|
const previousPath = recordState.file;
|
|
77674
80693
|
if (previousPath === void 0 || emittedPaths.has(previousPath)) continue;
|
|
77675
|
-
const absolute =
|
|
77676
|
-
if (
|
|
80694
|
+
const absolute = join17(workspace.root, previousPath);
|
|
80695
|
+
if (existsSync13(absolute)) rmSync6(absolute);
|
|
77677
80696
|
}
|
|
77678
80697
|
for (const file of files) {
|
|
77679
|
-
const absolute =
|
|
80698
|
+
const absolute = join17(workspace.root, file.path);
|
|
77680
80699
|
mkdirSync11(dirname7(absolute), { recursive: true });
|
|
77681
|
-
const existing =
|
|
80700
|
+
const existing = existsSync13(absolute) ? readFileSync14(absolute, "utf8") : null;
|
|
77682
80701
|
if (existing !== file.content)
|
|
77683
80702
|
writeFileSync11(absolute, file.content, "utf8");
|
|
77684
80703
|
}
|
|
@@ -77715,7 +80734,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements) {
|
|
|
77715
80734
|
return {
|
|
77716
80735
|
uri,
|
|
77717
80736
|
kind,
|
|
77718
|
-
text:
|
|
80737
|
+
text: readFileSync14(absolutePath, "utf8")
|
|
77719
80738
|
};
|
|
77720
80739
|
}
|
|
77721
80740
|
);
|
|
@@ -77905,72 +80924,107 @@ function containingClass(schema, memberId) {
|
|
|
77905
80924
|
return isObjectRecord2(classSchema) && Object.values(classSchema).includes(memberId);
|
|
77906
80925
|
}) ?? null;
|
|
77907
80926
|
}
|
|
77908
|
-
function
|
|
80927
|
+
function bodyOwnerName(thisClass) {
|
|
80928
|
+
if (thisClass === null) return null;
|
|
80929
|
+
if (typeof thisClass.name !== "string") return null;
|
|
80930
|
+
return thisClass.name;
|
|
80931
|
+
}
|
|
80932
|
+
function compileNSPropertyChange(schema, memberData, locator) {
|
|
77909
80933
|
const memberId = memberData.id;
|
|
77910
80934
|
const thisClass = containingClass(schema, memberId);
|
|
77911
80935
|
const returnTypeInfo = resolveNSPropertyReturnTypeInfo(
|
|
77912
80936
|
memberData,
|
|
77913
80937
|
schema.members
|
|
77914
80938
|
);
|
|
80939
|
+
const bodyIdentity = {
|
|
80940
|
+
recordKind: "member",
|
|
80941
|
+
recordId: String(memberId),
|
|
80942
|
+
ownerName: bodyOwnerName(thisClass),
|
|
80943
|
+
memberName: String(memberData.name ?? memberId)
|
|
80944
|
+
};
|
|
77915
80945
|
const next = { ...memberData };
|
|
77916
80946
|
if (typeof memberData.code === "string") {
|
|
77917
|
-
|
|
77918
|
-
|
|
77919
|
-
|
|
77920
|
-
|
|
77921
|
-
|
|
77922
|
-
|
|
77923
|
-
|
|
77924
|
-
|
|
77925
|
-
|
|
77926
|
-
|
|
77927
|
-
|
|
77928
|
-
|
|
80947
|
+
const code = memberData.code;
|
|
80948
|
+
next.getter = compileNeoScriptBodyOrThrow(
|
|
80949
|
+
() => compileNSGetter2(code, {
|
|
80950
|
+
project: schema.project,
|
|
80951
|
+
members: schema.members,
|
|
80952
|
+
classes: schema.classes,
|
|
80953
|
+
enums: schema.enums,
|
|
80954
|
+
interfaces: schema.interfaces,
|
|
80955
|
+
thisClass,
|
|
80956
|
+
returnTypeInfo,
|
|
80957
|
+
dialogueContext: null,
|
|
80958
|
+
implicitMemberAccess: true,
|
|
80959
|
+
staticMember: memberData.isStatic === true
|
|
80960
|
+
}),
|
|
80961
|
+
{ ...bodyIdentity, unit: "getter", code },
|
|
80962
|
+
locator
|
|
80963
|
+
);
|
|
77929
80964
|
} else {
|
|
77930
80965
|
delete next.getter;
|
|
77931
80966
|
}
|
|
77932
80967
|
if (typeof memberData.setterCode === "string") {
|
|
77933
|
-
|
|
77934
|
-
|
|
77935
|
-
|
|
77936
|
-
|
|
77937
|
-
|
|
77938
|
-
|
|
77939
|
-
|
|
77940
|
-
|
|
77941
|
-
|
|
77942
|
-
|
|
77943
|
-
|
|
80968
|
+
const setterCode = memberData.setterCode;
|
|
80969
|
+
next.setter = compileNeoScriptBodyOrThrow(
|
|
80970
|
+
() => compileNSSetter2(setterCode, {
|
|
80971
|
+
project: schema.project,
|
|
80972
|
+
members: schema.members,
|
|
80973
|
+
classes: schema.classes,
|
|
80974
|
+
enums: schema.enums,
|
|
80975
|
+
interfaces: schema.interfaces,
|
|
80976
|
+
thisClass,
|
|
80977
|
+
valueTypeInfo: returnTypeInfo,
|
|
80978
|
+
implicitMemberAccess: true,
|
|
80979
|
+
staticMember: memberData.isStatic === true
|
|
80980
|
+
}),
|
|
80981
|
+
{ ...bodyIdentity, unit: "setter", code: setterCode },
|
|
80982
|
+
locator
|
|
80983
|
+
);
|
|
77944
80984
|
} else {
|
|
77945
80985
|
delete next.setter;
|
|
77946
80986
|
}
|
|
77947
80987
|
return next;
|
|
77948
80988
|
}
|
|
77949
|
-
function compileNSFunctionChange(schema, memberData) {
|
|
80989
|
+
function compileNSFunctionChange(schema, memberData, locator) {
|
|
77950
80990
|
const next = { ...memberData };
|
|
77951
80991
|
if (typeof memberData.code !== "string") {
|
|
77952
80992
|
delete next.action;
|
|
77953
80993
|
return next;
|
|
77954
80994
|
}
|
|
80995
|
+
const code = memberData.code;
|
|
77955
80996
|
const contract = resolveNSFunctionContract(memberData, schema.members);
|
|
77956
|
-
|
|
77957
|
-
|
|
77958
|
-
|
|
77959
|
-
|
|
77960
|
-
|
|
77961
|
-
|
|
77962
|
-
|
|
77963
|
-
|
|
77964
|
-
|
|
77965
|
-
|
|
77966
|
-
|
|
77967
|
-
|
|
77968
|
-
|
|
77969
|
-
|
|
80997
|
+
const thisClass = containingClass(schema, memberData.id);
|
|
80998
|
+
next.action = compileNeoScriptBodyOrThrow(
|
|
80999
|
+
() => compileNSFunction2(code, {
|
|
81000
|
+
project: schema.project,
|
|
81001
|
+
members: schema.members,
|
|
81002
|
+
classes: schema.classes,
|
|
81003
|
+
enums: schema.enums,
|
|
81004
|
+
interfaces: schema.interfaces,
|
|
81005
|
+
thisClass,
|
|
81006
|
+
returnTypeInfo: contract.returnTypeInfo,
|
|
81007
|
+
argumentTypes: contract.argumentTypes,
|
|
81008
|
+
deferred: contract.deferred,
|
|
81009
|
+
functionName: String(memberData.name ?? memberData.id),
|
|
81010
|
+
implicitMemberAccess: true,
|
|
81011
|
+
staticMember: memberData.isStatic === true
|
|
81012
|
+
}),
|
|
81013
|
+
{
|
|
81014
|
+
recordKind: "member",
|
|
81015
|
+
recordId: String(memberData.id),
|
|
81016
|
+
ownerName: bodyOwnerName(thisClass),
|
|
81017
|
+
memberName: String(memberData.name ?? memberData.id),
|
|
81018
|
+
unit: "function",
|
|
81019
|
+
code
|
|
81020
|
+
},
|
|
81021
|
+
locator
|
|
81022
|
+
);
|
|
77970
81023
|
return next;
|
|
77971
81024
|
}
|
|
77972
81025
|
function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options = {}) {
|
|
77973
81026
|
const completeSweep = options.completeSweep ?? true;
|
|
81027
|
+
const locator = options.bodySourceLocator ?? createNeoScriptBodySourceLocator(workspace, status);
|
|
77974
81028
|
const changesById = new Map(
|
|
77975
81029
|
status.changes.filter((change) => change.recordKind === "member").map((change) => [change.recordId, change])
|
|
77976
81030
|
);
|
|
@@ -77980,10 +81034,10 @@ function prepareCompleteNeoScriptBodyChanges(workspace, status, schema, options
|
|
|
77980
81034
|
return member;
|
|
77981
81035
|
}
|
|
77982
81036
|
if (member.kind === 10) {
|
|
77983
|
-
return compileNSPropertyChange(schema, member);
|
|
81037
|
+
return compileNSPropertyChange(schema, member, locator);
|
|
77984
81038
|
}
|
|
77985
81039
|
if (member.kind === 23) {
|
|
77986
|
-
return compileNSFunctionChange(schema, member);
|
|
81040
|
+
return compileNSFunctionChange(schema, member, locator);
|
|
77987
81041
|
}
|
|
77988
81042
|
return member;
|
|
77989
81043
|
});
|
|
@@ -78322,21 +81376,33 @@ function resolveNSFunctionContract(member, members) {
|
|
|
78322
81376
|
}
|
|
78323
81377
|
return { returnTypeInfo, argumentTypes, deferred };
|
|
78324
81378
|
}
|
|
78325
|
-
function compileMigrationAction(schema, migrationData) {
|
|
81379
|
+
function compileMigrationAction(schema, migrationData, locator) {
|
|
78326
81380
|
const targetClassId = migrationData.targetClassId;
|
|
78327
81381
|
const thisClass = typeof targetClassId === "string" ? schema.classes.find(
|
|
78328
81382
|
(schemaClass2) => schemaClass2.id === targetClassId
|
|
78329
81383
|
) ?? null : null;
|
|
78330
|
-
|
|
78331
|
-
|
|
78332
|
-
|
|
78333
|
-
|
|
78334
|
-
|
|
78335
|
-
|
|
78336
|
-
|
|
78337
|
-
|
|
78338
|
-
|
|
78339
|
-
|
|
81384
|
+
const code = String(migrationData.code);
|
|
81385
|
+
return compileNeoScriptBodyOrThrow(
|
|
81386
|
+
() => compileNSAction2(code, {
|
|
81387
|
+
project: schema.project,
|
|
81388
|
+
members: schema.members,
|
|
81389
|
+
classes: schema.classes,
|
|
81390
|
+
enums: schema.enums,
|
|
81391
|
+
interfaces: schema.interfaces,
|
|
81392
|
+
thisClass,
|
|
81393
|
+
dialogueContext: null,
|
|
81394
|
+
migrationContext: true
|
|
81395
|
+
}),
|
|
81396
|
+
{
|
|
81397
|
+
recordKind: "migration",
|
|
81398
|
+
recordId: String(migrationData.id),
|
|
81399
|
+
ownerName: bodyOwnerName(thisClass),
|
|
81400
|
+
memberName: String(migrationData.name ?? migrationData.id),
|
|
81401
|
+
unit: "migration",
|
|
81402
|
+
code
|
|
81403
|
+
},
|
|
81404
|
+
locator
|
|
81405
|
+
);
|
|
78340
81406
|
}
|
|
78341
81407
|
var compileNSAction2, compileNSFunction2, compileNSGetter2, compileNSSetter2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS;
|
|
78342
81408
|
var init_push = __esm({
|
|
@@ -78365,6 +81431,7 @@ var init_push = __esm({
|
|
|
78365
81431
|
init_project_manifest();
|
|
78366
81432
|
init_merge();
|
|
78367
81433
|
init_push_change_intent();
|
|
81434
|
+
init_push_body_diagnostics();
|
|
78368
81435
|
({ compileNSAction: compileNSAction2, compileNSFunction: compileNSFunction2, compileNSGetter: compileNSGetter2, compileNSSetter: compileNSSetter2 } = compiler_adapter_exports);
|
|
78369
81436
|
ProjectTransactionInterruptedError = class extends Error {
|
|
78370
81437
|
constructor(transactionId) {
|
|
@@ -78403,7 +81470,7 @@ __export(dev_exports, {
|
|
|
78403
81470
|
runDev: () => runDev
|
|
78404
81471
|
});
|
|
78405
81472
|
import { watch } from "node:fs";
|
|
78406
|
-
import { join as
|
|
81473
|
+
import { join as join18 } from "node:path";
|
|
78407
81474
|
import { emitKeypressEvents } from "node:readline";
|
|
78408
81475
|
import { ConvexClient } from "convex/browser";
|
|
78409
81476
|
function isSchemaSignal(value) {
|
|
@@ -78513,7 +81580,7 @@ async function runDev(workspace, options) {
|
|
|
78513
81580
|
};
|
|
78514
81581
|
for (const dir of ["Classes", "Enums"]) {
|
|
78515
81582
|
try {
|
|
78516
|
-
watch(
|
|
81583
|
+
watch(join18(workspace.root, dir), { persistent: true }, onFileChange);
|
|
78517
81584
|
} catch {
|
|
78518
81585
|
}
|
|
78519
81586
|
}
|
|
@@ -78568,12 +81635,12 @@ __export(resolve_exports, {
|
|
|
78568
81635
|
runResolve: () => runResolve,
|
|
78569
81636
|
workspaceFilePath: () => workspaceFilePath
|
|
78570
81637
|
});
|
|
78571
|
-
import { readFileSync as
|
|
78572
|
-
import { join as
|
|
81638
|
+
import { readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync12 } from "node:fs";
|
|
81639
|
+
import { join as join19 } from "node:path";
|
|
78573
81640
|
function runResolve(workspace, side) {
|
|
78574
81641
|
let resolvedFiles = 0;
|
|
78575
81642
|
for (const filePath of listProjectSourceFilesV4(workspace.root)) {
|
|
78576
|
-
const source =
|
|
81643
|
+
const source = readFileSync15(filePath, "utf8");
|
|
78577
81644
|
if (detectConflictMarkers(source) === null) continue;
|
|
78578
81645
|
const resolved = resolveMarkers(source, side);
|
|
78579
81646
|
writeFileSync12(filePath, resolved, "utf8");
|
|
@@ -78584,12 +81651,12 @@ function runResolve(workspace, side) {
|
|
|
78584
81651
|
const binary = state.projectBinary;
|
|
78585
81652
|
const conflict2 = binary?.conflict;
|
|
78586
81653
|
if (binary === void 0 || conflict2 === void 0) continue;
|
|
78587
|
-
const destination =
|
|
81654
|
+
const destination = join19(workspace.root, binary.path);
|
|
78588
81655
|
if (side === "theirs") {
|
|
78589
81656
|
if (conflict2.remoteSha256 !== null && conflict2.artifactPath !== void 0) {
|
|
78590
81657
|
writeVerifiedBinaryDownloadV4(
|
|
78591
81658
|
destination,
|
|
78592
|
-
|
|
81659
|
+
readFileSync15(join19(workspace.root, conflict2.artifactPath)),
|
|
78593
81660
|
conflict2.remoteSha256
|
|
78594
81661
|
);
|
|
78595
81662
|
binary.sha256 = conflict2.remoteSha256;
|
|
@@ -78599,7 +81666,7 @@ function runResolve(workspace, side) {
|
|
|
78599
81666
|
}
|
|
78600
81667
|
}
|
|
78601
81668
|
if (conflict2.artifactPath !== void 0) {
|
|
78602
|
-
rmSync7(
|
|
81669
|
+
rmSync7(join19(workspace.root, conflict2.artifactPath), { force: true });
|
|
78603
81670
|
}
|
|
78604
81671
|
delete binary.conflict;
|
|
78605
81672
|
resolvedBinaries += 1;
|
|
@@ -78652,7 +81719,7 @@ function resolveMarkers(source, side) {
|
|
|
78652
81719
|
return output.join("\n");
|
|
78653
81720
|
}
|
|
78654
81721
|
function workspaceFilePath(workspace, file) {
|
|
78655
|
-
return
|
|
81722
|
+
return join19(workspace.root, file);
|
|
78656
81723
|
}
|
|
78657
81724
|
var init_resolve = __esm({
|
|
78658
81725
|
"src/commands/resolve.ts"() {
|