Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse optional chaining and nullish coalescing #884

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -27,7 +27,7 @@
"ts-node": "^6.2.0",
"tslint": "^5.20.0",
"typedoc-plugin-external-module-name": "^2.1.0",
"typescript": "^3.6.3",
"typescript": "^3.7.0-beta",
"webpack": "^4.40.2",
"webpack-cli": "^3.3.9"
},
Expand Down
35 changes: 35 additions & 0 deletions src/ast.ts
Expand Up @@ -53,6 +53,7 @@ export enum NodeKind {
PARENTHESIZED,
PROPERTYACCESS,
TERNARY,
COALESCE,
SUPER,
THIS,
TRUE,
Expand Down Expand Up @@ -328,13 +329,15 @@ export abstract class Node {
expression: Expression,
typeArgs: TypeNode[] | null,
args: Expression[],
isOptionalChaining: bool,
range: Range
): CallExpression {
var expr = new CallExpression();
expr.range = range;
expr.expression = expression;
expr.typeArguments = typeArgs;
expr.arguments = args;
expr.isOptionalChaining = isOptionalChaining;
return expr;
}

Expand Down Expand Up @@ -368,12 +371,14 @@ export abstract class Node {
static createElementAccessExpression(
expression: Expression,
element: Expression,
isOptionalChaining: bool,
range: Range
): ElementAccessExpression {
var expr = new ElementAccessExpression();
expr.range = range;
expr.expression = expression;
expr.elementExpression = element;
expr.isOptionalChaining = isOptionalChaining;
return expr;
}

Expand Down Expand Up @@ -448,6 +453,18 @@ export abstract class Node {
return expr;
}

static createCoalesceExpression(
ifThen: Expression,
ifElse: Expression,
range: Range
): CoalesceExpression {
var expr = new CoalesceExpression();
expr.range = range;
expr.ifThen = ifThen;
expr.ifElse = ifElse;
return expr;
}

static createObjectLiteralExpression(
names: IdentifierExpression[],
values: Expression[],
Expand All @@ -473,12 +490,14 @@ export abstract class Node {
static createPropertyAccessExpression(
expression: Expression,
property: IdentifierExpression,
isOptionalChaining: bool,
range: Range
): PropertyAccessExpression {
var expr = new PropertyAccessExpression();
expr.range = range;
expr.expression = expression;
expr.property = property;
expr.isOptionalChaining = isOptionalChaining;
return expr;
}

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

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

/** Represents a nullish coalescing expression. */
export class CoalesceExpression extends Expression {
kind = NodeKind.COALESCE;

/** Expression head checked for being nullish. */
ifThen: Expression;
/** Expression executed when `ifThen` is nullish. */
ifElse: Expression;
}

/** Represents a comma expression composed of multiple expressions. */
export class CommaExpression extends Expression {
kind = NodeKind.COMMA;
Expand All @@ -1440,6 +1471,8 @@ export class ElementAccessExpression extends Expression {
expression: Expression;
/** Element of the expression being accessed. */
elementExpression: Expression;
/** Whether optional chaining is used between the expression and the access. */
isOptionalChaining: bool;
}

/** Represents a float literal expression. */
Expand Down Expand Up @@ -1514,6 +1547,8 @@ export class PropertyAccessExpression extends Expression {
expression: Expression;
/** Property of the expression being accessed. */
property: IdentifierExpression;
/** Whether optional chaining is used between the expression and the property. */
isOptionalChaining: bool;
}

/** Represents a regular expression literal expression. */
Expand Down
5 changes: 2 additions & 3 deletions src/common.ts
Expand Up @@ -70,13 +70,11 @@ export enum CommonFlags {
TRAMPOLINE = 1 << 25,
/** Is a virtual method. */
VIRTUAL = 1 << 26,
/** Is the main function. */
MAIN = 1 << 27,

// Other

/** Is quoted. */
QUOTED = 1 << 28
QUOTED = 1 << 27
}

/** Path delimiter inserted between file system levels. */
Expand Down Expand Up @@ -133,6 +131,7 @@ export namespace CommonSymbols {
export const void_ = "void";
export const number = "number";
export const boolean = "boolean";
export const auto = "auto";
export const string = "string";
export const native = "native";
export const indexof = "indexof";
Expand Down
97 changes: 83 additions & 14 deletions src/compiler.ts
Expand Up @@ -106,6 +106,7 @@ import {
Range,
DecoratorKind,
AssertionKind,
SourceKind,

Statement,
BlockStatement,
Expand Down Expand Up @@ -135,6 +136,7 @@ import {
WhileStatement,

Expression,
ExportDefaultStatement,
AssertionExpression,
BinaryExpression,
CallExpression,
Expand All @@ -151,16 +153,15 @@ import {
ParenthesizedExpression,
PropertyAccessExpression,
TernaryExpression,
CoalesceExpression,
ArrayLiteralExpression,
StringLiteralExpression,
UnaryPostfixExpression,
UnaryPrefixExpression,

nodeIsConstantValue,
findDecorator,
isTypeOmitted,
ExportDefaultStatement,
SourceKind
isTypeOmitted
} from "./ast";

import {
Expand Down Expand Up @@ -1146,7 +1147,7 @@ export class Compiler extends DiagnosticEmitter {
assert(instance.prototype.arrowKind);

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

let expr = this.compileExpression((<ExpressionStatement>bodyNode).expression, returnType,
Constraints.CONV_IMPLICIT
Expand Down Expand Up @@ -2849,6 +2850,10 @@ export class Compiler extends DiagnosticEmitter {
expr = this.compileTernaryExpression(<TernaryExpression>expression, contextualType, constraints);
break;
}
case NodeKind.COALESCE: {
expr = this.compileCoalesceExpression(<CoalesceExpression>expression, contextualType, constraints);
break;
}
case NodeKind.UNARYPOSTFIX: {
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, contextualType, constraints);
break;
Expand Down Expand Up @@ -2892,9 +2897,13 @@ export class Compiler extends DiagnosticEmitter {
contextualType: Type,
constraints: Constraints = Constraints.NONE
): ExpressionRef {
return this.module.precomputeExpression(
this.compileExpression(expression, contextualType, constraints)
);
var orig = this.compileExpression(expression, contextualType, constraints);
var expr = this.module.precomputeExpression(orig);
if (orig != expr) {
let skippedAutoreleases = this.skippedAutoreleases;
if (skippedAutoreleases.has(orig)) skippedAutoreleases.add(expr);
}
return expr;
}

convertExpression(
Expand Down Expand Up @@ -5734,9 +5743,41 @@ export class Compiler extends DiagnosticEmitter {

var module = this.module;
var flow = this.currentFlow;
var targetExpression = expression.expression;

// TODO: In these cases we compile the target of an element or property
// access, but not the element or property access itself, essentially
// skipping over optional chaining.
switch (targetExpression.kind) {
case NodeKind.ELEMENTACCESS: {
if ((<ElementAccessExpression>targetExpression).isOptionalChaining) {
this.error(
DiagnosticCode.Not_implemented,
(<ElementAccessExpression>targetExpression).expression.range.atEnd
);
}
break;
}
case NodeKind.PROPERTYACCESS: {
if ((<PropertyAccessExpression>targetExpression).isOptionalChaining) {
this.error(
DiagnosticCode.Not_implemented,
(<PropertyAccessExpression>targetExpression).expression.range.atEnd
);
}
break;
}
}
// TODO
if (expression.isOptionalChaining) {
this.error(
DiagnosticCode.Not_implemented,
targetExpression.range.atEnd
);
}

// handle call to super
if (expression.expression.kind == NodeKind.SUPER) {
if (targetExpression.kind == NodeKind.SUPER) {
let flow = this.currentFlow;
let actualFunction = flow.actualFunction;
if (!actualFunction.is(CommonFlags.CONSTRUCTOR)) {
Expand Down Expand Up @@ -5793,7 +5834,7 @@ export class Compiler extends DiagnosticEmitter {
}

// otherwise resolve normally
var target = this.resolver.lookupExpression(expression.expression, flow); // reports
var target = this.resolver.lookupExpression(targetExpression, flow); // reports
if (!target) return module.unreachable();

var signature: Signature | null;
Expand All @@ -5817,7 +5858,7 @@ export class Compiler extends DiagnosticEmitter {
if (!prototype.is(CommonFlags.GENERIC)) {
this.error(
DiagnosticCode.Type_0_is_not_generic,
expression.expression.range, prototype.internalName
targetExpression.range, prototype.internalName
);
return module.unreachable();
}
Expand Down Expand Up @@ -5879,7 +5920,7 @@ export class Compiler extends DiagnosticEmitter {
// invalid because the type is effectively unknown inside the function body
this.error(
DiagnosticCode.Type_argument_expected,
expression.expression.range.atEnd
targetExpression.range.atEnd
);
return this.module.unreachable();
}
Expand Down Expand Up @@ -5962,15 +6003,15 @@ export class Compiler extends DiagnosticEmitter {
}
case ElementKind.FUNCTION_TARGET: {
signature = (<FunctionTarget>target).signature;
indexArg = this.compileExpression(expression.expression, (<FunctionTarget>target).type, Constraints.CONV_IMPLICIT);
indexArg = this.compileExpression(targetExpression, (<FunctionTarget>target).type, Constraints.CONV_IMPLICIT);
break;
}

case ElementKind.PROPERTY_PROTOTYPE: { // static property
let getterPrototype = assert((<PropertyPrototype>target).getterPrototype);
let getterInstance = this.resolver.resolveFunction(getterPrototype, null);
if (!getterInstance) return module.unreachable();
indexArg = this.compileCallDirect(getterInstance, [], expression.expression);
indexArg = this.compileCallDirect(getterInstance, [], targetExpression);
signature = this.currentType.signatureReference;
if (!signature) {
this.error(
Expand All @@ -5983,7 +6024,7 @@ export class Compiler extends DiagnosticEmitter {
}
case ElementKind.PROPERTY: { // instance property
let getterInstance = assert((<Property>target).getterInstance);
indexArg = this.compileCallDirect(getterInstance, [], expression.expression,
indexArg = this.compileCallDirect(getterInstance, [], targetExpression,
this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType)
);
signature = this.currentType.signatureReference;
Expand Down Expand Up @@ -6948,6 +6989,14 @@ export class Compiler extends DiagnosticEmitter {
contextualType: Type,
constraints: Constraints
): ExpressionRef {

if (expression.isOptionalChaining) {
this.error(
DiagnosticCode.Not_implemented,
expression.expression.range.atEnd
);
}

var module = this.module;
var targetExpression = expression.expression;
var targetType = this.resolver.resolveExpression(targetExpression, this.currentFlow); // reports
Expand Down Expand Up @@ -7951,6 +8000,14 @@ export class Compiler extends DiagnosticEmitter {
ctxType: Type,
constraints: Constraints
): ExpressionRef {

if (expression.isOptionalChaining) {
this.error(
DiagnosticCode.Not_implemented,
expression.expression.range.atEnd
);
}

var module = this.module;
var flow = this.currentFlow;

Expand Down Expand Up @@ -8138,6 +8195,18 @@ export class Compiler extends DiagnosticEmitter {
return expr;
}

compileCoalesceExpression(
expression: CoalesceExpression,
ctxType: Type,
constraints: Constraints
): ExpressionRef {
this.error(
DiagnosticCode.Not_implemented,
expression.range
);
return this.module.unreachable();
}

compileUnaryPostfixExpression(
expression: UnaryPostfixExpression,
contextualType: Type,
Expand Down