Skip to content

[ES7] Exponentiation #4914

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Oct 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
04ed892
Add tests for exponentiation
Sep 19, 2015
76ef7b4
Initial operator. Need to fix the grammar for unaryExpression
Sep 19, 2015
21d0369
Basic parsing for the UnaryExpression: IncrementExpression[?Yield]**…
Sep 19, 2015
1140eb8
Parse ES7 UnaryExpression and IncrementExpression
Sep 21, 2015
072089f
Downlevel emit for **=
Sep 21, 2015
31b8736
Update existed baselines that are affected by ES7 UnaryExpression
Sep 21, 2015
4fc74b2
Add and update tests for exponentiation
Sep 21, 2015
4037255
Update baselines to include new tests
Sep 21, 2015
a3f5666
remove unnecessary union
Sep 21, 2015
df18dfc
Address PR
Sep 25, 2015
5f7914c
Downlevel emit
Oct 3, 2015
ca5da90
Add tests
Oct 3, 2015
bf0903b
Address PR on fixing tempalte tests
Oct 3, 2015
1326ba9
Update grammar to error on none-parenthesis
Oct 3, 2015
ce7a054
Update parser with new grammar
Oct 5, 2015
69dc707
Update parse to use new grammar
Oct 6, 2015
7b3de84
Update test harness to pick up new ScriptTarget of ES7
Oct 6, 2015
80cdfd4
Fix emitting parenthesis when downlevel
Oct 6, 2015
a00e90c
Add and update tests
Oct 6, 2015
f8d6b34
Update baselines for exponentiation compound operator with indexing a…
Oct 6, 2015
bf970be
Add baselines for downlevel emit exponentiation and ES7 emit of expon…
Oct 6, 2015
46d799e
Add baselines for using exponentiation in template string
Oct 6, 2015
d0aaf41
Add baselines when having early syntax error in exponentiation
Oct 6, 2015
bd7cc1e
Invalid usage of exponentiation
Oct 6, 2015
fbe559e
Address PR and add new with exponentiation
Oct 6, 2015
788f222
parenthesized exponentiation with template string
Oct 6, 2015
bfaa51b
Add comment and address PR on comment
Oct 6, 2015
d96a00e
Parse typeAssertion expression as simpleUnaryExpression
Oct 6, 2015
8be77b4
Update test to parse typeAssertion as simpleUnaryExpression
Oct 6, 2015
37db03a
Address feedback on diagnostic message
Oct 7, 2015
3b8cdb6
Address comment to increment emitCount in separate line
Oct 7, 2015
75de6d4
Add comment
Oct 8, 2015
cd3f711
Address PR feedback
Oct 8, 2015
af5dc3e
Address PR feedback. Always emit parentheses around emit capturing
Oct 8, 2015
5e921c1
Address PR feedback, remove scriptTarget ES7
Oct 9, 2015
1fc11aa
Address PR feedback, add comment
Oct 9, 2015
d57ee1d
Merge branch 'master' into errorsuperbeforethis
Oct 9, 2015
d3e10b3
Merge remote-tracking branch 'origin/master' into exponentiation
Oct 12, 2015
2918f9d
Update baseline after merge conflict
Oct 12, 2015
6a62c01
Merge branch 'exponentiation' of https://github.com/Microsoft/TypeScr…
Oct 12, 2015
e405cce
fix linter failure
Oct 12, 2015
9025879
Address PR feedback
Oct 12, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 1 addition & 28 deletions lib/lib.core.es6.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3965,34 +3965,7 @@ interface ObjectConstructor {
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source The source object from which to copy properties.
*/
assign<T, U>(target: T, source: U): T & U;

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source1 The first source object from which to copy properties.
* @param source2 The second source object from which to copy properties.
*/
assign<T, U, V>(target: T, source1: U, source2: V): T & U & V;

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source1 The first source object from which to copy properties.
* @param source2 The second source object from which to copy properties.
* @param source3 The third source object from which to copy properties.
*/
assign<T, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;

/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param sources One or more source objects from which to copy properties
* @param sources One or more source objects to copy properties from.
*/
assign(target: any, ...sources: any[]): any;

Expand Down
6 changes: 4 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10042,7 +10042,9 @@ namespace ts {
let rightType = checkExpression(node.right, contextualMapper);
switch (operator) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.AsteriskAsteriskToken:
case SyntaxKind.AsteriskEqualsToken:
case SyntaxKind.AsteriskAsteriskEqualsToken:
case SyntaxKind.SlashToken:
case SyntaxKind.SlashEqualsToken:
case SyntaxKind.PercentToken:
Expand All @@ -10061,7 +10063,7 @@ namespace ts {
case SyntaxKind.CaretEqualsToken:
case SyntaxKind.AmpersandToken:
case SyntaxKind.AmpersandEqualsToken:
// TypeScript 1.0 spec (April 2014): 4.15.1
// TypeScript 1.0 spec (April 2014): 4.19.1
// These operators require their operands to be of type Any, the Number primitive type,
// or an enum type. Operands of an enum type are treated
// as having the primitive type Number. If one operand is the null or undefined value,
Expand Down Expand Up @@ -10090,7 +10092,7 @@ namespace ts {
return numberType;
case SyntaxKind.PlusToken:
case SyntaxKind.PlusEqualsToken:
// TypeScript 1.0 spec (April 2014): 4.15.2
// TypeScript 1.0 spec (April 2014): 4.19.2
// The binary + operator requires both operands to be of the Number primitive type or an enum type,
// or at least one of the operands to be of type Any or the String primitive type.

Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2469,5 +2469,13 @@
"A constructor cannot contain a 'super' call when its class extends 'null'": {
"category": "Error",
"code": 17005
},
"An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": {
"category": "Error",
"code": 17006
},
"A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": {
"category": "Error",
"code": 17007
}
}
184 changes: 141 additions & 43 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,68 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}

/**
* Emit ES7 exponentiation operator downlevel using Math.pow
* @param node a binary expression node containing exponentiationOperator (**, **=)
*/
function emitExponentiationOperator(node: BinaryExpression) {
let leftHandSideExpression = node.left;
if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
let synthesizedLHS: ElementAccessExpression | PropertyAccessExpression;
let shouldEmitParentheses = false;
if (isElementAccessExpression(leftHandSideExpression)) {
shouldEmitParentheses = true;
write("(");

synthesizedLHS = <ElementAccessExpression>createSynthesizedNode(SyntaxKind.ElementAccessExpression, /*startsOnNewLine*/ false);

let identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefinedTempVariablesInPlaces*/ false, /*shouldEmitCommaBeforeAssignment*/ false);
synthesizedLHS.expression = identifier;

if (leftHandSideExpression.argumentExpression.kind !== SyntaxKind.NumericLiteral &&
leftHandSideExpression.argumentExpression.kind !== SyntaxKind.StringLiteral) {
let tempArgumentExpression = createAndRecordTempVariable(TempFlags._i);
(<ElementAccessExpression>synthesizedLHS).argumentExpression = tempArgumentExpression;
emitAssignment(tempArgumentExpression, leftHandSideExpression.argumentExpression, /*shouldEmitCommaBeforeAssignment*/ true);
}
else {
(<ElementAccessExpression>synthesizedLHS).argumentExpression = leftHandSideExpression.argumentExpression;
}
write(", ");
}
else if (isPropertyAccessExpression(leftHandSideExpression)) {
shouldEmitParentheses = true;
write("(");
synthesizedLHS = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression, /*startsOnNewLine*/ false);

let identifier = emitTempVariableAssignment(leftHandSideExpression.expression, /*canDefinedTempVariablesInPlaces*/ false, /*shouldemitCommaBeforeAssignment*/ false);
synthesizedLHS.expression = identifier;

(<PropertyAccessExpression>synthesizedLHS).dotToken = leftHandSideExpression.dotToken;
(<PropertyAccessExpression>synthesizedLHS).name = leftHandSideExpression.name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of this looks duplicated, couldn't you consolidate it further?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand what you mean

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parts of the if and the else if look the same.

write(", ");
}

emit(synthesizedLHS || leftHandSideExpression);
write(" = ");
write("Math.pow(");
emit(synthesizedLHS || leftHandSideExpression);
write(", ");
emit(node.right);
write(")");
if (shouldEmitParentheses) {
write(")");
}
}
else {
write("Math.pow(");
emit(leftHandSideExpression);
write(", ");
emit(node.right);
write(")");
}
}

function emitBinaryExpression(node: BinaryExpression) {
if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken &&
(node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
Expand All @@ -2795,12 +2857,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitNodeWithoutSourceMap(node.left);
write(`", `);
}
emit(node.left);
let indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined);
write(tokenToString(node.operatorToken.kind));
let indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " ");
emit(node.right);
decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator);

if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) {
// Downleveled emit exponentiation operator using Math.pow
emitExponentiationOperator(node);
}
else {
emit(node.left);
// Add indentation before emit the operator if the operator is on different line
// For example:
// 3
// + 2;
// emitted as
// 3
// + 2;
let indentedBeforeOperator = indentIfOnDifferentLines(node, node.left, node.operatorToken, node.operatorToken.kind !== SyntaxKind.CommaToken ? " " : undefined);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this last parameter supposed to be?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an already existed code. I believe what it does is toe indent the operator if it is on different line

3
+2;

will be

3
    + 2;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure; can you leave a comment specifying what the parameter name is, and potentially break the call into several lines?

write(tokenToString(node.operatorToken.kind));
let indentedAfterOperator = indentIfOnDifferentLines(node, node.operatorToken, node.right, " ");
emit(node.right);
decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator);
}

if (exportChanged) {
write(")");
}
Expand Down Expand Up @@ -3437,6 +3514,58 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(";");
}

/**
* Emit an assignment to a given identifier, 'name', with a given expression, 'value'.
* @param name an identifier as a left-hand-side operand of the assignment
* @param value an expression as a right-hand-side operand of the assignment
* @param shouldEmitCommaBeforeAssignment a boolean indicating whether to prefix an assignment with comma
*/
function emitAssignment(name: Identifier, value: Expression, shouldEmitCommaBeforeAssignment: boolean) {
if (shouldEmitCommaBeforeAssignment) {
write(", ");
}

let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name);

if (exportChanged) {
write(`${exportFunctionForFile}("`);
emitNodeWithCommentsAndWithoutSourcemap(name);
write(`", `);
}

const isVariableDeclarationOrBindingElement =
name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement);

if (isVariableDeclarationOrBindingElement) {
emitModuleMemberName(<Declaration>name.parent);
}
else {
emit(name);
}

write(" = ");
emit(value);

if (exportChanged) {
write(")");
}
}

/**
* Create temporary variable, emit an assignment of the variable the given expression
* @param expression an expression to assign to the newly created temporary variable
* @param canDefineTempVariablesInPlace a boolean indicating whether you can define the temporary variable at an assignment location
* @param shouldEmitCommaBeforeAssignment a boolean indicating whether an assignment should prefix with comma
*/
function emitTempVariableAssignment(expression: Expression, canDefineTempVariablesInPlace: boolean, shouldEmitCommaBeforeAssignment: boolean): Identifier {
let identifier = createTempVariable(TempFlags.Auto);
if (!canDefineTempVariablesInPlace) {
recordTempDeclaration(identifier);
}
emitAssignment(identifier, expression, shouldEmitCommaBeforeAssignment);
return identifier;
}

function emitDestructuring(root: BinaryExpression | VariableDeclaration | ParameterDeclaration, isAssignmentExpressionStatement: boolean, value?: Expression) {
let emitCount = 0;

Expand All @@ -3462,36 +3591,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitBindingElement(<BindingElement>root, value);
}

function emitAssignment(name: Identifier, value: Expression) {
if (emitCount++) {
write(", ");
}

const isVariableDeclarationOrBindingElement =
name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement);

let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name);

if (exportChanged) {
write(`${exportFunctionForFile}("`);
emitNodeWithCommentsAndWithoutSourcemap(name);
write(`", `);
}

if (isVariableDeclarationOrBindingElement) {
emitModuleMemberName(<Declaration>name.parent);
}
else {
emit(name);
}

write(" = ");
emit(value);

if (exportChanged) {
write(")");
}
}

/**
* Ensures that there exists a declared identifier whose value holds the given expression.
Expand All @@ -3507,11 +3606,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return expr;
}

let identifier = createTempVariable(TempFlags.Auto);
if (!canDefineTempVariablesInPlace) {
recordTempDeclaration(identifier);
}
emitAssignment(identifier, expr);
let identifier = emitTempVariableAssignment(expr, canDefineTempVariablesInPlace, emitCount > 0);
emitCount++;
return identifier;
}

Expand Down Expand Up @@ -3611,7 +3707,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value);
}
else {
emitAssignment(<Identifier>target, value);
emitAssignment(<Identifier>target, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0);
emitCount++;
}
}

Expand Down Expand Up @@ -3680,7 +3777,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}
else {
emitAssignment(<Identifier>target.name, value);
emitAssignment(<Identifier>target.name, value, /*shouldEmitCommaBeforeAssignment*/ emitCount > 0);
emitCount++;
}
}
}
Expand Down
Loading