@velarscript/compiler 0.11.1 → 0.12.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/analyzer.d.ts +26 -19
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +110 -192
- package/dist/analyzer.js.map +1 -1
- package/dist/application-package-host.d.ts +1 -7
- package/dist/application-package-host.d.ts.map +1 -1
- package/dist/application-package-host.js +1 -1
- package/dist/ast.d.ts +24 -5
- package/dist/ast.d.ts.map +1 -1
- package/dist/ast.js +3 -0
- package/dist/ast.js.map +1 -1
- package/dist/core-vocabulary.d.ts +2 -2
- package/dist/emitter.d.ts +7 -0
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +56 -0
- package/dist/emitter.js.map +1 -1
- package/dist/extension.d.ts +2 -0
- package/dist/extension.d.ts.map +1 -1
- package/dist/formatter.js +11 -0
- package/dist/formatter.js.map +1 -1
- package/dist/framework-host.d.ts +0 -1
- package/dist/framework-host.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lexer.d.ts.map +1 -1
- package/dist/lexer.js +17 -10
- package/dist/lexer.js.map +1 -1
- package/dist/parser.d.ts +15 -0
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +73 -20
- package/dist/parser.js.map +1 -1
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +3 -0
- package/dist/semantic.js.map +1 -1
- package/dist/token.d.ts +8 -1
- package/dist/token.d.ts.map +1 -1
- package/dist/token.js.map +1 -1
- package/package.json +1 -1
package/dist/parser.js
CHANGED
|
@@ -162,9 +162,7 @@ export class Parser {
|
|
|
162
162
|
return expression;
|
|
163
163
|
const operatorToken = this.advance();
|
|
164
164
|
this.diagnostics.push(recoveredDiagnostic("VEL2028", "Assignment is a statement, not an expression; write it on its own line inside a function, action, or handler body", operatorToken.span));
|
|
165
|
-
|
|
166
|
-
this.diagnostics.push(diagnostic("VEL2005", "Assignment target must be a name, member, or index", expression.span));
|
|
167
|
-
}
|
|
165
|
+
this.reportInvalidAssignmentTarget(expression);
|
|
168
166
|
const value = this.recoverExpressionAssignment(this.parseExpression());
|
|
169
167
|
return { kind: "AssignmentExpression", target: expression, operator, value, span: span(expression.span.start, value.span.end) };
|
|
170
168
|
}
|
|
@@ -445,9 +443,7 @@ export class Parser {
|
|
|
445
443
|
const operator = assignmentOperators[this.current().kind];
|
|
446
444
|
if (operator) {
|
|
447
445
|
this.advance();
|
|
448
|
-
|
|
449
|
-
this.diagnostics.push(diagnostic("VEL2005", "Assignment target must be a name, member, or index", expression.span));
|
|
450
|
-
}
|
|
446
|
+
this.reportInvalidAssignmentTarget(expression);
|
|
451
447
|
const value = this.parseExpression();
|
|
452
448
|
return {
|
|
453
449
|
kind: "AssignmentStatement",
|
|
@@ -1512,18 +1508,16 @@ export class Parser {
|
|
|
1512
1508
|
this.consumeNewlines();
|
|
1513
1509
|
while (!this.check("dedent") && !this.check("eof")) {
|
|
1514
1510
|
const methodStart = this.current().span.start;
|
|
1515
|
-
//
|
|
1516
|
-
//
|
|
1517
|
-
//
|
|
1518
|
-
// and validated on one path because they are one idea — a question the
|
|
1519
|
-
// language asks the type, answered in a block that is not a method.
|
|
1511
|
+
// `@` always selects the contextual compiler namespace. In a class that
|
|
1512
|
+
// closed namespace contains `dispose` and `iterate`; their behavior
|
|
1513
|
+
// differs, but their resolution, rejection, and collision rules do not.
|
|
1520
1514
|
if (this.check("at")) {
|
|
1521
1515
|
const marker = this.advance();
|
|
1522
|
-
const memberName = this.expect("identifier", "Expected a compiler-
|
|
1516
|
+
const memberName = this.expect("identifier", "Expected a compiler-owned class name after '@'");
|
|
1523
1517
|
const keywordSpan = span(marker.span.start, memberName.span.end);
|
|
1524
1518
|
const known = memberName.value === "dispose" || memberName.value === "iterate";
|
|
1525
1519
|
if (!known) {
|
|
1526
|
-
this.diagnostics.push(diagnostic("VEL2022", `Unknown
|
|
1520
|
+
this.diagnostics.push(diagnostic("VEL2022", `Unknown compiler-owned name '@${memberName.value}' in a class; the class namespace contains only '@dispose:' and '@iterate:'`, keywordSpan));
|
|
1527
1521
|
}
|
|
1528
1522
|
const body = this.parseBlock();
|
|
1529
1523
|
const blockSpan = span(methodStart, body.at(-1)?.span.end ?? this.previous().span.end);
|
|
@@ -2590,6 +2584,17 @@ export class Parser {
|
|
|
2590
2584
|
this.advance();
|
|
2591
2585
|
return this.withParseDepth(() => this.parseUnary());
|
|
2592
2586
|
}
|
|
2587
|
+
if (this.match("bang")) {
|
|
2588
|
+
// D86 rule 212: a `!` reached here stands before its operand, so it is
|
|
2589
|
+
// the JavaScript negation, not the required-value unwrap the postfix
|
|
2590
|
+
// loop reads. D54 rule 118 keeps that reading a teaching diagnostic.
|
|
2591
|
+
this.reportPrefixBang(this.previous());
|
|
2592
|
+
const operator = this.previous();
|
|
2593
|
+
return this.withParseDepth(() => {
|
|
2594
|
+
const operand = this.parseUnary();
|
|
2595
|
+
return { kind: "UnaryExpression", operator: "not", operand, span: span(operator.span.start, operand.span.end) };
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2593
2598
|
if (this.match("not") || this.match("plus") || this.match("minus") || this.match("tilde")) {
|
|
2594
2599
|
const operator = this.previous();
|
|
2595
2600
|
return this.withParseDepth(() => {
|
|
@@ -2635,7 +2640,7 @@ export class Parser {
|
|
|
2635
2640
|
return this.parsePostfix();
|
|
2636
2641
|
const operator = this.previous();
|
|
2637
2642
|
return this.withParseDepth(() => {
|
|
2638
|
-
const operand = this.check("not") || this.check("plus") || this.check("minus") || this.check("tilde")
|
|
2643
|
+
const operand = this.check("not") || this.check("bang") || this.check("plus") || this.check("minus") || this.check("tilde")
|
|
2639
2644
|
? this.parseUnary()
|
|
2640
2645
|
: this.parsePowerBase();
|
|
2641
2646
|
return {
|
|
@@ -2648,6 +2653,7 @@ export class Parser {
|
|
|
2648
2653
|
}
|
|
2649
2654
|
parsePostfix() {
|
|
2650
2655
|
let expression = this.parsePrimary();
|
|
2656
|
+
let typeArgumentsRemoved = false;
|
|
2651
2657
|
while (true) {
|
|
2652
2658
|
const explicitTypeArgumentsEnd = this.explicitTypeArgumentsEnd(expression);
|
|
2653
2659
|
if (explicitTypeArgumentsEnd !== null) {
|
|
@@ -2657,7 +2663,26 @@ export class Parser {
|
|
|
2657
2663
|
const name = expression.kind === "IdentifierExpression" ? expression.name
|
|
2658
2664
|
: expression.kind === "MemberExpression" ? expression.property
|
|
2659
2665
|
: "function";
|
|
2660
|
-
|
|
2666
|
+
// D85 rule 207: an empty `Set<string>()` has no argument to infer from,
|
|
2667
|
+
// so "remove the type arguments" alone would leave the author with
|
|
2668
|
+
// code the analyzer rejects. Name where the type belongs instead, and
|
|
2669
|
+
// withhold the mechanical fix that would not reach working source.
|
|
2670
|
+
const emptyCollection = (name === "Set" || name === "Map")
|
|
2671
|
+
&& this.check("leftParen") && this.peekKind(1) === "rightParen";
|
|
2672
|
+
this.diagnostics.push(recoveredDiagnostic("VEL2031", emptyCollection
|
|
2673
|
+
? `Type arguments are inferred at each call site; an empty '${name}()' takes its type from the binding — write 'const values: ${name === "Set" ? "Set<string>" : "Map<string, number>"} = ${name}()'`
|
|
2674
|
+
: `Type arguments are inferred at each call site; write '${name}(...)' without '<...>'`, span(start, this.previous().span.end), ...(emptyCollection
|
|
2675
|
+
? []
|
|
2676
|
+
: [mechanicalFix(span(start, this.previous().span.end), "", "Remove the explicit type arguments")])));
|
|
2677
|
+
typeArgumentsRemoved = true;
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
// D86 rule 212: a `!` that follows an operand is the required-value
|
|
2681
|
+
// unwrap. It binds with the rest of the postfix chain, so `a!.b` unwraps
|
|
2682
|
+
// `a` and then reads `b`, and `a.b!` unwraps the field.
|
|
2683
|
+
if (this.match("bang")) {
|
|
2684
|
+
const bang = this.previous();
|
|
2685
|
+
expression = { kind: "RequiredExpression", value: expression, span: span(expression.span.start, bang.span.end) };
|
|
2661
2686
|
continue;
|
|
2662
2687
|
}
|
|
2663
2688
|
let call = false;
|
|
@@ -2717,8 +2742,10 @@ export class Parser {
|
|
|
2717
2742
|
arguments: arguments_,
|
|
2718
2743
|
...(sawNamed ? { argumentNames } : {}),
|
|
2719
2744
|
optional: optionalCall,
|
|
2745
|
+
...(typeArgumentsRemoved ? { typeArgumentsRemoved: true } : {}),
|
|
2720
2746
|
span: span(expression.span.start, close.span.end),
|
|
2721
2747
|
};
|
|
2748
|
+
typeArgumentsRemoved = false;
|
|
2722
2749
|
continue;
|
|
2723
2750
|
}
|
|
2724
2751
|
if (this.check("optionalDot") && this.peekKind(1) === "leftBracket") {
|
|
@@ -3031,13 +3058,12 @@ export class Parser {
|
|
|
3031
3058
|
}
|
|
3032
3059
|
return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
|
|
3033
3060
|
}
|
|
3034
|
-
//
|
|
3035
|
-
//
|
|
3036
|
-
//
|
|
3037
|
-
// receiving a bare 'Expected an expression'.
|
|
3061
|
+
// `@name` is resolved only by a compiler-owned syntax context. Here it
|
|
3062
|
+
// cannot become an expression or runtime value, so name the namespace
|
|
3063
|
+
// rule instead of reporting a bare 'Expected an expression'.
|
|
3038
3064
|
if (token.kind === "at") {
|
|
3039
3065
|
const name = this.check("identifier") ? this.advance().value : "";
|
|
3040
|
-
this.diagnostics.push(diagnostic("VEL2002", `'@${name}'
|
|
3066
|
+
this.diagnostics.push(diagnostic("VEL2002", `'@${name}' is a compiler-owned contextual name and is not valid here; use it only in a context that defines it, such as a component's '@mounted:' block`, span(token.span.start, this.previous().span.end)));
|
|
3041
3067
|
this.skipMistypedDeclaration();
|
|
3042
3068
|
return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
|
|
3043
3069
|
}
|
|
@@ -3593,6 +3619,33 @@ export class Parser {
|
|
|
3593
3619
|
}
|
|
3594
3620
|
return token;
|
|
3595
3621
|
}
|
|
3622
|
+
/**
|
|
3623
|
+
* D54 rule 118 keeps prefix `!` a teaching diagnostic rather than a second
|
|
3624
|
+
* spelling of `not`; D86 rule 212 moved the report here because only the
|
|
3625
|
+
* parser knows the `!` stood before its operand. The rewrite carries the
|
|
3626
|
+
* spacing the word form needs, exactly as the lexer's own word-operator
|
|
3627
|
+
* fixes do: `!ready` becomes `not ready`, `and!ready` becomes `and not ready`.
|
|
3628
|
+
*/
|
|
3629
|
+
/**
|
|
3630
|
+
* D86 rule 212: `value! = next` names the unwrap on the left of a write. The
|
|
3631
|
+
* unwrap reads a value and proves it present; a write has neither a result
|
|
3632
|
+
* to unwrap nor a fact to prove, and assigning `null` back into an optional
|
|
3633
|
+
* is legitimate — so the target is the location itself.
|
|
3634
|
+
*/
|
|
3635
|
+
reportInvalidAssignmentTarget(expression) {
|
|
3636
|
+
if (expression.kind === "IdentifierExpression" || expression.kind === "MemberExpression" || expression.kind === "IndexExpression")
|
|
3637
|
+
return;
|
|
3638
|
+
if (expression.kind === "RequiredExpression") {
|
|
3639
|
+
this.diagnostics.push(diagnostic("VEL2005", "'!' unwraps a value that is read, so it cannot stand on an assignment target; assign to the location itself", expression.span, mechanicalFix(span(expression.span.end - 1, expression.span.end), "", "Remove the '!'")));
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3642
|
+
this.diagnostics.push(diagnostic("VEL2005", "Assignment target must be a name, member, or index", expression.span));
|
|
3643
|
+
}
|
|
3644
|
+
reportPrefixBang(bang) {
|
|
3645
|
+
const spaceBefore = this.tokens[this.index - 2]?.span.end === bang.span.start ? " " : "";
|
|
3646
|
+
const spaceAfter = this.current().span.start === bang.span.end ? " " : "";
|
|
3647
|
+
this.diagnostics.push(recoveredDiagnostic("VEL1005", "Use 'not'; VelarScript uses readable logical operators", bang.span, mechanicalFix(bang.span, `${spaceBefore}not${spaceAfter}`, "Use readable 'not'")));
|
|
3648
|
+
}
|
|
3596
3649
|
current() {
|
|
3597
3650
|
return this.tokens[this.index] ?? this.tokens[this.tokens.length - 1];
|
|
3598
3651
|
}
|