Adds destructuring logic and placeholders for each transformer.

This commit is contained in:
Ron Buckton
2016-02-09 15:27:33 -08:00
parent e545f1b1ba
commit 49d2d93379
13 changed files with 682 additions and 15 deletions
+25 -8
View File
@@ -135,9 +135,18 @@ namespace ts {
return node;
}
export function createTempVariable(tempKind: TempVariableKind): Identifier {
export function createTempVariable(): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier);
name.tempKind = tempKind;
name.text = undefined;
name.tempKind = TempVariableKind.Auto;
getNodeId(name);
return name;
}
export function createLoopVariable(): Identifier {
const name = <Identifier>createNode(SyntaxKind.Identifier);
name.text = undefined;
name.tempKind = TempVariableKind.Loop;
getNodeId(name);
return name;
}
@@ -171,16 +180,16 @@ namespace ts {
return createVoid(createLiteral(0));
}
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression);
export function createPropertyAccess(expression: Expression, name: string | Identifier, location?: TextRange) {
const node = <PropertyAccessExpression>createNode(SyntaxKind.PropertyAccessExpression, location);
node.expression = parenthesizeForAccess(expression);
node.dotToken = createSynthesizedNode(SyntaxKind.DotToken);
node.name = coerceIdentifier(name);
return node;
}
export function createElementAccess(expression: Expression, index: string | number | Expression) {
const node = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression);
export function createElementAccess(expression: Expression, index: string | number | Expression, location?: TextRange) {
const node = <ElementAccessExpression>createNode(SyntaxKind.ElementAccessExpression, location);
node.expression = parenthesizeForAccess(expression);
node.argumentExpression = coerceExpression(index);
return node;
@@ -216,8 +225,8 @@ namespace ts {
return <Expression>createBinary(left, SyntaxKind.CommaToken, right);
}
export function createCall(expression: Expression, argumentsArray: Expression[]) {
const node = <CallExpression>createNode(SyntaxKind.CallExpression);
export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) {
const node = <CallExpression>createNode(SyntaxKind.CallExpression, location);
node.expression = parenthesizeForAccess(expression);
node.arguments = createNodeArray(argumentsArray);
return node;
@@ -228,6 +237,14 @@ namespace ts {
return createCall(createPropertyAccess(array, "slice"), argumentsList);
}
export function createMathPow(left: Expression, right: Expression, location?: TextRange) {
return createCall(
createPropertyAccess(createIdentifier("Math"), "pow"),
[left, right],
location
);
}
export function parenthesizeExpression(expression: Expression) {
const node = <ParenthesizedExpression>createNode(SyntaxKind.ParenthesizedExpression);
node.expression = expression;
+33 -4
View File
@@ -7,12 +7,15 @@ namespace ts {
setSourceFile(sourceFile: SourceFile): void;
emitPos(pos: number): void;
emitStart(range: TextRange): void;
emitEnd(range: TextRange, stopOverridingSpan?: boolean): void;
changeEmitSourcePos(): void;
emitEnd(range: TextRange): void;
/*@deprecated*/ emitEnd(range: TextRange, stopOverridingSpan: boolean): void;
/*@deprecated*/ changeEmitSourcePos(): void;
getText(): string;
getSourceMappingURL(): string;
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void;
reset(): void;
enable(): void;
disable(): void;
}
let nullSourceMapWriter: SourceMapWriter;
@@ -38,6 +41,8 @@ namespace ts {
getSourceMappingURL(): string { return undefined; },
initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean): void { },
reset(): void { },
enable(): void { },
disable(): void { }
};
}
@@ -62,6 +67,8 @@ namespace ts {
// Source map data
let sourceMapData: SourceMapData;
let disableDepth: number;
return {
getSourceMapData: () => sourceMapData,
setSourceFile,
@@ -73,6 +80,8 @@ namespace ts {
getSourceMappingURL,
initialize,
reset,
enable,
disable,
};
function initialize(filePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) {
@@ -81,6 +90,7 @@ namespace ts {
}
currentSourceFile = undefined;
disableDepth = 0;
// Current source map file and its index in the sources list
sourceMapSourceIndex = -1;
@@ -147,6 +157,17 @@ namespace ts {
lastEncodedSourceMapSpan = undefined;
lastEncodedNameIndex = undefined;
sourceMapData = undefined;
disableDepth = 0;
}
function enable() {
if (disableDepth > 0) {
disableDepth--;
}
}
function disable() {
disableDepth++;
}
function updateLastEncodedAndRecordedSpans() {
@@ -168,7 +189,7 @@ namespace ts {
sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
defaultLastEncodedSourceMapSpan;
// TODO: Update lastEncodedNameIndex
// TODO: Update lastEncodedNameIndex
// Since we dont support this any more, lets not worry about it right now.
// When we start supporting nameIndex, we will get back to this
@@ -236,7 +257,7 @@ namespace ts {
}
function emitPos(pos: number) {
if (pos === -1) {
if (positionIsSynthesized(pos) || disableDepth > 0) {
return;
}
@@ -288,9 +309,17 @@ namespace ts {
function emitStart(range: TextRange) {
emitPos(getStartPos(range));
if ((<SynthesizedNode>range).disableSourceMap) {
disable();
}
}
function emitEnd(range: TextRange, stopOverridingEnd?: boolean) {
if ((<SynthesizedNode>range).disableSourceMap) {
enable();
}
emitPos(range.end);
stopOverridingSpan = stopOverridingEnd;
}
+35 -1
View File
@@ -1,10 +1,44 @@
/// <reference path="visitor.ts" />
/// <reference path="transformers/ts.ts" />
/// <reference path="transformers/jsx.ts" />
/// <reference path="transformers/es7.ts" />
/// <reference path="transformers/es6.ts" />
/// <reference path="transformers/module/module.ts" />
/// <reference path="transformers/module/system.ts" />
/// <reference path="transformers/module/es6.ts" />
/* @internal */
namespace ts {
const moduleTransformerMap: Map<Transformer> = {
[ModuleKind.ES6]: transformES6Module,
[ModuleKind.System]: transformSystemModule,
[ModuleKind.AMD]: transformModule,
[ModuleKind.CommonJS]: transformModule,
[ModuleKind.UMD]: transformModule,
[ModuleKind.None]: transformModule
};
export function getTransformers(compilerOptions: CompilerOptions) {
const jsx = compilerOptions.jsx;
const languageVersion = getLanguageVersion(compilerOptions);
const moduleKind = getModuleKind(compilerOptions);
const transformers: Transformer[] = [];
// TODO(rbuckton): Add transformers
transformers.push(transformTypeScript);
transformers.push(moduleTransformerMap[moduleKind]);
if (jsx === JsxEmit.React) {
transformers.push(transformJsx);
}
if (languageVersion < ScriptTarget.ES7) {
transformers.push(transformES7);
}
if (languageVersion < ScriptTarget.ES6) {
transformers.push(transformES6);
}
return transformers;
}
+347
View File
@@ -0,0 +1,347 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
/**
* Flattens a destructuring assignment expression.
*
* @param root The destructuring assignment expression.
* @param needsValue Indicates whether the value from the right-hand-side of the
* destructuring assignment is needed as part of a larger expression.
* @param recordTempVariable A callback used to record new temporary variables.
*/
export function flattenDestructuringAssignment(node: BinaryExpression, needsValue: boolean, recordTempVariable: (node: Identifier) => void) {
let location: TextRange = node;
let value = node.right;
if (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
return value;
}
const expressions: Expression[] = [];
if (needsValue) {
// Temporary assignment needed to emit root should highlight whole binary expression
value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, node, emitTempVariableAssignment);
}
else if (nodeIsSynthesized(node)) {
// Source map node for root.left = root.right is root
// but if root is synthetic, which could be in below case, use the target which is { a }
// for ({a} of {a: string}) {
// }
location = node.right;
}
flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment);
if (needsValue) {
expressions.push(value);
}
const expression = inlineExpressions(expressions);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const expression = createAssignment(name, value, location);
if (isSimpleExpression(value)) {
(<SynthesizedNode>expression).disableSourceMap = true;
}
aggregateTransformFlags(expression);
expressions.push(expression);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable();
recordTempVariable(name);
emitAssignment(name, value, location);
return name;
}
}
/**
* Flattens binding patterns in a parameter declaration.
*
* @param node The ParameterDeclaration to flatten.
* @param value The rhs value for the binding pattern.
*/
export function flattenParameterDestructuring(node: ParameterDeclaration, value: Expression) {
const declarations: VariableDeclaration[] = [];
flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const declaration = createVariableDeclaration(name, value, location);
if (isSimpleExpression(value)) {
(<SynthesizedNode>declaration).disableSourceMap = true;
}
aggregateTransformFlags(declaration);
declarations.push(declaration);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable();
emitAssignment(name, value, location);
return name;
}
}
/**
* Flattens binding patterns in a variable declaration.
*
* @param node The VariableDeclaration to flatten.
* @param value An optional rhs value for the binding pattern.
*/
export function flattenVariableDestructuring(node: VariableDeclaration, value?: Expression) {
const declarations: VariableDeclaration[] = [];
flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
const declaration = createVariableDeclaration(name, value, location);
if (declarations.length === 0) {
declaration.pos = -1;
}
if (isSimpleExpression(value)) {
(<SynthesizedNode>declaration).disableSourceMap = true;
}
declaration.original = original;
declarations.push(declaration);
aggregateTransformFlags(declaration);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable();
emitAssignment(name, value, location, /*original*/ undefined);
return name;
}
}
/**
* Flattens binding patterns in a variable declaration and transforms them into an expression.
*
* @param node The VariableDeclaration to flatten.
* @param recordTempVariable A callback used to record new temporary variables.
*/
export function flattenVariableDestructuringToExpression(node: VariableDeclaration, recordTempVariable: (name: Identifier) => void) {
const pendingAssignments: Expression[] = [];
flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment);
const expression = inlineExpressions(pendingAssignments);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
const expression = createAssignment(name, value, location);
if (isSimpleExpression(value)) {
(<SynthesizedNode>expression).disableSourceMap = true;
}
expression.original = original;
pendingAssignments.push(expression);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable();
recordTempVariable(name);
emitAssignment(name, value, location, /*original*/ undefined);
return name;
}
}
function flattenDestructuring(
root: BindingElement | BinaryExpression,
value: Expression,
location: TextRange,
emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) {
if (isBinaryExpression(root)) {
emitDestructuringAssignment(root.left, value, location)
}
else {
emitBindingElement(root, value);
}
function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) {
// When emitting target = value use source map node to highlight, including any temporary assignments needed for this
let target: Expression;
if (isShortHandPropertyAssignment(bindingTarget)) {
if (bindingTarget.objectAssignmentInitializer) {
value = createDefaultValueCheck(value, bindingTarget.objectAssignmentInitializer, location);
}
target = bindingTarget.name;
}
else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) {
value = createDefaultValueCheck(value, bindingTarget.right, location);
target = bindingTarget.left;
}
else {
target = bindingTarget;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
emitObjectLiteralAssignment(<ObjectLiteralExpression>target, value, location);
}
else if (target.kind === SyntaxKind.ArrayLiteralExpression) {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, location);
}
else {
const name = cloneNode(<Identifier>target, /*location*/ target, /*flags*/ undefined, /*parent*/ undefined, /*original*/ target);
emitAssignment(name, value, location, /*original*/ undefined);
}
}
function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) {
const properties = target.properties;
if (properties.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (const p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
const propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
// Assignment for target = value.propName should highligh whole property, hence use p as source map node
emitDestructuringAssignment(target, createDestructuringPropertyAccess(value, propName), p);
}
}
}
function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) {
const elements = target.elements;
if (elements.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (let i = 0; i < elements.length; i++) {
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
// Assignment for target = value.propName should highligh whole property, hence use e as source map node
if (e.kind !== SyntaxKind.SpreadElementExpression) {
emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e);
}
else if (i === elements.length - 1) {
emitDestructuringAssignment((<SpreadElementExpression>e).expression, createArraySlice(value, i), e);
}
}
}
}
function emitBindingElement(target: BindingElement, value: Expression) {
// Any temporary assignments needed to emit target = value should point to target
if (target.initializer) {
// Combine value and initializer
value = value ? createDefaultValueCheck(value, target.initializer, target) : target.initializer;
}
else if (!value) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero();
}
const name = target.name;
if (isBindingPattern(name)) {
const elements = name.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment);
}
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
if (name.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
let propName = element.propertyName || <Identifier>element.name;
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
}
else if (element.kind !== SyntaxKind.OmittedExpression) {
if (!element.dotDotDotToken) {
// Rewrite element to a declaration that accesses array element at index i
emitBindingElement(element, createElementAccess(value, i));
}
else if (i === elements.length - 1) {
emitBindingElement(element, createArraySlice(value, i));
}
}
}
}
else {
const clonedName = cloneNode(name, /*location*/ undefined, /*flags*/ undefined, /*parent*/ undefined, /*original*/ name);
emitAssignment(clonedName, value, target, target);
}
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression {
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
return createConditional(
createStrictEquality(value, createVoidZero()),
defaultValue,
value
);
}
function createDestructuringPropertyAccess(object: Expression, propertyName: PropertyName): LeftHandSideExpression {
if (isComputedPropertyName(propertyName)) {
return createElementAccess(
object,
ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, propertyName, emitTempVariableAssignment)
);
}
else if (isIdentifier(propertyName)) {
return createPropertyAccess(
object,
propertyName.text
);
}
else {
// We create a synthetic copy of the identifier in order to avoid the rewriting that might
// otherwise occur when the identifier is emitted.
return createElementAccess(
object,
cloneNode(propertyName)
);
}
}
}
/**
* Ensures that there exists a declared identifier whose value holds the given expression.
* This function is useful to ensure that the expression's value can be read from in subsequent expressions.
* Unless 'reuseIdentifierExpressions' is false, 'value' will be returned if it is just an identifier.
*
* @param value the expression whose value needs to be bound.
* @param reuseIdentifierExpressions true if identifier expressions can simply be returned;
* false if it is necessary to always emit an identifier.
* @param location The location to use for source maps and comments.
* @param emitTempVariableAssignment A callback used to emit a temporary variable.
*/
function ensureIdentifier(
value: Expression,
reuseIdentifierExpressions: boolean,
location: TextRange,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier) {
if (isIdentifier(value) && reuseIdentifierExpressions) {
return value;
}
else {
return emitTempVariableAssignment(value, location);
}
}
}
+30
View File
@@ -0,0 +1,30 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): ES6->ES5 transformer
export function transformES6(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
if (node.transformFlags & TransformFlags.ES6) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsES6) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node): Node {
return node;
}
}
}
+32
View File
@@ -0,0 +1,32 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): ES7->ES6 transformer
export function transformES7(context: TransformationContext) {
const { hoistVariableDeclaration } = context;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
if (node.transformFlags & TransformFlags.ES7) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsES7) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node) {
return node;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): JSX->React transformer
export function transformJsx(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
if (node.transformFlags & TransformFlags.Jsx) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsJsx) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node): Node {
return node;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/*@internal*/
namespace ts {
export function transformES6Module(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
return node;
}
}
}
@@ -0,0 +1,18 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): CommonJS/AMD/UMD transformer
export function transformModule(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
return node;
}
}
}
@@ -0,0 +1,18 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): System module transformer
export function transformSystemModule(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node): Node {
return node;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/*@internal*/
namespace ts {
// TODO(rbuckton): TS->ES7 transformer
export function transformTypeScript(context: TransformationContext) {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
return visitEachChild(node, visitor, context);
}
function visitor(node: Node) {
if (node.transformFlags & TransformFlags.TypeScript) {
return visitorWorker(node);
}
else if (node.transformFlags & TransformFlags.ContainsTypeScript) {
return visitEachChild(node, visitor, context);
}
else {
return node;
}
}
function visitorWorker(node: Node): Node {
return node;
}
}
}
+3 -1
View File
@@ -2508,8 +2508,10 @@ namespace ts {
ES3 = 0,
ES5 = 1,
ES6 = 2,
ES7 = 3,
ES2015 = ES6,
Latest = ES6,
ES2016 = ES7,
Latest = ES7,
}
export const enum LanguageVariant {
+64 -1
View File
@@ -11,7 +11,8 @@ namespace ts {
export interface SynthesizedNode extends Node {
leadingCommentRanges?: CommentRange[];
trailingCommentRanges?: CommentRange[];
startsOnNewLine: boolean;
startsOnNewLine?: boolean;
disableSourceMap?: boolean;
}
export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration {
@@ -2724,6 +2725,68 @@ namespace ts {
return carriageReturnLineFeed;
}
/**
* Tests whether a node and its subtree is simple enough to have its position
* information ignored when emitting source maps in a destructuring assignment.
*
* @param node The expression to test.
*/
export function isSimpleExpression(node: Expression): boolean {
return isSimpleExpressionWorker(node, 0);
}
function isSimpleExpressionWorker(node: Expression, depth: number): boolean {
if (depth <= 5) {
const kind = node.kind;
if (kind === SyntaxKind.StringLiteral
|| kind === SyntaxKind.NumericLiteral
|| kind === SyntaxKind.RegularExpressionLiteral
|| kind === SyntaxKind.NoSubstitutionTemplateLiteral
|| kind === SyntaxKind.Identifier
|| kind === SyntaxKind.ThisKeyword
|| kind === SyntaxKind.SuperKeyword
|| kind === SyntaxKind.TrueKeyword
|| kind === SyntaxKind.FalseKeyword
|| kind === SyntaxKind.NullKeyword) {
return true;
}
else if (kind === SyntaxKind.PropertyAccessExpression) {
return isSimpleExpressionWorker((<PropertyAccessExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ElementAccessExpression) {
return isSimpleExpressionWorker((<ElementAccessExpression>node).expression, depth + 1)
&& isSimpleExpressionWorker((<ElementAccessExpression>node).argumentExpression, depth + 1);
}
else if (kind === SyntaxKind.PrefixUnaryExpression
|| kind === SyntaxKind.PostfixUnaryExpression) {
return isSimpleExpressionWorker((<PrefixUnaryExpression | PostfixUnaryExpression>node).operand, depth + 1);
}
else if (kind === SyntaxKind.BinaryExpression) {
return (<BinaryExpression>node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken
&& isSimpleExpressionWorker((<BinaryExpression>node).left, depth + 1)
&& isSimpleExpressionWorker((<BinaryExpression>node).right, depth + 1);
}
else if (kind === SyntaxKind.ConditionalExpression) {
return isSimpleExpressionWorker((<ConditionalExpression>node).condition, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenTrue, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenFalse, depth + 1)
}
else if (kind === SyntaxKind.VoidExpression
|| kind === SyntaxKind.TypeOfExpression
|| kind === SyntaxKind.DeleteExpression) {
return isSimpleExpressionWorker((<VoidExpression | TypeOfExpression | DeleteExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ArrayLiteralExpression) {
return (<ArrayLiteralExpression>node).elements.length === 0;
}
else if (kind === SyntaxKind.ObjectLiteralExpression) {
return (<ObjectLiteralExpression>node).properties.length === 0;
}
}
return false;
}
// Node tests
//
// All node tests in the following list should *not* reference parent pointers so that