Skip to content

Commit 3ab89ab

Browse files
committed
Parse optional chaining and nullish coalescing
1 parent 024d9de commit 3ab89ab

15 files changed

+299
-73
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"ts-node": "^6.2.0",
2828
"tslint": "^5.20.0",
2929
"typedoc-plugin-external-module-name": "^2.1.0",
30-
"typescript": "^3.6.3",
30+
"typescript": "^3.7.0-beta",
3131
"webpack": "^4.40.2",
3232
"webpack-cli": "^3.3.9"
3333
},

src/ast.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export enum NodeKind {
5353
PARENTHESIZED,
5454
PROPERTYACCESS,
5555
TERNARY,
56+
COALESCE,
5657
SUPER,
5758
THIS,
5859
TRUE,
@@ -328,13 +329,15 @@ export abstract class Node {
328329
expression: Expression,
329330
typeArgs: TypeNode[] | null,
330331
args: Expression[],
332+
isOptionalChaining: bool,
331333
range: Range
332334
): CallExpression {
333335
var expr = new CallExpression();
334336
expr.range = range;
335337
expr.expression = expression;
336338
expr.typeArguments = typeArgs;
337339
expr.arguments = args;
340+
expr.isOptionalChaining = isOptionalChaining;
338341
return expr;
339342
}
340343

@@ -368,12 +371,14 @@ export abstract class Node {
368371
static createElementAccessExpression(
369372
expression: Expression,
370373
element: Expression,
374+
isOptionalChaining: bool,
371375
range: Range
372376
): ElementAccessExpression {
373377
var expr = new ElementAccessExpression();
374378
expr.range = range;
375379
expr.expression = expression;
376380
expr.elementExpression = element;
381+
expr.isOptionalChaining = isOptionalChaining;
377382
return expr;
378383
}
379384

@@ -448,6 +453,18 @@ export abstract class Node {
448453
return expr;
449454
}
450455

456+
static createCoalesceExpression(
457+
ifThen: Expression,
458+
ifElse: Expression,
459+
range: Range
460+
): CoalesceExpression {
461+
var expr = new CoalesceExpression();
462+
expr.range = range;
463+
expr.ifThen = ifThen;
464+
expr.ifElse = ifElse;
465+
return expr;
466+
}
467+
451468
static createObjectLiteralExpression(
452469
names: IdentifierExpression[],
453470
values: Expression[],
@@ -473,12 +490,14 @@ export abstract class Node {
473490
static createPropertyAccessExpression(
474491
expression: Expression,
475492
property: IdentifierExpression,
493+
isOptionalChaining: bool,
476494
range: Range
477495
): PropertyAccessExpression {
478496
var expr = new PropertyAccessExpression();
479497
expr.range = range;
480498
expr.expression = expression;
481499
expr.property = property;
500+
expr.isOptionalChaining = isOptionalChaining;
482501
return expr;
483502
}
484503

@@ -1387,6 +1406,8 @@ export class CallExpression extends Expression {
13871406
typeArguments: TypeNode[] | null;
13881407
/** Provided arguments. */
13891408
arguments: Expression[];
1409+
/** Whether optional chaining is used between the expression and the signature. */
1410+
isOptionalChaining: bool;
13901411

13911412
/** Gets the type arguments range for reporting. */
13921413
get typeArgumentsRange(): Range {
@@ -1417,6 +1438,16 @@ export class ClassExpression extends Expression {
14171438
declaration: ClassDeclaration;
14181439
}
14191440

1441+
/** Represents a nullish coalescing expression. */
1442+
export class CoalesceExpression extends Expression {
1443+
kind = NodeKind.COALESCE;
1444+
1445+
/** Expression head checked for being nullish. */
1446+
ifThen: Expression;
1447+
/** Expression executed when `ifThen` is nullish. */
1448+
ifElse: Expression;
1449+
}
1450+
14201451
/** Represents a comma expression composed of multiple expressions. */
14211452
export class CommaExpression extends Expression {
14221453
kind = NodeKind.COMMA;
@@ -1440,6 +1471,8 @@ export class ElementAccessExpression extends Expression {
14401471
expression: Expression;
14411472
/** Element of the expression being accessed. */
14421473
elementExpression: Expression;
1474+
/** Whether optional chaining is used between the expression and the access. */
1475+
isOptionalChaining: bool;
14431476
}
14441477

14451478
/** Represents a float literal expression. */
@@ -1514,6 +1547,8 @@ export class PropertyAccessExpression extends Expression {
15141547
expression: Expression;
15151548
/** Property of the expression being accessed. */
15161549
property: IdentifierExpression;
1550+
/** Whether optional chaining is used between the expression and the property. */
1551+
isOptionalChaining: bool;
15171552
}
15181553

15191554
/** Represents a regular expression literal expression. */

src/common.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,11 @@ export enum CommonFlags {
7070
TRAMPOLINE = 1 << 25,
7171
/** Is a virtual method. */
7272
VIRTUAL = 1 << 26,
73-
/** Is the main function. */
74-
MAIN = 1 << 27,
7573

7674
// Other
7775

7876
/** Is quoted. */
79-
QUOTED = 1 << 28
77+
QUOTED = 1 << 27
8078
}
8179

8280
/** Path delimiter inserted between file system levels. */
@@ -133,6 +131,7 @@ export namespace CommonSymbols {
133131
export const void_ = "void";
134132
export const number = "number";
135133
export const boolean = "boolean";
134+
export const auto = "auto";
136135
export const string = "string";
137136
export const native = "native";
138137
export const indexof = "indexof";

src/compiler.ts

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ import {
106106
Range,
107107
DecoratorKind,
108108
AssertionKind,
109+
SourceKind,
109110

110111
Statement,
111112
BlockStatement,
@@ -135,6 +136,7 @@ import {
135136
WhileStatement,
136137

137138
Expression,
139+
ExportDefaultStatement,
138140
AssertionExpression,
139141
BinaryExpression,
140142
CallExpression,
@@ -151,16 +153,15 @@ import {
151153
ParenthesizedExpression,
152154
PropertyAccessExpression,
153155
TernaryExpression,
156+
CoalesceExpression,
154157
ArrayLiteralExpression,
155158
StringLiteralExpression,
156159
UnaryPostfixExpression,
157160
UnaryPrefixExpression,
158161

159162
nodeIsConstantValue,
160163
findDecorator,
161-
isTypeOmitted,
162-
ExportDefaultStatement,
163-
SourceKind
164+
isTypeOmitted
164165
} from "./ast";
165166

166167
import {
@@ -1146,7 +1147,7 @@ export class Compiler extends DiagnosticEmitter {
11461147
assert(instance.prototype.arrowKind);
11471148

11481149
// none of the following can be an arrow function
1149-
assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET | CommonFlags.MAIN));
1150+
assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET));
11501151

11511152
let expr = this.compileExpression((<ExpressionStatement>bodyNode).expression, returnType,
11521153
Constraints.CONV_IMPLICIT
@@ -2849,6 +2850,10 @@ export class Compiler extends DiagnosticEmitter {
28492850
expr = this.compileTernaryExpression(<TernaryExpression>expression, contextualType, constraints);
28502851
break;
28512852
}
2853+
case NodeKind.COALESCE: {
2854+
expr = this.compileCoalesceExpression(<CoalesceExpression>expression, contextualType, constraints);
2855+
break;
2856+
}
28522857
case NodeKind.UNARYPOSTFIX: {
28532858
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, contextualType, constraints);
28542859
break;
@@ -2892,9 +2897,13 @@ export class Compiler extends DiagnosticEmitter {
28922897
contextualType: Type,
28932898
constraints: Constraints = Constraints.NONE
28942899
): ExpressionRef {
2895-
return this.module.precomputeExpression(
2896-
this.compileExpression(expression, contextualType, constraints)
2897-
);
2900+
var orig = this.compileExpression(expression, contextualType, constraints);
2901+
var expr = this.module.precomputeExpression(orig);
2902+
if (orig != expr) {
2903+
let skippedAutoreleases = this.skippedAutoreleases;
2904+
if (skippedAutoreleases.has(orig)) skippedAutoreleases.add(expr);
2905+
}
2906+
return expr;
28982907
}
28992908

29002909
convertExpression(
@@ -5734,9 +5743,41 @@ export class Compiler extends DiagnosticEmitter {
57345743

57355744
var module = this.module;
57365745
var flow = this.currentFlow;
5746+
var targetExpression = expression.expression;
5747+
5748+
// TODO: In these cases we compile the target of an element or property
5749+
// access, but not the element or property access itself, essentially
5750+
// skipping over optional chaining.
5751+
switch (targetExpression.kind) {
5752+
case NodeKind.ELEMENTACCESS: {
5753+
if ((<ElementAccessExpression>targetExpression).isOptionalChaining) {
5754+
this.error(
5755+
DiagnosticCode.Not_implemented,
5756+
(<ElementAccessExpression>targetExpression).expression.range.atEnd
5757+
);
5758+
}
5759+
break;
5760+
}
5761+
case NodeKind.PROPERTYACCESS: {
5762+
if ((<PropertyAccessExpression>targetExpression).isOptionalChaining) {
5763+
this.error(
5764+
DiagnosticCode.Not_implemented,
5765+
(<PropertyAccessExpression>targetExpression).expression.range.atEnd
5766+
);
5767+
}
5768+
break;
5769+
}
5770+
}
5771+
// TODO
5772+
if (expression.isOptionalChaining) {
5773+
this.error(
5774+
DiagnosticCode.Not_implemented,
5775+
targetExpression.range.atEnd
5776+
);
5777+
}
57375778

57385779
// handle call to super
5739-
if (expression.expression.kind == NodeKind.SUPER) {
5780+
if (targetExpression.kind == NodeKind.SUPER) {
57405781
let flow = this.currentFlow;
57415782
let actualFunction = flow.actualFunction;
57425783
if (!actualFunction.is(CommonFlags.CONSTRUCTOR)) {
@@ -5793,7 +5834,7 @@ export class Compiler extends DiagnosticEmitter {
57935834
}
57945835

57955836
// otherwise resolve normally
5796-
var target = this.resolver.lookupExpression(expression.expression, flow); // reports
5837+
var target = this.resolver.lookupExpression(targetExpression, flow); // reports
57975838
if (!target) return module.unreachable();
57985839

57995840
var signature: Signature | null;
@@ -5817,7 +5858,7 @@ export class Compiler extends DiagnosticEmitter {
58175858
if (!prototype.is(CommonFlags.GENERIC)) {
58185859
this.error(
58195860
DiagnosticCode.Type_0_is_not_generic,
5820-
expression.expression.range, prototype.internalName
5861+
targetExpression.range, prototype.internalName
58215862
);
58225863
return module.unreachable();
58235864
}
@@ -5879,7 +5920,7 @@ export class Compiler extends DiagnosticEmitter {
58795920
// invalid because the type is effectively unknown inside the function body
58805921
this.error(
58815922
DiagnosticCode.Type_argument_expected,
5882-
expression.expression.range.atEnd
5923+
targetExpression.range.atEnd
58835924
);
58845925
return this.module.unreachable();
58855926
}
@@ -5962,15 +6003,15 @@ export class Compiler extends DiagnosticEmitter {
59626003
}
59636004
case ElementKind.FUNCTION_TARGET: {
59646005
signature = (<FunctionTarget>target).signature;
5965-
indexArg = this.compileExpression(expression.expression, (<FunctionTarget>target).type, Constraints.CONV_IMPLICIT);
6006+
indexArg = this.compileExpression(targetExpression, (<FunctionTarget>target).type, Constraints.CONV_IMPLICIT);
59666007
break;
59676008
}
59686009

59696010
case ElementKind.PROPERTY_PROTOTYPE: { // static property
59706011
let getterPrototype = assert((<PropertyPrototype>target).getterPrototype);
59716012
let getterInstance = this.resolver.resolveFunction(getterPrototype, null);
59726013
if (!getterInstance) return module.unreachable();
5973-
indexArg = this.compileCallDirect(getterInstance, [], expression.expression);
6014+
indexArg = this.compileCallDirect(getterInstance, [], targetExpression);
59746015
signature = this.currentType.signatureReference;
59756016
if (!signature) {
59766017
this.error(
@@ -5983,7 +6024,7 @@ export class Compiler extends DiagnosticEmitter {
59836024
}
59846025
case ElementKind.PROPERTY: { // instance property
59856026
let getterInstance = assert((<Property>target).getterInstance);
5986-
indexArg = this.compileCallDirect(getterInstance, [], expression.expression,
6027+
indexArg = this.compileCallDirect(getterInstance, [], targetExpression,
59876028
this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType)
59886029
);
59896030
signature = this.currentType.signatureReference;
@@ -6948,6 +6989,14 @@ export class Compiler extends DiagnosticEmitter {
69486989
contextualType: Type,
69496990
constraints: Constraints
69506991
): ExpressionRef {
6992+
6993+
if (expression.isOptionalChaining) {
6994+
this.error(
6995+
DiagnosticCode.Not_implemented,
6996+
expression.expression.range.atEnd
6997+
);
6998+
}
6999+
69517000
var module = this.module;
69527001
var targetExpression = expression.expression;
69537002
var targetType = this.resolver.resolveExpression(targetExpression, this.currentFlow); // reports
@@ -7951,6 +8000,14 @@ export class Compiler extends DiagnosticEmitter {
79518000
ctxType: Type,
79528001
constraints: Constraints
79538002
): ExpressionRef {
8003+
8004+
if (expression.isOptionalChaining) {
8005+
this.error(
8006+
DiagnosticCode.Not_implemented,
8007+
expression.expression.range.atEnd
8008+
);
8009+
}
8010+
79548011
var module = this.module;
79558012
var flow = this.currentFlow;
79568013

@@ -8138,6 +8195,18 @@ export class Compiler extends DiagnosticEmitter {
81388195
return expr;
81398196
}
81408197

8198+
compileCoalesceExpression(
8199+
expression: CoalesceExpression,
8200+
ctxType: Type,
8201+
constraints: Constraints
8202+
): ExpressionRef {
8203+
this.error(
8204+
DiagnosticCode.Not_implemented,
8205+
expression.range
8206+
);
8207+
return this.module.unreachable();
8208+
}
8209+
81418210
compileUnaryPostfixExpression(
81428211
expression: UnaryPostfixExpression,
81438212
contextualType: Type,

0 commit comments

Comments
 (0)