From be1de2324804cc38545986cfaf660225eab138c0 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 16 Oct 2015 17:57:41 -0700 Subject: [PATCH] Modified associativity rules due to addition of exponentiation operator. --- src/compiler/factory.ts | 69 ++++++++++++++++++++++----------------- src/compiler/utilities.ts | 50 ++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 30 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c66f4cbae1b..015a787c69b 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -135,22 +135,7 @@ namespace ts { return modifiers; } - - // export function createSourceFileNode(): SourceFile { - // let node = createNode(SyntaxKind.SourceFile); - // return node; - // } - - // export function updateSourceFileNode(node: SourceFile, statements: NodeArray, endOfFileToken: Node): SourceFile { - // if (statements !== node.statements || endOfFileToken !== node.endOfFileToken) { - // let newNode = createNode(SyntaxKind.SourceFile); - // newNode.statements = statements; - // newNode.endOfFileToken = endOfFileToken; - // return updateFrom(node, newNode); - // } - // return node; - // } - + export function createNumericLiteral2(value: number, location?: TextRange, flags?: NodeFlags): LiteralExpression { let node = createNumericLiteral(String(value), location, flags); return node; @@ -168,30 +153,54 @@ namespace ts { return nodeIsSynthesized(node) ? node : cloneNode(node); } - export function parenthesizeForBinary(expr: Expression, operator: SyntaxKind) { + const enum BinaryOperandSide { + Left, + Right + } + + function parenthesizeForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperandSide) { // 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; + 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 (isParenthesizedExpression(expr)) { - return expr; + if (isParenthesizedExpression(operand)) { + return operand; } - let exprPrecedence = getExpressionPrecedence(expr); + return needsParenthesesForBinary(operand, operator, side) + ? createParenthesizedExpression(operand) + : operand; + } + + function needsParenthesesForBinary(operand: Expression, operator: SyntaxKind, side: BinaryOperandSide) { + let operandPrecedence = getExpressionPrecedence(operand); let operatorPrecedence = getBinaryOperatorPrecedence(operator); - if (exprPrecedence < operatorPrecedence) { - // lower precedence, the expression needs parenthesis - return createParenthesizedExpression(expr); - } - else { - // higher precedence. - return expr; + 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: BinaryOperandSide) { + return side === BinaryOperandSide.Left + && getExpressionAssociativity(operand) === Associativity.Right; + } + + function isModuloOperandOnRightHandSide(operand: Expression, operator: SyntaxKind, side: BinaryOperandSide) { + return side === BinaryOperandSide.Right + && operator !== SyntaxKind.PercentToken + && isBinaryExpression(operand) + && operand.operatorToken.kind === SyntaxKind.PercentToken; + } + export 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. @@ -262,7 +271,7 @@ namespace ts { } export function createBinaryExpression2(left: Expression, operator: SyntaxKind, right: Expression, location?: TextRange) { - return createBinaryExpression(parenthesizeForBinary(left, operator), createNode(operator), parenthesizeForBinary(right, operator), location); + return createBinaryExpression(parenthesizeForBinary(left, operator, BinaryOperandSide.Left), createNode(operator), parenthesizeForBinary(right, operator, BinaryOperandSide.Right), location); } export function createConditionalExpression2(condition: Expression, whenTrue: Expression, whenFalse: Expression, location?: TextRange, flags?: NodeFlags) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 10217af3c88..fd2eefa6c3a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1072,6 +1072,56 @@ namespace ts { } } + export const enum Associativity { + Left, + Right + } + + export function getExpressionAssociativity(expr: Expression) { + return getOperatorAssociativity(expr.kind, getOperator(expr), isNewExpression(expr) && !expr.arguments); + } + + export function getBinaryOperatorAssociativity(operator: SyntaxKind) { + return getOperatorAssociativity(SyntaxKind.BinaryExpression, operator); + } + + export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, isNewExpressionWithoutArguments?: boolean) { + switch (kind) { + case SyntaxKind.NewExpression: + return isNewExpressionWithoutArguments ? Associativity.Right : Associativity.Left; + + 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(expr: Expression) { return getOperatorPrecedence(expr.kind, getOperator(expr), isNewExpression(expr) && !expr.arguments) }