Use union types to make For/ForIn statements simpler.

Conflicts:
	src/services/syntax/SyntaxGenerator.js.map
This commit is contained in:
Cyrus Najmabadi 2014-11-09 23:49:29 -08:00
parent 2288f4268f
commit 3174cbca0a
9 changed files with 76 additions and 115 deletions

View File

@ -1641,8 +1641,7 @@ var definitions = [
children: [
{ name: 'forKeyword', isToken: true, excludeFromAST: true },
{ name: 'openParenToken', isToken: true, excludeFromAST: true },
{ name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true },
{ name: 'initializer', type: 'IExpressionSyntax', isOptional: true },
{ name: 'initializer', type: 'VariableDeclarationSyntax | IExpressionSyntax', isOptional: true },
{ name: 'firstSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
{ name: 'condition', type: 'IExpressionSyntax', isOptional: true },
{ name: 'secondSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
@ -1658,10 +1657,9 @@ var definitions = [
children: [
{ name: 'forKeyword', isToken: true, excludeFromAST: true },
{ name: 'openParenToken', isToken: true, excludeFromAST: true },
{ name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true },
{ name: 'left', type: 'IExpressionSyntax', isOptional: true },
{ name: 'left', type: 'VariableDeclarationSyntax | IExpressionSyntax' },
{ name: 'inKeyword', isToken: true, excludeFromAST: true },
{ name: 'expression', type: 'IExpressionSyntax' },
{ name: 'right', type: 'IExpressionSyntax' },
{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
{ name: 'statement', type: 'IStatementSyntax' }
]

File diff suppressed because one or more lines are too long

View File

@ -1710,63 +1710,48 @@ module TypeScript.Parser {
var _currentToken = currentToken();
var tokenKind = _currentToken.kind;
if (tokenKind === SyntaxKind.VarKeyword) {
// for ( var VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement
// If we see 'for ( ;' then there is no initializer, and this must be a 'for' statement.
// If we don't see a semicolon, then parse our a variable declaration or an initializer
// expression. Both could be hte start of a 'for' or 'for-in' statement. So, after that
// check to see if we have an 'in' keyword to make the final determination as to what we
// have.
// When trying to parse either a variable declaration or an expression do not allow 'in'
// to be parsed, as that will actually be consumed by the 'for in' statement production
// instead. Also, we allow any expression here (even though the grammar only allows for
// LeftHandSideExpression). We will make sure we actually have a LHS expression in the
// grammar walker.
var initializer = tokenKind === SyntaxKind.SemicolonToken
? undefined
: tokenKind === SyntaxKind.VarKeyword
? parseVariableDeclaration(/*allowIn:*/ false)
: parseExpression(/*allowIn:*/ false);
// In order to be a 'for-in' statement, we had to have an initializer of some sort, and
// we had to actually get an 'in' keyword.
if (initializer !== undefined && currentToken().kind === SyntaxKind.InKeyword) {
// for ( var VariableDeclarationNoIn in Expression ) Statement
return parseForOrForInStatementWithVariableDeclaration(forKeyword, openParenToken);
}
else if (tokenKind === SyntaxKind.SemicolonToken) {
// for ( ; Expressionopt ; Expressionopt ) Statement
return parseForStatementWithNoVariableDeclarationOrInitializer(forKeyword, openParenToken);
// for ( LeftHandSideExpression in Expression ) Statement
return new ForInStatementSyntax(parseNodeData,
forKeyword, openParenToken, initializer, eatToken(SyntaxKind.InKeyword),
parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false));
}
else {
// for ( ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement
// for ( LeftHandSideExpression in Expression ) Statement
return parseForOrForInStatementWithInitializer(forKeyword, openParenToken);
// NOTE: From the es5 section on Automatic Semicolon Insertion.
// a semicolon is never inserted automatically if the semicolon would then ... become
// one of the two semicolons in the header of a for statement
// for (ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement
// for (var VariableDeclarationListNoIn; Expressionopt; Expressionopt) Statement
return new ForStatementSyntax(parseNodeData,
forKeyword, openParenToken, initializer,
eatToken(SyntaxKind.SemicolonToken), tryParseForStatementCondition(),
eatToken(SyntaxKind.SemicolonToken), tryParseForStatementIncrementor(),
eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false));
}
}
function parseForOrForInStatementWithVariableDeclaration(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): IStatementSyntax {
// Debug.assert(forKeyword.kind === SyntaxKind.ForKeyword && openParenToken.kind === SyntaxKind.OpenParenToken);
// Debug.assert(currentToken().kind === SyntaxKind.VarKeyword);
// for ( var VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement
// for ( var VariableDeclarationNoIn in Expression ) Statement
var variableDeclaration = parseVariableDeclaration(/*allowIn:*/ false);
return currentToken().kind === SyntaxKind.InKeyword
? parseForInStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, variableDeclaration, undefined)
: parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, variableDeclaration, undefined);
}
function parseForInStatementWithVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax): ForInStatementSyntax {
// for ( var VariableDeclarationNoIn in Expression ) Statement
return new ForInStatementSyntax(parseNodeData,
forKeyword, openParenToken, variableDeclaration, initializer, eatToken(SyntaxKind.InKeyword),
parseExpression(/*allowIn:*/ true), eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false));
}
function parseForOrForInStatementWithInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): IStatementSyntax {
// Debug.assert(forKeyword.kind === SyntaxKind.ForKeyword && openParenToken.kind === SyntaxKind.OpenParenToken);
// for ( ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement
// for ( LeftHandSideExpression in Expression ) Statement
var initializer = parseExpression(/*allowIn:*/ false);
return currentToken().kind === SyntaxKind.InKeyword
? parseForInStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, undefined, initializer)
: parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, undefined, initializer);
}
function parseForStatementWithNoVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken): ForStatementSyntax {
// Debug.assert(forKeyword.kind === SyntaxKind.ForKeyword && openParenToken.kind === SyntaxKind.OpenParenToken);
// Debug.assert(currentToken().kind === SyntaxKind.SemicolonToken);
// for ( ; Expressionopt ; Expressionopt ) Statement
return parseForStatementWithVariableDeclarationOrInitializer(forKeyword, openParenToken, /*variableDeclaration:*/ undefined, /*initializer:*/ undefined);
}
function tryParseForStatementCondition(): IExpressionSyntax {
var tokenKind = currentToken().kind;
if (tokenKind !== SyntaxKind.SemicolonToken &&
@ -1788,18 +1773,6 @@ module TypeScript.Parser {
return undefined;
}
function parseForStatementWithVariableDeclarationOrInitializer(forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax): ForStatementSyntax {
// NOTE: From the es5 section on Automatic Semicolon Insertion.
// a semicolon is never inserted automatically if the semicolon would then ... become
// one of the two semicolons in the header of a for statement
return new ForStatementSyntax(parseNodeData,
forKeyword, openParenToken, variableDeclaration, initializer,
eatToken(SyntaxKind.SemicolonToken), tryParseForStatementCondition(),
eatToken(SyntaxKind.SemicolonToken), tryParseForStatementIncrementor(),
eatToken(SyntaxKind.CloseParenToken), parseStatement(/*inErrorRecovery:*/ false));
}
function tryEatBreakOrContinueLabel(): ISyntaxToken {
// If there is no newline after the break keyword, then we can consume an optional
// identifier.

View File

@ -820,8 +820,7 @@ module TypeScript.PrettyPrinter {
this.appendToken(node.forKeyword);
this.ensureSpace();
this.appendToken(node.openParenToken);
this.appendNode(node.variableDeclaration);
this.appendElement(node.initializer);
visitNodeOrToken(this, node.initializer);
this.appendToken(node.firstSemicolonToken);
if (node.condition) {
@ -844,12 +843,11 @@ module TypeScript.PrettyPrinter {
this.appendToken(node.forKeyword);
this.ensureSpace();
this.appendToken(node.openParenToken);
this.appendNode(node.variableDeclaration);
this.appendElement(node.left);
this.ensureSpace();
this.appendToken(node.inKeyword);
this.ensureSpace();
this.appendElement(node.expression);
this.appendElement(node.right);
this.appendToken(node.closeParenToken);
this.appendBlockOrStatement(node.statement);
}

View File

@ -790,8 +790,7 @@ var definitions:ITypeDefinition[] = [
children: [
<any>{ name: 'forKeyword', isToken: true, excludeFromAST: true },
<any>{ name: 'openParenToken', isToken: true, excludeFromAST: true },
<any>{ name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true },
<any>{ name: 'initializer', type: 'IExpressionSyntax', isOptional: true },
<any>{ name: 'initializer', type: 'VariableDeclarationSyntax | IExpressionSyntax', isOptional: true },
<any>{ name: 'firstSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
<any>{ name: 'condition', type: 'IExpressionSyntax', isOptional: true },
<any>{ name: 'secondSemicolonToken', isToken: true, tokenKinds: ['SemicolonToken'], excludeFromAST: true },
@ -807,10 +806,9 @@ var definitions:ITypeDefinition[] = [
children: [
<any>{ name: 'forKeyword', isToken: true, excludeFromAST: true },
<any>{ name: 'openParenToken', isToken: true, excludeFromAST: true },
<any>{ name: 'variableDeclaration', type: 'VariableDeclarationSyntax', isOptional: true },
<any>{ name: 'left', type: 'IExpressionSyntax', isOptional: true },
<any>{ name: 'left', type: 'VariableDeclarationSyntax | IExpressionSyntax' },
<any>{ name: 'inKeyword', isToken: true, excludeFromAST: true },
<any>{ name: 'expression', type: 'IExpressionSyntax' },
<any>{ name: 'right', type: 'IExpressionSyntax' },
<any>{ name: 'closeParenToken', isToken: true, excludeFromAST: true },
<any>{ name: 'statement', type: 'IStatementSyntax' }
]

View File

@ -296,8 +296,7 @@ module TypeScript {
export interface ForStatementSyntax extends ISyntaxNode, IStatementSyntax {
forKeyword: ISyntaxToken;
openParenToken: ISyntaxToken;
variableDeclaration: VariableDeclarationSyntax;
initializer: IExpressionSyntax;
initializer: VariableDeclarationSyntax | IExpressionSyntax;
firstSemicolonToken: ISyntaxToken;
condition: IExpressionSyntax;
secondSemicolonToken: ISyntaxToken;
@ -305,19 +304,18 @@ module TypeScript {
closeParenToken: ISyntaxToken;
statement: IStatementSyntax;
}
export interface ForStatementConstructor { new (data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForStatementSyntax }
export interface ForStatementConstructor { new (data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, initializer: VariableDeclarationSyntax | IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForStatementSyntax }
export interface ForInStatementSyntax extends ISyntaxNode, IStatementSyntax {
forKeyword: ISyntaxToken;
openParenToken: ISyntaxToken;
variableDeclaration: VariableDeclarationSyntax;
left: IExpressionSyntax;
left: VariableDeclarationSyntax | IExpressionSyntax;
inKeyword: ISyntaxToken;
expression: IExpressionSyntax;
right: IExpressionSyntax;
closeParenToken: ISyntaxToken;
statement: IStatementSyntax;
}
export interface ForInStatementConstructor { new (data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, left: IExpressionSyntax, inKeyword: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForInStatementSyntax }
export interface ForInStatementConstructor { new (data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, left: VariableDeclarationSyntax | IExpressionSyntax, inKeyword: ISyntaxToken, right: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax): ForInStatementSyntax }
export interface EmptyStatementSyntax extends ISyntaxNode, IStatementSyntax {
semicolonToken: ISyntaxToken;

View File

@ -800,11 +800,10 @@ module TypeScript {
}
}
export var ForStatementSyntax: ForStatementConstructor = <any>function(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, initializer: IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) {
export var ForStatementSyntax: ForStatementConstructor = <any>function(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, initializer: VariableDeclarationSyntax | IExpressionSyntax, firstSemicolonToken: ISyntaxToken, condition: IExpressionSyntax, secondSemicolonToken: ISyntaxToken, incrementor: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) {
if (data) { this.__data = data; }
this.forKeyword = forKeyword,
this.openParenToken = openParenToken,
this.variableDeclaration = variableDeclaration,
this.initializer = initializer,
this.firstSemicolonToken = firstSemicolonToken,
this.condition = condition,
@ -814,7 +813,6 @@ module TypeScript {
this.statement = statement,
forKeyword.parent = this,
openParenToken.parent = this,
variableDeclaration && (variableDeclaration.parent = this),
initializer && (initializer.parent = this),
firstSemicolonToken.parent = this,
condition && (condition.parent = this),
@ -824,53 +822,49 @@ module TypeScript {
statement.parent = this;
};
ForStatementSyntax.prototype.kind = SyntaxKind.ForStatement;
ForStatementSyntax.prototype.childCount = 10;
ForStatementSyntax.prototype.childCount = 9;
ForStatementSyntax.prototype.childAt = function(index: number): ISyntaxElement {
switch (index) {
case 0: return this.forKeyword;
case 1: return this.openParenToken;
case 2: return this.variableDeclaration;
case 3: return this.initializer;
case 4: return this.firstSemicolonToken;
case 5: return this.condition;
case 6: return this.secondSemicolonToken;
case 7: return this.incrementor;
case 8: return this.closeParenToken;
case 9: return this.statement;
case 2: return this.initializer;
case 3: return this.firstSemicolonToken;
case 4: return this.condition;
case 5: return this.secondSemicolonToken;
case 6: return this.incrementor;
case 7: return this.closeParenToken;
case 8: return this.statement;
}
}
export var ForInStatementSyntax: ForInStatementConstructor = <any>function(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, variableDeclaration: VariableDeclarationSyntax, left: IExpressionSyntax, inKeyword: ISyntaxToken, expression: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) {
export var ForInStatementSyntax: ForInStatementConstructor = <any>function(data: number, forKeyword: ISyntaxToken, openParenToken: ISyntaxToken, left: VariableDeclarationSyntax | IExpressionSyntax, inKeyword: ISyntaxToken, right: IExpressionSyntax, closeParenToken: ISyntaxToken, statement: IStatementSyntax) {
if (data) { this.__data = data; }
this.forKeyword = forKeyword,
this.openParenToken = openParenToken,
this.variableDeclaration = variableDeclaration,
this.left = left,
this.inKeyword = inKeyword,
this.expression = expression,
this.right = right,
this.closeParenToken = closeParenToken,
this.statement = statement,
forKeyword.parent = this,
openParenToken.parent = this,
variableDeclaration && (variableDeclaration.parent = this),
left && (left.parent = this),
left.parent = this,
inKeyword.parent = this,
expression.parent = this,
right.parent = this,
closeParenToken.parent = this,
statement.parent = this;
};
ForInStatementSyntax.prototype.kind = SyntaxKind.ForInStatement;
ForInStatementSyntax.prototype.childCount = 8;
ForInStatementSyntax.prototype.childCount = 7;
ForInStatementSyntax.prototype.childAt = function(index: number): ISyntaxElement {
switch (index) {
case 0: return this.forKeyword;
case 1: return this.openParenToken;
case 2: return this.variableDeclaration;
case 3: return this.left;
case 4: return this.inKeyword;
case 5: return this.expression;
case 6: return this.closeParenToken;
case 7: return this.statement;
case 2: return this.left;
case 3: return this.inKeyword;
case 4: return this.right;
case 5: return this.closeParenToken;
case 6: return this.statement;
}
}

View File

@ -1158,7 +1158,7 @@ module TypeScript {
}
private checkForInLeftHandSideExpression(node: ForInStatementSyntax): boolean {
if (node.left && !SyntaxUtilities.isLeftHandSizeExpression(node.left)) {
if (node.left.kind !== SyntaxKind.VariableDeclaration && !SyntaxUtilities.isLeftHandSizeExpression(node.left)) {
this.pushDiagnostic(node.left, DiagnosticCode.Invalid_left_hand_side_in_for_in_statement);
return true;
}
@ -1170,8 +1170,8 @@ module TypeScript {
// The parser accepts a Variable Declaration in a ForInStatement, but the grammar only
// allows a very restricted form. Specifically, there must be only a single Variable
// Declarator in the Declaration.
if (node.variableDeclaration && node.variableDeclaration.variableDeclarators.length > 1) {
this.pushDiagnostic(node.variableDeclaration, DiagnosticCode.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement);
if (node.left.kind === SyntaxKind.VariableDeclaration && (<VariableDeclarationSyntax>node.left).variableDeclarators.length > 1) {
this.pushDiagnostic(node.left, DiagnosticCode.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement);
return true;
}

View File

@ -276,7 +276,6 @@ module TypeScript {
public visitForStatement(node: ForStatementSyntax): void {
this.visitToken(node.forKeyword);
this.visitToken(node.openParenToken);
visitNodeOrToken(this, node.variableDeclaration);
visitNodeOrToken(this, node.initializer);
this.visitToken(node.firstSemicolonToken);
visitNodeOrToken(this, node.condition);
@ -289,10 +288,9 @@ module TypeScript {
public visitForInStatement(node: ForInStatementSyntax): void {
this.visitToken(node.forKeyword);
this.visitToken(node.openParenToken);
visitNodeOrToken(this, node.variableDeclaration);
visitNodeOrToken(this, node.left);
this.visitToken(node.inKeyword);
visitNodeOrToken(this, node.expression);
visitNodeOrToken(this, node.right);
this.visitToken(node.closeParenToken);
visitNodeOrToken(this, node.statement);
}