@velarscript/compiler 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advisory-suppression.d.ts +62 -0
- package/dist/advisory-suppression.d.ts.map +1 -0
- package/dist/advisory-suppression.js +179 -0
- package/dist/advisory-suppression.js.map +1 -0
- package/dist/analyzer.d.ts +405 -17
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +1755 -211
- package/dist/analyzer.js.map +1 -1
- package/dist/ast.d.ts +6 -3
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js.map +1 -1
- package/dist/binding-stability.d.ts +22 -0
- package/dist/binding-stability.d.ts.map +1 -0
- package/dist/binding-stability.js +63 -0
- package/dist/binding-stability.js.map +1 -0
- package/dist/collection-lowering-runtime.d.ts +1 -1
- package/dist/collection-lowering-runtime.d.ts.map +1 -1
- package/dist/collection-lowering-runtime.js +183 -56
- package/dist/collection-lowering-runtime.js.map +1 -1
- package/dist/diagnostic.d.ts +34 -0
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +11 -1
- package/dist/diagnostic.js.map +1 -1
- package/dist/embedded-javascript.d.ts +12 -1
- package/dist/embedded-javascript.d.ts.map +1 -1
- package/dist/embedded-javascript.js +111 -2
- package/dist/embedded-javascript.js.map +1 -1
- package/dist/embedded-module.d.ts.map +1 -1
- package/dist/embedded-module.js +22 -0
- package/dist/embedded-module.js.map +1 -1
- package/dist/emitter.d.ts +83 -1
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +518 -41
- package/dist/emitter.js.map +1 -1
- package/dist/error-runtime.d.ts +9 -0
- package/dist/error-runtime.d.ts.map +1 -1
- package/dist/error-runtime.js +21 -0
- package/dist/error-runtime.js.map +1 -1
- package/dist/extension.d.ts +5 -2
- package/dist/extension.d.ts.map +1 -1
- package/dist/extension.js +2 -1
- package/dist/extension.js.map +1 -1
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +222 -91
- package/dist/formatter.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -5
- package/dist/index.js.map +1 -1
- package/dist/interpolated-string.d.ts.map +1 -1
- package/dist/interpolated-string.js +29 -2
- package/dist/interpolated-string.js.map +1 -1
- package/dist/json-runtime.d.ts.map +1 -1
- package/dist/json-runtime.js +27 -3
- package/dist/json-runtime.js.map +1 -1
- package/dist/lexer.d.ts +217 -1
- package/dist/lexer.d.ts.map +1 -1
- package/dist/lexer.js +799 -37
- package/dist/lexer.js.map +1 -1
- package/dist/limits.d.ts +9 -0
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +9 -0
- package/dist/limits.js.map +1 -1
- package/dist/mechanical-fix.d.ts.map +1 -1
- package/dist/mechanical-fix.js +38 -0
- package/dist/mechanical-fix.js.map +1 -1
- package/dist/parser.d.ts +30 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +137 -21
- package/dist/parser.js.map +1 -1
- package/dist/promise-runtime.d.ts +20 -1
- package/dist/promise-runtime.d.ts.map +1 -1
- package/dist/promise-runtime.js +38 -3
- package/dist/promise-runtime.js.map +1 -1
- package/dist/reactive-bridge-runtime.d.ts.map +1 -1
- package/dist/reactive-bridge-runtime.js +7 -1
- package/dist/reactive-bridge-runtime.js.map +1 -1
- package/dist/runtime-abi.d.ts +21 -1
- package/dist/runtime-abi.d.ts.map +1 -1
- package/dist/runtime-abi.js +23 -3
- package/dist/runtime-abi.js.map +1 -1
- package/dist/source-names.d.ts +26 -0
- package/dist/source-names.d.ts.map +1 -1
- package/dist/source-names.js +49 -5
- package/dist/source-names.js.map +1 -1
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +13 -0
- package/dist/source.js.map +1 -1
- package/dist/stable-order.d.ts +16 -0
- package/dist/stable-order.d.ts.map +1 -0
- package/dist/stable-order.js +18 -0
- package/dist/stable-order.js.map +1 -0
- package/dist/text-runtime.d.ts.map +1 -1
- package/dist/text-runtime.js +182 -41
- package/dist/text-runtime.js.map +1 -1
- package/dist/token.d.ts +11 -0
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js.map +1 -1
- package/dist/type-registry-runtime.d.ts.map +1 -1
- package/dist/type-registry-runtime.js +8 -1
- package/dist/type-registry-runtime.js.map +1 -1
- package/dist/type-validation-runtime.d.ts.map +1 -1
- package/dist/type-validation-runtime.js +129 -1
- package/dist/type-validation-runtime.js.map +1 -1
- package/dist/types.d.ts +17 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +161 -17
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/parser.js
CHANGED
|
@@ -5,6 +5,7 @@ import { findInterpolatedExpressionEnd, scanStringEscape, scanStringLiteral } fr
|
|
|
5
5
|
import { declarationKeywordGuidance, sourceTypeNameGuidance, REST_PARAMETER_ELEMENT_TYPE_MESSAGE } from "./language-guidance.js";
|
|
6
6
|
import { Lexer } from "./lexer.js";
|
|
7
7
|
import { span } from "./source.js";
|
|
8
|
+
import { isTypeEvidenceName } from "./source-names.js";
|
|
8
9
|
import { keywordKinds } from "./token.js";
|
|
9
10
|
import { formatTypeSyntax } from "./types.js";
|
|
10
11
|
const memberNameKinds = new Set(["identifier", ...Object.values(keywordKinds)]);
|
|
@@ -22,7 +23,35 @@ const statementStarterWords = new Set(CORE_STATEMENT_HEAD_KEYWORDS);
|
|
|
22
23
|
// Token kinds that legally appear at the top level of a record literal's
|
|
23
24
|
// field list: field names, shorthand entries, and their separators.
|
|
24
25
|
const recordFieldLevelKinds = new Set(["identifier", "string", "comma", ...Object.values(keywordKinds)]);
|
|
26
|
+
// A generic close that runs into the next operator lexes as one token: `>>`,
|
|
27
|
+
// `>>>`, and — where a default value or an assignment follows the annotation —
|
|
28
|
+
// `>=`, `>>=`, `>>>=`. Each maps to what is left after one `>` is taken for
|
|
29
|
+
// the close, so `List<number>=[1]` reads as the annotation and the `=` the
|
|
30
|
+
// author wrote. The lexer keeps emitting the compound operators, so the
|
|
31
|
+
// expression grammar is untouched.
|
|
32
|
+
const typeGreaterRemainderKinds = new Map([
|
|
33
|
+
["rightShift", "greater"],
|
|
34
|
+
["unsignedRightShift", "rightShift"],
|
|
35
|
+
["greaterEqual", "assign"],
|
|
36
|
+
["rightShiftAssign", "greaterEqual"],
|
|
37
|
+
["unsignedRightShiftAssign", "rightShiftAssign"],
|
|
38
|
+
]);
|
|
39
|
+
// The token kinds a type argument list can contain: names and the punctuation
|
|
40
|
+
// of optional, union, function, qualified and nested types. Anything else
|
|
41
|
+
// between `<` and `>` leaves the comparison reading as the only one.
|
|
42
|
+
const typeArgumentTokenKinds = new Set([
|
|
43
|
+
"identifier", "null", "comma", "dot", "question", "pipe", "arrow", "fatArrow",
|
|
44
|
+
"leftParen", "rightParen", "leftBracket", "rightBracket", "colon", "ellipsis",
|
|
45
|
+
]);
|
|
25
46
|
const MAX_PARSE_DEPTH = 512;
|
|
47
|
+
// An interpolation re-enters the compiler — a fresh lex and a fresh parse of
|
|
48
|
+
// the fragment — so one nest holds far more JavaScript stack than one ordinary
|
|
49
|
+
// expression level. Charging it a block of the budget keeps
|
|
50
|
+
// PARSER_COMPLEXITY_FAILURE ahead of the stack, which is what turns a
|
|
51
|
+
// pathological f-string into one diagnostic instead of seconds of work ended
|
|
52
|
+
// by a stack overflow. 64 nested interpolations remain available, which is
|
|
53
|
+
// orders of magnitude past any spelling a reader can follow.
|
|
54
|
+
const NESTED_EXPRESSION_PARSE_COST = 8;
|
|
26
55
|
const PARSER_COMPLEXITY_FAILURE = Object.freeze({ kind: "VelarParserComplexityFailure" });
|
|
27
56
|
export function isParserComplexityFailure(value) {
|
|
28
57
|
return value === PARSER_COMPLEXITY_FAILURE;
|
|
@@ -73,6 +102,13 @@ function describeStatementToken(token) {
|
|
|
73
102
|
return token.kind;
|
|
74
103
|
return text.length > 24 ? `${text.slice(0, 24)}…` : text;
|
|
75
104
|
}
|
|
105
|
+
// D90 R6: the spelling a numeric token was written with. The lexer keeps it
|
|
106
|
+
// only where it differs from the value the token carries — digit separators and
|
|
107
|
+
// an uppercase radix prefix — so a representability report quotes the author's
|
|
108
|
+
// own line rather than the normalized digits it was read from.
|
|
109
|
+
function writtenNumber(token) {
|
|
110
|
+
return token.payload?.written ?? token.value;
|
|
111
|
+
}
|
|
76
112
|
const assignmentOperators = {
|
|
77
113
|
assign: "=",
|
|
78
114
|
plusAssign: "+=",
|
|
@@ -95,6 +131,7 @@ export class Parser {
|
|
|
95
131
|
tokens;
|
|
96
132
|
lexicalExtensions;
|
|
97
133
|
diagnostics = [];
|
|
134
|
+
advisories = [];
|
|
98
135
|
genericCallableNames = new Set();
|
|
99
136
|
/** Extension-owned contextual keywords: names until a shape claims them. */
|
|
100
137
|
contextualKeywords;
|
|
@@ -141,6 +178,7 @@ export class Parser {
|
|
|
141
178
|
return {
|
|
142
179
|
program: { kind: "Program", body, span: span(0, end) },
|
|
143
180
|
diagnostics: this.diagnostics,
|
|
181
|
+
advisories: this.advisories,
|
|
144
182
|
};
|
|
145
183
|
}
|
|
146
184
|
parseExpressionFragment() {
|
|
@@ -150,7 +188,7 @@ export class Parser {
|
|
|
150
188
|
if (!this.check("eof")) {
|
|
151
189
|
this.diagnostics.push(diagnostic("VEL2006", "Unexpected tokens in interpolated expression", this.current().span));
|
|
152
190
|
}
|
|
153
|
-
return { expression, diagnostics: this.diagnostics };
|
|
191
|
+
return { expression, diagnostics: this.diagnostics, advisories: this.advisories };
|
|
154
192
|
}
|
|
155
193
|
// An assignment written where only an expression is valid (an interpolated
|
|
156
194
|
// fragment or an arrow body) receives directive guidance and recovers as an
|
|
@@ -317,6 +355,22 @@ export class Parser {
|
|
|
317
355
|
this.diagnostics.push(diagnostic("VEL2033", `Use '${target} = not ${target}'; the 'invert' statement was removed`, removed));
|
|
318
356
|
return { kind: "PassStatement", span: removed };
|
|
319
357
|
}
|
|
358
|
+
// D89 (message correction): 'raise E(...)' is Python's spelling of the
|
|
359
|
+
// statement VelarScript writes with 'throw'. Left alone it falls into the
|
|
360
|
+
// unknown-declaration-keyword message below, which lists 'def', 'type',
|
|
361
|
+
// 'enum', 'class', 'const', and 'let' and never names the one word that
|
|
362
|
+
// was wrong. 'raise' stayed an ordinary identifier, so only the Python
|
|
363
|
+
// statement's own shape — the bare word followed by the error value — is
|
|
364
|
+
// claimed here; 'raise(...)', 'raise.field', and 'raise = ...' keep their
|
|
365
|
+
// ordinary meanings. The recovery parses the rest as the throw it meant,
|
|
366
|
+
// so the thrown value is checked in the same compile.
|
|
367
|
+
if (this.check("identifier") && this.current().value === "raise" && this.peekKind(1) === "identifier") {
|
|
368
|
+
const keyword = this.current();
|
|
369
|
+
this.diagnostics.push(recoveredDiagnostic("VEL2026", "Use 'throw'; VelarScript raises an error with 'throw value'", keyword.span, mechanicalFix(keyword.span, "throw", "Use 'throw'")));
|
|
370
|
+
this.advance();
|
|
371
|
+
const value = this.parseExpression();
|
|
372
|
+
return { kind: "ThrowStatement", value, span: span(keyword.span.start, value.span.end) };
|
|
373
|
+
}
|
|
320
374
|
if (this.check("identifier") && this.peekKind(1) === "identifier") {
|
|
321
375
|
const first = this.current();
|
|
322
376
|
const guidance = declarationKeywordGuidance(first.value);
|
|
@@ -2776,25 +2830,32 @@ export class Parser {
|
|
|
2776
2830
|
const callableName = expression.kind === "IdentifierExpression" ? expression.name
|
|
2777
2831
|
: expression.kind === "MemberExpression" ? expression.property
|
|
2778
2832
|
: null;
|
|
2779
|
-
if (callableName === null || !this.check("less")
|
|
2833
|
+
if (callableName === null || !this.check("less"))
|
|
2780
2834
|
return null;
|
|
2781
2835
|
// Beyond the same-file generic-name list, the recovery extends to any
|
|
2782
2836
|
// callee — imported generics and methods — when the angle content carries
|
|
2783
2837
|
// type evidence: a builtin type name, a capitalized name, optional or
|
|
2784
2838
|
// union syntax, or nested generics. Without that evidence the comparison
|
|
2785
2839
|
// reading wins, so `a<b>(c)` over three numbers stays a chain (rule #41).
|
|
2840
|
+
//
|
|
2841
|
+
// D90: the reading does not depend on the spacing around `<` and `>`, so a
|
|
2842
|
+
// formatter pass that adds or removes a space can never move a line
|
|
2843
|
+
// between the two grammars — `Map < string, number > ()` earns the same
|
|
2844
|
+
// teaching diagnostic as `Map<string, number>()`. What stands in for the
|
|
2845
|
+
// adjacency is grammar evidence, on two axes. Every token inside the
|
|
2846
|
+
// angles must be one a type argument list can contain, so
|
|
2847
|
+
// `a < Limit and g > (c)` stays the pair of comparisons it is. And every
|
|
2848
|
+
// argument of a `,`-separated list must carry evidence of its own, so
|
|
2849
|
+
// `two(a < Limit, g > (c))` stays two ordinary arguments — a program that
|
|
2850
|
+
// works — while `mapValues<string, bool>([1])` is still claimed.
|
|
2786
2851
|
const knownGeneric = this.genericCallableNames.has(callableName);
|
|
2787
2852
|
const typeEvidence = (token) => {
|
|
2788
2853
|
if (token.kind === "question" || token.kind === "pipe" || token.kind === "arrow")
|
|
2789
2854
|
return true;
|
|
2790
|
-
|
|
2791
|
-
return false;
|
|
2792
|
-
if (["string", "number", "bool", "null", "unknown", "any", "List", "Set", "Map", "Record", "Promise", "Function", "Type", "readonly"].includes(token.value))
|
|
2793
|
-
return true;
|
|
2794
|
-
const first = token.value[0] ?? "";
|
|
2795
|
-
return first >= "A" && first <= "Z";
|
|
2855
|
+
return token.kind === "identifier" && isTypeEvidenceName(token.value);
|
|
2796
2856
|
};
|
|
2797
|
-
let
|
|
2857
|
+
let everyArgumentIsTyped = true;
|
|
2858
|
+
let argumentIsTyped = false;
|
|
2798
2859
|
let depth = 0;
|
|
2799
2860
|
for (let index = this.index; index < this.tokens.length; index += 1) {
|
|
2800
2861
|
const token = this.tokens[index];
|
|
@@ -2803,19 +2864,25 @@ export class Parser {
|
|
|
2803
2864
|
if (token.kind === "less") {
|
|
2804
2865
|
depth += 1;
|
|
2805
2866
|
if (depth >= 2)
|
|
2806
|
-
|
|
2867
|
+
argumentIsTyped = true;
|
|
2807
2868
|
}
|
|
2808
2869
|
else if (token.kind === "greater") {
|
|
2809
2870
|
depth -= 1;
|
|
2810
2871
|
if (depth === 0) {
|
|
2811
|
-
|
|
2812
|
-
if (call?.kind !== "leftParen" || call.span.start !== token.span.end)
|
|
2872
|
+
if (this.tokens[index + 1]?.kind !== "leftParen")
|
|
2813
2873
|
return null;
|
|
2814
|
-
return knownGeneric ||
|
|
2874
|
+
return knownGeneric || (everyArgumentIsTyped && argumentIsTyped) ? index : null;
|
|
2815
2875
|
}
|
|
2816
2876
|
}
|
|
2877
|
+
else if (!typeArgumentTokenKinds.has(token.kind)) {
|
|
2878
|
+
return null;
|
|
2879
|
+
}
|
|
2880
|
+
else if (token.kind === "comma" && depth === 1) {
|
|
2881
|
+
everyArgumentIsTyped &&= argumentIsTyped;
|
|
2882
|
+
argumentIsTyped = false;
|
|
2883
|
+
}
|
|
2817
2884
|
else if (typeEvidence(token)) {
|
|
2818
|
-
|
|
2885
|
+
argumentIsTyped = true;
|
|
2819
2886
|
}
|
|
2820
2887
|
}
|
|
2821
2888
|
return null;
|
|
@@ -2873,9 +2940,10 @@ export class Parser {
|
|
|
2873
2940
|
return { kind: "LiteralExpression", value: 0, raw: "0", span: token.span };
|
|
2874
2941
|
}
|
|
2875
2942
|
const value = Number(match[1]);
|
|
2943
|
+
const exact = Number.isFinite(value) && this.checkExactIntegerLiteral(match[1], writtenNumber(token).slice(0, -match[2].length), token.span);
|
|
2876
2944
|
if (!Number.isFinite(value))
|
|
2877
2945
|
this.diagnostics.push(diagnostic("VEL2017", "Numeric literals must be finite", token.span));
|
|
2878
|
-
const extensionExpression = this.parseExtensionNumericLiteral(token,
|
|
2946
|
+
const extensionExpression = this.parseExtensionNumericLiteral(token, exact ? value : 0, match[2]);
|
|
2879
2947
|
if (extensionExpression)
|
|
2880
2948
|
return extensionExpression;
|
|
2881
2949
|
this.diagnostics.push(diagnostic("VEL2002", `No compiler extension accepts numeric suffix '${match[2]}'`, token.span));
|
|
@@ -3164,15 +3232,40 @@ export class Parser {
|
|
|
3164
3232
|
}
|
|
3165
3233
|
numberLiteral(token, negative = false, literalSpan = token.span) {
|
|
3166
3234
|
const value = Number(token.value) * (negative ? -1 : 1);
|
|
3235
|
+
const exact = Number.isFinite(value) && this.checkExactIntegerLiteral(token.value, writtenNumber(token), literalSpan, negative);
|
|
3167
3236
|
if (!Number.isFinite(value))
|
|
3168
3237
|
this.diagnostics.push(diagnostic("VEL2017", "Numeric literals must be finite", literalSpan));
|
|
3169
3238
|
return {
|
|
3170
3239
|
kind: "LiteralExpression",
|
|
3171
|
-
value:
|
|
3240
|
+
value: exact ? value : 0,
|
|
3172
3241
|
raw: `${negative ? "-" : ""}${token.value}`,
|
|
3173
3242
|
span: literalSpan,
|
|
3174
3243
|
};
|
|
3175
3244
|
}
|
|
3245
|
+
/**
|
|
3246
|
+
* D90 R6: an integer literal whose value cannot be held exactly is rejected
|
|
3247
|
+
* rather than rounded, so `9007199254740993` is an error instead of silently
|
|
3248
|
+
* becoming `9007199254740992`. Only a literal *written* as an integer is one
|
|
3249
|
+
* here: a fraction part or an exponent spells a decimal value, which keeps
|
|
3250
|
+
* the ordinary nearest-value reading. An explicit radix takes the same test,
|
|
3251
|
+
* because a hex literal is written precisely when the exact bit pattern is
|
|
3252
|
+
* the point.
|
|
3253
|
+
*
|
|
3254
|
+
* The report quotes the author's own spelling — the token carries it when the
|
|
3255
|
+
* two differ — so a source line reading `1_000_000_000_000_000_000_1` is
|
|
3256
|
+
* quoted with its separators rather than as the normalized digits the value
|
|
3257
|
+
* was read from.
|
|
3258
|
+
*/
|
|
3259
|
+
checkExactIntegerLiteral(text, written, literalSpan, negative = false) {
|
|
3260
|
+
if (!/^(?:[0-9]+|0[xXbBoO][0-9a-fA-F]+)$/u.test(text))
|
|
3261
|
+
return true;
|
|
3262
|
+
const rounded = Number(text);
|
|
3263
|
+
if (BigInt(text) === BigInt(rounded))
|
|
3264
|
+
return true;
|
|
3265
|
+
const sign = negative ? "-" : "";
|
|
3266
|
+
this.diagnostics.push(diagnostic("VEL2017", `Numeric literals must be exactly representable; '${sign}${written}' becomes ${sign}${rounded}`, literalSpan));
|
|
3267
|
+
return false;
|
|
3268
|
+
}
|
|
3176
3269
|
// TXT-I2: the offset of a top-level ':' in an interpolation fragment, or
|
|
3177
3270
|
// null. Ternary colons are consumed by their pending '?', bracket and
|
|
3178
3271
|
// string contents never count, and '?.'/'??' are not ternary heads — so
|
|
@@ -3262,15 +3355,38 @@ export class Parser {
|
|
|
3262
3355
|
const mappedSpan = (local) => sourceOffsets
|
|
3263
3356
|
? span(sourceOffsets[local.start] ?? offset, sourceOffsets[local.end] ?? sourceOffsets.at(-1) ?? offset)
|
|
3264
3357
|
: span(local.start + offset, local.end + offset);
|
|
3358
|
+
// A report carries two coordinate systems: its own span and the spans of
|
|
3359
|
+
// the rewrite it names. Both are fragment-local here, so both are mapped;
|
|
3360
|
+
// mapping only the span leaves `velar fix` splicing an interpolation
|
|
3361
|
+
// offset into module text, which rewrites whatever happens to sit there.
|
|
3362
|
+
const mappedFix = (item) => item.fix
|
|
3363
|
+
? { ...item, fix: { ...item.fix, edits: item.fix.edits.map((edit) => ({ ...edit, span: mappedSpan(edit.span) })) } }
|
|
3364
|
+
: item;
|
|
3265
3365
|
const shiftedTokens = lexed.tokens.map((item) => ({ ...item, span: mappedSpan(item.span) }));
|
|
3266
|
-
const shiftedDiagnostics = lexed.diagnostics.map((item) => ({ ...item, span: mappedSpan(item.span) }));
|
|
3267
|
-
const
|
|
3366
|
+
const shiftedDiagnostics = lexed.diagnostics.map((item) => mappedFix({ ...item, span: mappedSpan(item.span) }));
|
|
3367
|
+
const shiftedAdvisories = lexed.advisories.map((item) => mappedFix({ ...item, span: mappedSpan(item.span) }));
|
|
3368
|
+
const nested = this.createNestedParser(shiftedTokens);
|
|
3369
|
+
nested.inheritParseBudget(this);
|
|
3370
|
+
const parsed = nested.parseExpressionFragment();
|
|
3268
3371
|
this.diagnostics.push(...shiftedDiagnostics, ...parsed.diagnostics);
|
|
3372
|
+
this.advisories.push(...shiftedAdvisories, ...parsed.advisories);
|
|
3269
3373
|
return parsed.expression;
|
|
3270
3374
|
}
|
|
3271
3375
|
createNestedParser(tokens) {
|
|
3272
3376
|
return new Parser(tokens, this.lexicalExtensions);
|
|
3273
3377
|
}
|
|
3378
|
+
/**
|
|
3379
|
+
* An interpolation is parsed by a nested parser, so the nest continues the
|
|
3380
|
+
* budget rather than restarting it: without this, `MAX_PARSE_DEPTH` resets
|
|
3381
|
+
* at every level and only a JavaScript stack overflow ends a deeply nested
|
|
3382
|
+
* f-string, after the superlinear work is already paid. It is a method
|
|
3383
|
+
* rather than a constructor argument because `packages/web` and
|
|
3384
|
+
* `packages/node` override `createNestedParser`, so an added argument would
|
|
3385
|
+
* be silently dropped for exactly the modules that build one.
|
|
3386
|
+
*/
|
|
3387
|
+
inheritParseBudget(parent) {
|
|
3388
|
+
this.parseDepth = parent.parseDepth + NESTED_EXPRESSION_PARSE_COST;
|
|
3389
|
+
}
|
|
3274
3390
|
withParseDepth(parse) {
|
|
3275
3391
|
this.parseDepth += 1;
|
|
3276
3392
|
if (this.parseDepth > MAX_PARSE_DEPTH) {
|
|
@@ -3510,16 +3626,16 @@ export class Parser {
|
|
|
3510
3626
|
return { kind, value: "", span: token.span };
|
|
3511
3627
|
}
|
|
3512
3628
|
checkTypeGreater() {
|
|
3513
|
-
return
|
|
3629
|
+
return typeGreaterRemainderKinds.has(this.current().kind) || this.check("greater");
|
|
3514
3630
|
}
|
|
3515
3631
|
/** Consume one `>` from a generic close without changing shift lexing. */
|
|
3516
3632
|
expectTypeGreater(message) {
|
|
3517
3633
|
if (this.check("greater"))
|
|
3518
3634
|
return this.advance();
|
|
3519
3635
|
const token = this.current();
|
|
3520
|
-
|
|
3636
|
+
const remainderKind = typeGreaterRemainderKinds.get(token.kind);
|
|
3637
|
+
if (remainderKind) {
|
|
3521
3638
|
const close = { kind: "greater", value: ">", span: span(token.span.start, token.span.start + 1) };
|
|
3522
|
-
const remainderKind = token.kind === "rightShift" ? "greater" : "rightShift";
|
|
3523
3639
|
this.tokens[this.index] = {
|
|
3524
3640
|
kind: remainderKind,
|
|
3525
3641
|
value: token.value.slice(1),
|