Adds the transformFiles API

This commit is contained in:
Ron Buckton
2016-02-04 18:14:39 -08:00
parent ba9181c81c
commit 1e35593437
6 changed files with 792 additions and 13 deletions
+218 -13
View File
@@ -1,11 +1,12 @@
/// <reference path="core.ts"/>
/// <reference path="utilities.ts"/>
/* @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<T extends Node>(elements?: T[], pos?: number, end?: number): NodeArray<T> {
const array = <NodeArray<T>>(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 = <ModifiersArray>(elements || []);
array.pos = pos;
@@ -33,19 +36,16 @@ namespace ts {
return array;
}
/* @internal */
export function createSynthesizedNode(kind: SyntaxKind, startsOnNewLine?: boolean): Node {
const node = <SynthesizedNode>createNode(kind, /*pos*/ -1, /*end*/ -1);
node.startsOnNewLine = startsOnNewLine;
return node;
}
/* @internal */
export function createSynthesizedNodeArray<T extends Node>(elements?: T[]): NodeArray<T> {
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<T extends Node>(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<T extends Node>(elements: T[]): NodeArrayNode<T> {
const node = <NodeArrayNode<T>>createSynthesizedNode(SyntaxKind.NodeArrayNode);
node.nodes = createNodeArray(elements);
return node;
}
/* @internal */
export function createReturn(expression?: Expression): ReturnStatement {
const node = <ReturnStatement>createSynthesizedNode(SyntaxKind.ReturnStatement);
node.expression = expression;
return node;
}
/* @internal */
export function createStatement(expression: Expression): ExpressionStatement {
const node = <ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
node.expression = expression;
return node;
}
/* @internal */
export function createVariableStatement(declarationList: VariableDeclarationList): VariableStatement {
const node = <VariableStatement>createSynthesizedNode(SyntaxKind.VariableStatement);
node.declarationList = declarationList;
return node;
}
/* @internal */
export function createVariableDeclarationList(declarations: VariableDeclaration[]): VariableDeclarationList {
const node = <VariableDeclarationList>createSynthesizedNode(SyntaxKind.VariableDeclarationList);
node.declarations = createNodeArray(declarations);
return node;
}
/* @internal */
export function createBlock(statements: Statement[]): Block {
const block = <Block>createSynthesizedNode(SyntaxKind.Block);
block.statements = createNodeArray(statements);
return block;
}
export function createVariableDeclaration(name: BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration {
const node = <VariableDeclaration>allocateNode(SyntaxKind.VariableDeclaration, location);
node.name = name;
node.initializer = initializer;
return node;
}
export function createIdentifier(text: string): Identifier {
const node = <Identifier>allocateNode(SyntaxKind.Identifier);
node.text = text;
return node;
}
export function createTempVariable(tempKind: TempVariableKind): Identifier {
const name = <Identifier>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<T extends PrimaryExpression>(value: string | number | boolean | void): T {
if (typeof value === "string") {
const node = <T & StringLiteral>allocateNode(SyntaxKind.StringLiteral);
node.text = value;
return node;
}
else if (typeof value === "number") {
const node = <T & LiteralExpression>allocateNode(SyntaxKind.NumericLiteral);
node.text = value.toString();
return node;
}
else if (typeof value === "boolean") {
return <T>allocateNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword);
}
else if (value === null) {
return <T>allocateNode(SyntaxKind.NullKeyword);
}
}
export function createVoid(expression: UnaryExpression) {
const node = <VoidExpression>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 = <PropertyAccessExpression>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 = <ElementAccessExpression>allocateNode(SyntaxKind.ElementAccessExpression);
node.expression = parenthesizeForAccess(expression);
node.argumentExpression = coerceExpression(index);
return node;
}
export function createConditional(condition: Expression, whenTrue: Expression, whenFalse: Expression) {
const node = <ConditionalExpression>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 = <BinaryExpression>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 <Expression>createBinary(left, SyntaxKind.CommaToken, right);
}
export function createCall(expression: Expression, argumentsArray: Expression[]) {
const node = <CallExpression>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 = <ParenthesizedExpression>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 = (<AssertionExpression>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
&& (<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 = (<AssertionExpression>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 <LeftHandSideExpression>expr;
}
return parenthesizeExpression(expr);
}
}
+12
View File
@@ -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<T>(cbNode: (node: Node) => T, node: Node): T {
if (node) {
return cbNode(node);
+331
View File
@@ -0,0 +1,331 @@
/// <reference path="visitor.ts" />
/* @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<string> = {};
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((<Identifier>node).text);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
return generateNameForModuleOrEnum(<ModuleDeclaration | EnumDeclaration>node);
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ExportDeclaration:
return generateNameForImportOrExportDeclaration(<ImportDeclaration | ExportDeclaration>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((<LiteralExpression>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<T, U>(...args: ((t: T) => (u: U) => U)[]): (t: T) => (u: U) => U;
function chain<T, U>(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<T>(...args: ((t: T) => T)[]): (t: T) => T;
function compose<T>(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<T>(arrayLike: ArrayLike<T>) {
const array: T[] = [];
for (let i = 0; i < arrayLike.length; i++) {
array[i] = arrayLike[i];
}
return array;
}
}
+1
View File
@@ -19,6 +19,7 @@
"checker.ts",
"factory.ts",
"visitor.ts",
"transformer.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
+34
View File
@@ -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;
+196
View File
@@ -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 && (<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 && (<NewExpression>expression).arguments !== undefined;
return getOperatorPrecedence(expression.kind, operator, hasArguments);
}
function getOperator(expression: Expression) {
if (expression.kind === SyntaxKind.BinaryExpression) {
return (<BinaryExpression>expression).operatorToken.kind;
}
else if (expression.kind === SyntaxKind.PrefixUnaryExpression || expression.kind === SyntaxKind.PostfixUnaryExpression) {
return (<PrefixUnaryExpression | PostfixUnaryExpression>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<Diagnostic[]> = {};