diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts
index c28162d8723..754dea2905c 100644
--- a/src/compiler/factory.ts
+++ b/src/compiler/factory.ts
@@ -1,11 +1,12 @@
///
///
+/* @internal */
namespace ts {
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
- export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
+ function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
if (kind === SyntaxKind.SourceFile) {
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
}
@@ -14,7 +15,10 @@ namespace ts {
}
}
- /* @internal */
+ function allocateNode(kind: SyntaxKind, location?: TextRange) {
+ return location ? createNode(kind, location.pos, location.end) : createSynthesizedNode(kind);
+ }
+
export function createNodeArray(elements?: T[], pos?: number, end?: number): NodeArray {
const array = >(elements || []);
array.pos = pos;
@@ -23,7 +27,6 @@ namespace ts {
return array;
}
- /* @internal */
export function createModifiersArray(elements?: Modifier[], pos?: number, end?: number): ModifiersArray {
const array = (elements || []);
array.pos = pos;
@@ -33,19 +36,16 @@ namespace ts {
return array;
}
- /* @internal */
export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
const node = createNode(kind, /*pos*/ -1, /*end*/ -1);
node.startsOnNewLine = startsOnNewLine;
return node;
}
- /* @internal */
export function createSynthesizedNodeArray(elements?: T[]): NodeArray {
return createNodeArray(elements, /*pos*/ -1, /*end*/ -1);
}
- /* @internal */
export function createSynthesizedModifiersArray(elements?: Modifier[]): ModifiersArray {
return createModifiersArray(elements, /*pos*/ -1, /*end*/ -1);
}
@@ -61,7 +61,6 @@ namespace ts {
* @param parent The parent for the new node.
* @param original An optional pointer to the original source tree node.
*/
- /* @internal */
export function cloneNode(node: T, location?: TextRange, flags?: NodeFlags, parent?: Node, original?: Node): T {
// We don't use "clone" from core.ts here, as we need to preserve the prototype chain of
// the original node. We also need to exclude specific properties and only include own-
@@ -93,46 +92,252 @@ namespace ts {
return clone;
}
- /* @internal */
export function createNodeArrayNode(elements: T[]): NodeArrayNode {
const node = >createSynthesizedNode(SyntaxKind.NodeArrayNode);
node.nodes = createNodeArray(elements);
return node;
}
- /* @internal */
export function createReturn(expression?: Expression): ReturnStatement {
const node = createSynthesizedNode(SyntaxKind.ReturnStatement);
node.expression = expression;
return node;
}
- /* @internal */
export function createStatement(expression: Expression): ExpressionStatement {
const node = createSynthesizedNode(SyntaxKind.ExpressionStatement);
node.expression = expression;
return node;
}
- /* @internal */
export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement {
const node = createSynthesizedNode(SyntaxKind.VariableStatement);
node.declarationList = declarationList;
return node;
}
- /* @internal */
export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList {
const node = createSynthesizedNode(SyntaxKind.VariableDeclarationList);
node.declarations = createNodeArray(declarations);
return node;
}
- /* @internal */
export function createBlock(statements: Statement[]): Block {
const block = createSynthesizedNode(SyntaxKind.Block);
block.statements = createNodeArray(statements);
return block;
}
+ export function createVariableDeclaration(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration {
+ const node = allocateNode(SyntaxKind.VariableDeclaration, location);
+ node.name = name;
+ node.initializer = initializer;
+ return node;
+ }
+
+ export function createIdentifier(text: string): Identifier {
+ const node = allocateNode(SyntaxKind.Identifier);
+ node.text = text;
+ return node;
+ }
+
+ export function createTempVariable(tempKind: TempVariableKind): Identifier {
+ const name = allocateNode(SyntaxKind.Identifier);
+ name.tempKind = tempKind;
+ getNodeId(name);
+ return name;
+ }
+
+ export function createLiteral(value: string): StringLiteral;
+ export function createLiteral(value: number): LiteralExpression;
+ export function createLiteral(value: string | number | boolean | void): PrimaryExpression;
+ export function createLiteral(value: string | number | boolean | void): T {
+ if (typeof value === "string") {
+ const node = allocateNode(SyntaxKind.StringLiteral);
+ node.text = value;
+ return node;
+ }
+ else if (typeof value === "number") {
+ const node = allocateNode(SyntaxKind.NumericLiteral);
+ node.text = value.toString();
+ return node;
+ }
+ else if (typeof value === "boolean") {
+ return allocateNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
+ }
+ else if (value === null) {
+ return allocateNode(SyntaxKind.NullKeyword);
+ }
+ }
+
+ export function createVoid(expression: UnaryExpression) {
+ const node = allocateNode(SyntaxKind.VoidExpression);
+ node.expression = expression;
+ return node;
+ }
+
+ export function createVoidZero() {
+ return createVoid(createLiteral(0));
+ }
+
+ export function createPropertyAccess(expression: Expression, name: string | Identifier) {
+ const node = allocateNode(SyntaxKind.PropertyAccessExpression);
+ 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 = allocateNode(SyntaxKind.ElementAccessExpression);
+ node.expression = parenthesizeForAccess(expression);
+ node.argumentExpression = coerceExpression(index);
+ return node;
+ }
+
+ export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) {
+ const node = allocateNode(SyntaxKind.ConditionalExpression);
+ node.condition = condition;
+ node.questionToken = createSynthesizedNode(SyntaxKind.QualifiedName);
+ node.whenTrue = whenTrue;
+ node.colonToken = createSynthesizedNode(SyntaxKind.ColonToken);
+ node.whenFalse = whenFalse;
+ return node;
+ }
+
+ export function createBinary(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) {
+ const node = allocateNode(SyntaxKind.BinaryExpression, location);
+ node.left = parenthesizeForBinary(left, operator, BinaryOperand.Left);
+ node.operatorToken = createSynthesizedNode(operator);
+ node.right = parenthesizeForBinary(right, operator, BinaryOperand.Right);
+ return node;
+ }
+
+ export function createAssignment(left: Expression, right: Expression, location?: TextRange) {
+ return createBinary(left, SyntaxKind.EqualsToken, right, location);
+ }
+
+ export function createStrictEquality(left: Expression, right: Expression) {
+ return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right);
+ }
+
+ export function createComma(left: Expression, right: Expression) {
+ return createBinary(left, SyntaxKind.CommaToken, right);
+ }
+
+ export function createCall(expression: Expression, argumentsArray: Expression[]) {
+ const node = allocateNode(SyntaxKind.CallExpression);
+ node.expression = parenthesizeForAccess(expression);
+ node.arguments = createNodeArray(argumentsArray);
+ return node;
+ }
+
+ export function createArraySlice(array: Expression, start?: number | Expression) {
+ const argumentsList: Expression[] = start !== undefined ? [coerceExpression(start)] : [];
+ return createCall(createPropertyAccess(array, "slice"), argumentsList);
+ }
+
+ export function parenthesizeExpression(expression: Expression) {
+ const node = allocateNode(SyntaxKind.ParenthesizedExpression);
+ node.expression = expression;
+ return node;
+ }
+
+ export function inlineExpressions(expressions: Expression[]) {
+ return reduceLeft(expressions, createComma);
+ }
+
+ function coerceIdentifier(value: string | Identifier) {
+ if (typeof value === "string") {
+ return createIdentifier(value);
+ }
+ else {
+ return value;
+ }
+ }
+
+ function coerceExpression(value: string | number | boolean | Expression): Expression {
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+ return createLiteral(value);
+ }
+ else if (value === null) {
+ return createLiteral(null);
+ }
+ else {
+ return value;
+ }
+ }
+
+ const enum BinaryOperand {
+ Left,
+ Right
+ }
+
+ function parenthesizeForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
+ // When diagnosing whether the expression needs parentheses, the decision should be based
+ // on the innermost expression in a chain of nested type assertions.
+ while (operand.kind === SyntaxKind.TypeAssertionExpression || operand.kind === SyntaxKind.AsExpression) {
+ operand = (operand).expression;
+ }
+
+ // If the resulting expression is already parenthesized, we do not need to do any further processing.
+ if (operand.kind === SyntaxKind.ParenthesizedExpression) {
+ return operand;
+ }
+
+ return needsParenthesesForBinary(operand, operator, side)
+ ? parenthesizeExpression(operand)
+ : operand;
+ }
+
+ function needsParenthesesForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
+ const operandPrecedence = getExpressionPrecedence(operand);
+ const operatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, operator);
+ switch (compareValues(operandPrecedence, operatorPrecedence)) {
+ case Comparison.LessThan:
+ return true;
+ case Comparison.EqualTo:
+ return isRightAssociativeOperandOnLeftHandSide(operand, side)
+ || isModuloOperandOnRightHandSide(operand, operator, side);
+ case Comparison.GreaterThan:
+ return false;
+ }
+ }
+
+ function isRightAssociativeOperandOnLeftHandSide(operand: Expression, side: BinaryOperand) {
+ return side === BinaryOperand.Left
+ && getExpressionAssociativity(operand) === Associativity.Right;
+ }
+
+ function isModuloOperandOnRightHandSide(operand: Expression, operator: SyntaxKind, side: BinaryOperand) {
+ return side === BinaryOperand.Right
+ && operator !== SyntaxKind.PercentToken
+ && operand.kind === SyntaxKind.BinaryExpression
+ && (operand).operatorToken.kind === SyntaxKind.PercentToken;
+ }
+
+ function parenthesizeForAccess(expr: Expression): LeftHandSideExpression {
+ // When diagnosing whether the expression needs parentheses, the decision should be based
+ // on the innermost expression in a chain of nested type assertions.
+ while (expr.kind === SyntaxKind.TypeAssertionExpression || expr.kind === SyntaxKind.AsExpression) {
+ expr = (expr).expression;
+ }
+
+ // isLeftHandSideExpression is almost the correct criterion for when it is not necessary
+ // to parenthesize the expression before a dot. The known exceptions are:
+ //
+ // NewExpression:
+ // new C.x -> not the same as (new C).x
+ // NumberLiteral
+ // 1.x -> not the same as (1).x
+ //
+ if (isLeftHandSideExpression(expr) &&
+ expr.kind !== SyntaxKind.NewExpression &&
+ expr.kind !== SyntaxKind.NumericLiteral) {
+
+ return expr;
+ }
+
+ return parenthesizeExpression(expr);
+ }
}
\ No newline at end of file
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 1d9f9fe2305..b21f9919f45 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -5,6 +5,18 @@
namespace ts {
/* @internal */ export let parseTime = 0;
+ let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
+ let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
+
+ export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
+ if (kind === SyntaxKind.SourceFile) {
+ return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
+ }
+ else {
+ return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, pos, end);
+ }
+ }
+
function visitNode(cbNode: (node: Node) => T, node: Node): T {
if (node) {
return cbNode(node);
diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts
new file mode 100644
index 00000000000..a4a5ae7c91a
--- /dev/null
+++ b/src/compiler/transformer.ts
@@ -0,0 +1,331 @@
+///
+
+/* @internal */
+namespace ts {
+ export function getTransformers(compilerOptions: CompilerOptions) {
+ const transformers: Transformer[] = [];
+ // TODO(rbuckton): Add transformers
+ return transformers;
+ }
+
+ export function transformFiles(resolver: EmitResolver, host: EmitHost, sourceFiles: SourceFile[], transformers: Transformer[]) {
+ const nodeToGeneratedName: Identifier[] = [];
+ const generatedNameSet: Map = {};
+ const nodeEmitFlags: NodeEmitFlags[] = [];
+ const lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
+ const lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
+ let lexicalEnvironmentStackOffset = 0;
+ let hoistedVariableDeclarations: VariableDeclaration[];
+ let hoistedFunctionDeclarations: FunctionDeclaration[];
+ let currentSourceFile: SourceFile;
+
+ const context: TransformationContext = {
+ getCompilerOptions: () => host.getCompilerOptions(),
+ getEmitResolver: () => resolver,
+ getNodeEmitFlags,
+ setNodeEmitFlags,
+ isUniqueName,
+ getGeneratedNameForNode,
+ nodeHasGeneratedName,
+ makeUniqueName,
+ hoistVariableDeclaration,
+ hoistFunctionDeclaration,
+ startLexicalEnvironment,
+ endLexicalEnvironment
+ };
+
+ // Chain together and initialize each transformer.
+ const transformation = chain(...transformers)(context);
+
+ // Transform each source file.
+ return map(sourceFiles, transformSourceFile);
+
+ /**
+ * Transforms a source file.
+ * @param sourceFile The source file to transform.
+ */
+ function transformSourceFile(sourceFile: SourceFile) {
+ if (isDeclarationFile(sourceFile)) {
+ return sourceFile;
+ }
+
+ currentSourceFile = sourceFile;
+ const visited = transformation(sourceFile);
+ currentSourceFile = undefined;
+ return visited;
+ }
+
+ /**
+ * Gets flags that control emit behavior of a node.
+ */
+ function getNodeEmitFlags(node: Node) {
+ while (node) {
+ const nodeId = getNodeId(node);
+ if (nodeEmitFlags[nodeId] !== undefined) {
+ return nodeEmitFlags[nodeId];
+ }
+
+ node = node.original;
+ }
+
+ return undefined;
+ }
+
+ /**
+ * Sets flags that control emit behavior of a node.
+ */
+ function setNodeEmitFlags(node: Node, flags: NodeEmitFlags) {
+ nodeEmitFlags[getNodeId(node)] = flags;
+ }
+
+ /**
+ * Generate a name that is unique within the current file and doesn't conflict with any names
+ * in global scope. The name is formed by adding an '_n' suffix to the specified base name,
+ * where n is a positive integer. Note that names generated by makeTempVariableName and
+ * makeUniqueName are guaranteed to never conflict.
+ */
+ function makeUniqueName(baseName: string): Identifier {
+ // Find the first unique 'name_n', where n is a positive number
+ if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
+ baseName += "_";
+ }
+
+ let i = 1;
+ while (true) {
+ const generatedName = baseName + i;
+ if (isUniqueName(generatedName)) {
+ return createIdentifier(generatedNameSet[generatedName] = generatedName);
+ }
+
+ i++;
+ }
+ }
+
+ /**
+ * Gets the generated name for a node.
+ */
+ function getGeneratedNameForNode(node: Node) {
+ const id = getNodeId(node);
+ return nodeToGeneratedName[id] || (nodeToGeneratedName[id] = generateNameForNode(node));
+ }
+
+ /**
+ * Gets a value indicating whether a node has a generated name.
+ */
+ function nodeHasGeneratedName(node: Node) {
+ const id = getNodeId(node);
+ return nodeToGeneratedName[id] !== undefined;
+ }
+
+ /**
+ * Tests whether the provided name is unique.
+ */
+ function isUniqueName(name: string): boolean {
+ return !resolver.hasGlobalName(name)
+ && !hasProperty(currentSourceFile.identifiers, name)
+ && !hasProperty(generatedNameSet, name);
+ }
+
+ /**
+ * Tests whether the provided name is unique within a container.
+ */
+ function isUniqueLocalName(name: string, container: Node): boolean {
+ container = getOriginalNode(container);
+ for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
+ if (node.locals && hasProperty(node.locals, name)) {
+ // We conservatively include alias symbols to cover cases where they're emitted as locals
+ if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Generates a name for a node.
+ */
+ function generateNameForNode(node: Node): Identifier {
+ switch (node.kind) {
+ case SyntaxKind.Identifier:
+ return makeUniqueName((node).text);
+ case SyntaxKind.ModuleDeclaration:
+ case SyntaxKind.EnumDeclaration:
+ return generateNameForModuleOrEnum(node);
+ case SyntaxKind.ImportDeclaration:
+ case SyntaxKind.ExportDeclaration:
+ return generateNameForImportOrExportDeclaration(node);
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ExportAssignment:
+ return generateNameForExportDefault();
+ case SyntaxKind.ClassExpression:
+ return generateNameForClassExpression();
+ case SyntaxKind.ComputedPropertyName:
+ case SyntaxKind.Parameter:
+ case SyntaxKind.TaggedTemplateExpression:
+ return createTempVariable(TempVariableKind.Auto);
+ }
+ }
+
+ function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
+ const name = node.name;
+ // Use module/enum name itself if it is unique, otherwise make a unique variation
+ return isUniqueLocalName(name.text, node) ? name : makeUniqueName(name.text);
+ }
+
+ function generateNameForImportOrExportDeclaration(node: ImportDeclaration | ExportDeclaration) {
+ const expr = getExternalModuleName(node);
+ const baseName = expr.kind === SyntaxKind.StringLiteral
+ ? escapeIdentifier(makeIdentifierFromModuleName((expr).text))
+ : "module";
+ return makeUniqueName(baseName);
+ }
+
+ function generateNameForExportDefault() {
+ return makeUniqueName("default");
+ }
+
+ function generateNameForClassExpression() {
+ return makeUniqueName("class");
+ }
+
+ /**
+ * Records a hoisted variable declaration within a lexical environment.
+ */
+ function hoistVariableDeclaration(name: Identifier): void {
+ if (!hoistedVariableDeclarations) {
+ hoistedVariableDeclarations = [];
+ }
+
+ hoistedVariableDeclarations.push(createVariableDeclaration(name));
+ }
+
+ /**
+ * Records a hoisted function declaration within a lexical environment.
+ */
+ function hoistFunctionDeclaration(func: FunctionDeclaration): void {
+ if (!hoistedFunctionDeclarations) {
+ hoistedFunctionDeclarations = [];
+ }
+
+ hoistedFunctionDeclarations.push(func);
+ }
+
+ /**
+ * Starts a new lexical environment. Any existing hoisted variable or function declarations
+ * are pushed onto a stack, and the related storage variables are reset.
+ */
+ function startLexicalEnvironment(): void {
+ // Save the current lexical environment. Rather than resizing the array we adjust the
+ // stack size variable. This allows us to reuse existing array slots we've
+ // already allocated between transformations to avoid allocation and GC overhead during
+ // transformation.
+ lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedVariableDeclarations;
+ lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = hoistedFunctionDeclarations;
+ lexicalEnvironmentStackOffset++;
+
+ hoistedVariableDeclarations = undefined;
+ hoistedFunctionDeclarations = undefined;
+ }
+
+ /**
+ * Ends a lexical environment. The previous set of hoisted declarations are restored and
+ * any hoisted declarations added in this environment are returned.
+ */
+ function endLexicalEnvironment(): Statement[] {
+ let statements: Statement[];
+ if (hoistedVariableDeclarations || hoistedFunctionDeclarations) {
+ statements = [];
+ if (hoistedFunctionDeclarations) {
+ for (const declaration of hoistedFunctionDeclarations) {
+ statements.push(declaration);
+ }
+ }
+
+ if (hoistedVariableDeclarations) {
+ statements.push(
+ createVariableStatement(
+ createVariableDeclarationList(hoistedVariableDeclarations)
+ )
+ );
+ }
+ }
+
+ // Restore the previous lexical environment.
+ lexicalEnvironmentStackOffset--;
+ hoistedVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
+ hoistedFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
+ return statements;
+ }
+ }
+
+ /**
+ * High-order function, creates a function that executes a function composition.
+ * For example, `chain(a, b)` is the equivalent of `x => ((a', b') => y => b'(a'(y)))(a(x), b(x))`
+ *
+ * @param args The functions to chain.
+ */
+ function chain(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
+ function chain(a: (t: T) => (u: U) => U, b: (t: T) => (u: U) => U, c: (t: T) => (u: U) => U, d: (t: T) => (u: U) => U, e: (t: T) => (u: U) => U): (t: T) => (u: U) => U {
+ if (e) {
+ const args = arrayOf<(t: T) => (u: U) => U>(arguments);
+ return t => compose(...map(args, f => f(t)));
+ }
+ else if (d) {
+ return t => compose(a(t), b(t), c(t), d(t));
+ }
+ else if (c) {
+ return t => compose(a(t), b(t), c(t));
+ }
+ else if (b) {
+ return t => compose(a(t), b(t));
+ }
+ else if (a) {
+ return t => compose(a(t));
+ }
+ else {
+ return t => u => u;
+ }
+ }
+
+ /**
+ * High-order function, composes functions. Note that functions are composed inside-out;
+ * for example, `compose(a, b)` is the equivalent of `x => b(a(x))`.
+ *
+ * @param args The functions to compose.
+ */
+ function compose(...args: ((t: T) => T)[]): (t: T) => T;
+ function compose(a: (t: T) => T, b: (t: T) => T, c: (t: T) => T, d: (t: T) => T, e: (t: T) => T): (t: T) => T {
+ if (e) {
+ const args = arrayOf(arguments);
+ return t => reduceLeft<(t: T) => T, T>(args, (u, f) => f(u), t);
+ }
+ else if (d) {
+ return t => d(c(b(a(t))));
+ }
+ else if (c) {
+ return t => c(b(a(t)));
+ }
+ else if (b) {
+ return t => b(a(t));
+ }
+ else if (a) {
+ return t => a(t);
+ }
+ else {
+ return t => t;
+ }
+ }
+
+ /**
+ * Makes an array from an ArrayLike.
+ */
+ function arrayOf(arrayLike: ArrayLike) {
+ const array: T[] = [];
+ for (let i = 0; i < arrayLike.length; i++) {
+ array[i] = arrayLike[i];
+ }
+ return array;
+ }
+}
\ No newline at end of file
diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json
index 9d96dfbbb21..29f39b983f3 100644
--- a/src/compiler/tsconfig.json
+++ b/src/compiler/tsconfig.json
@@ -19,6 +19,7 @@
"checker.ts",
"factory.ts",
"visitor.ts",
+ "transformer.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 0b020839124..39ee80b7b00 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -495,9 +495,15 @@ namespace ts {
// @kind(SyntaxKind.StaticKeyword)
export interface Modifier extends Node { }
+ export const enum TempVariableKind {
+ Auto, // Automatically generated identifier
+ Loop, // Automatically generated identifier with a preference for '_i'
+ }
+
// @kind(SyntaxKind.Identifier)
export interface Identifier extends PrimaryExpression {
text: string; // Text of identifier (with escapes converted to characters)
+ tempKind?: TempVariableKind; // Specifies whether to auto-generate the text for an identifier.
originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later
}
@@ -2782,6 +2788,34 @@ namespace ts {
ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression,
}
+ /* @internal */
+ export const enum NodeEmitFlags {
+ EmitHelpers = 1 << 0,
+ EmitExportStar = 1 << 1,
+ UMDDefine = 1 << 2,
+ NoLexicalEnvironment = 1 << 3,
+ SingleLine = 1 << 4,
+ }
+
+ /* @internal */
+ export interface TransformationContext extends LexicalEnvironment {
+ getCompilerOptions(): CompilerOptions;
+ getEmitResolver(): EmitResolver;
+ getNodeEmitFlags(node: Node): NodeEmitFlags;
+ setNodeEmitFlags(node: Node, flags: NodeEmitFlags): void;
+ hoistFunctionDeclaration(node: FunctionDeclaration): void;
+ hoistVariableDeclaration(node: Identifier): void;
+ isUniqueName(name: string): boolean;
+ getGeneratedNameForNode(node: Node): Identifier;
+ nodeHasGeneratedName(node: Node): boolean;
+ makeUniqueName(baseName: string): Identifier;
+ identifierSubstitution?: (node: Identifier) => Identifier;
+ expressionSubstitution?: (node: Expression) => Expression;
+ }
+
+ /* @internal */
+ export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;
+
export interface TextSpan {
start: number;
length: number;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 75d0521a8b3..0f1d27a28f2 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1607,6 +1607,202 @@ namespace ts {
return node ? getNodeId(node) : 0;
}
+ export const enum Associativity {
+ Left,
+ Right
+ }
+
+ export function getExpressionAssociativity(expression: Expression) {
+ const operator = getOperator(expression);
+ const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression).arguments !== undefined;
+ return getOperatorAssociativity(expression.kind, operator, hasArguments);
+ }
+
+ export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, hasArguments?: boolean) {
+ switch (kind) {
+ case SyntaxKind.NewExpression:
+ return hasArguments ? Associativity.Left : Associativity.Right;
+
+ case SyntaxKind.PrefixUnaryExpression:
+ case SyntaxKind.TypeOfExpression:
+ case SyntaxKind.VoidExpression:
+ case SyntaxKind.DeleteExpression:
+ case SyntaxKind.AwaitExpression:
+ case SyntaxKind.ConditionalExpression:
+ case SyntaxKind.YieldExpression:
+ return Associativity.Right;
+
+ case SyntaxKind.BinaryExpression:
+ switch (operator) {
+ case SyntaxKind.AsteriskAsteriskToken:
+ case SyntaxKind.EqualsToken:
+ case SyntaxKind.PlusEqualsToken:
+ case SyntaxKind.MinusEqualsToken:
+ case SyntaxKind.AsteriskAsteriskEqualsToken:
+ case SyntaxKind.AsteriskEqualsToken:
+ case SyntaxKind.SlashEqualsToken:
+ case SyntaxKind.PercentEqualsToken:
+ case SyntaxKind.LessThanLessThanEqualsToken:
+ case SyntaxKind.GreaterThanGreaterThanEqualsToken:
+ case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
+ case SyntaxKind.AmpersandEqualsToken:
+ case SyntaxKind.CaretEqualsToken:
+ case SyntaxKind.BarEqualsToken:
+ return Associativity.Right;
+ }
+ }
+
+ return Associativity.Left;
+ }
+
+ export function getExpressionPrecedence(expression: Expression) {
+ const operator = getOperator(expression);
+ const hasArguments = expression.kind === SyntaxKind.NewExpression && (expression).arguments !== undefined;
+ return getOperatorPrecedence(expression.kind, operator, hasArguments);
+ }
+
+ function getOperator(expression: Expression) {
+ if (expression.kind === SyntaxKind.BinaryExpression) {
+ return (expression).operatorToken.kind;
+ }
+ else if (expression.kind === SyntaxKind.PrefixUnaryExpression || expression.kind === SyntaxKind.PostfixUnaryExpression) {
+ return (expression).operator;
+ }
+ else {
+ return expression.kind;
+ }
+ }
+
+ export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: SyntaxKind, hasArguments?: boolean) {
+ switch (nodeKind) {
+ case SyntaxKind.ThisKeyword:
+ case SyntaxKind.SuperKeyword:
+ case SyntaxKind.Identifier:
+ case SyntaxKind.NullKeyword:
+ case SyntaxKind.TrueKeyword:
+ case SyntaxKind.FalseKeyword:
+ case SyntaxKind.NumericLiteral:
+ case SyntaxKind.StringLiteral:
+ case SyntaxKind.ArrayLiteralExpression:
+ case SyntaxKind.ObjectLiteralExpression:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.JsxElement:
+ case SyntaxKind.JsxSelfClosingElement:
+ case SyntaxKind.RegularExpressionLiteral:
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
+ case SyntaxKind.TemplateExpression:
+ case SyntaxKind.ParenthesizedExpression:
+ return 19;
+
+ case SyntaxKind.TaggedTemplateExpression:
+ case SyntaxKind.PropertyAccessExpression:
+ case SyntaxKind.ElementAccessExpression:
+ return 18;
+
+ case SyntaxKind.NewExpression:
+ return hasArguments ? 18 : 17;
+
+ case SyntaxKind.CallExpression:
+ return 17;
+
+ case SyntaxKind.PostfixUnaryExpression:
+ return 16;
+
+ case SyntaxKind.PrefixUnaryExpression:
+ case SyntaxKind.TypeOfExpression:
+ case SyntaxKind.VoidExpression:
+ case SyntaxKind.DeleteExpression:
+ case SyntaxKind.AwaitExpression:
+ return 15;
+
+ case SyntaxKind.BinaryExpression:
+ switch (operatorKind) {
+ case SyntaxKind.ExclamationToken:
+ case SyntaxKind.TildeToken:
+ return 15;
+
+ case SyntaxKind.AsteriskAsteriskToken:
+ case SyntaxKind.AsteriskToken:
+ case SyntaxKind.SlashToken:
+ case SyntaxKind.PercentToken:
+ return 14;
+
+ case SyntaxKind.PlusToken:
+ case SyntaxKind.MinusToken:
+ return 13;
+
+ case SyntaxKind.LessThanLessThanToken:
+ case SyntaxKind.GreaterThanGreaterThanToken:
+ case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
+ return 12;
+
+ case SyntaxKind.LessThanToken:
+ case SyntaxKind.LessThanEqualsToken:
+ case SyntaxKind.GreaterThanToken:
+ case SyntaxKind.GreaterThanEqualsToken:
+ case SyntaxKind.InKeyword:
+ case SyntaxKind.InstanceOfKeyword:
+ return 11;
+
+ case SyntaxKind.EqualsEqualsToken:
+ case SyntaxKind.EqualsEqualsEqualsToken:
+ case SyntaxKind.ExclamationEqualsToken:
+ case SyntaxKind.ExclamationEqualsEqualsToken:
+ return 10;
+
+ case SyntaxKind.AmpersandToken:
+ return 9;
+
+ case SyntaxKind.CaretToken:
+ return 8;
+
+ case SyntaxKind.BarToken:
+ return 7;
+
+ case SyntaxKind.AmpersandAmpersandToken:
+ return 6;
+
+ case SyntaxKind.BarBarToken:
+ return 5;
+
+ case SyntaxKind.EqualsToken:
+ case SyntaxKind.PlusEqualsToken:
+ case SyntaxKind.MinusEqualsToken:
+ case SyntaxKind.AsteriskAsteriskEqualsToken:
+ case SyntaxKind.AsteriskEqualsToken:
+ case SyntaxKind.SlashEqualsToken:
+ case SyntaxKind.PercentEqualsToken:
+ case SyntaxKind.LessThanLessThanEqualsToken:
+ case SyntaxKind.GreaterThanGreaterThanEqualsToken:
+ case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
+ case SyntaxKind.AmpersandEqualsToken:
+ case SyntaxKind.CaretEqualsToken:
+ case SyntaxKind.BarEqualsToken:
+ return 3;
+
+ case SyntaxKind.CommaToken:
+ return 0;
+
+ default:
+ return -1;
+ }
+
+ case SyntaxKind.ConditionalExpression:
+ return 4;
+
+ case SyntaxKind.YieldExpression:
+ return 2;
+
+ case SyntaxKind.SpreadElementExpression:
+ return 1;
+
+ default:
+ return -1;
+ }
+ }
+
export function createDiagnosticCollection(): DiagnosticCollection {
let nonFileDiagnostics: Diagnostic[] = [];
const fileDiagnostics: Map = {};