diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b9f8857cb23..caf07cbf4dc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8447,6 +8447,13 @@ module ts { checkSourceElement(node.statement); } + function checkForOfStatement(node: ForOfStatement) { + // TODO: not yet implemented + if (!checkGrammarForStatementInAmbientContext(node)) { + checkGrammarForOfStatement(node); + } + } + function checkForInStatement(node: ForInStatement) { // Grammar checking if (!checkGrammarForStatementInAmbientContext(node)) { @@ -9450,6 +9457,8 @@ module ts { return checkForStatement(node); case SyntaxKind.ForInStatement: return checkForInStatement(node); + case SyntaxKind.ForOfStatement: + return checkForOfStatement(node); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return checkBreakOrContinueStatement(node); @@ -10755,6 +10764,12 @@ module ts { } } + function checkGrammarForOfStatement(forOfStatement: ForOfStatement): boolean { + if (languageVersion < ScriptTarget.ES6) { + return grammarErrorOnFirstToken(forOfStatement, Diagnostics.For_of_statements_are_only_available_when_targeting_ECMAScript_6_or_higher); + } + } + function checkGrammarAccessor(accessor: MethodDeclaration): boolean { var kind = accessor.kind; if (languageVersion < ScriptTarget.ES5) { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 2c64f8ff4b0..a661f53ddfc 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -313,6 +313,7 @@ module ts { Property_0_does_not_exist_on_const_enum_1: { code: 2475, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on 'const' enum '{1}'." }, let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations: { code: 2476, category: DiagnosticCategory.Error, key: "'let' is not allowed to be used as a name in 'let' or 'const' declarations." }, Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1: { code: 2477, category: DiagnosticCategory.Error, key: "Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'." }, + For_of_statements_are_only_available_when_targeting_ECMAScript_6_or_higher: { code: 2482, category: DiagnosticCategory.Error, key: "For-of statements are only available when targeting ECMAScript 6 or higher" }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8299e9f7733..d59d4fe0d8e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1244,6 +1244,10 @@ "category": "Error", "code": 2477 }, + "For-of statements are only available when targeting ECMAScript 6 or higher": { + "category": "Error", + "code": 2482 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", @@ -1520,7 +1524,7 @@ "Exported type alias '{0}' has or is using private name '{1}'.": { "category": "Error", "code": 4081 - }, + }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 6a2338268e8..8aa2fb72df3 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2911,7 +2911,7 @@ module ts { emitEmbeddedStatement(node.statement); } - function emitForInStatement(node: ForInStatement) { + function emitForInOrForOfStatement(node: ForInStatement | ForOfStatement) { var endPos = emitToken(SyntaxKind.ForKeyword, node.pos); write(" "); endPos = emitToken(SyntaxKind.OpenParenToken, endPos); @@ -2932,7 +2932,13 @@ module ts { else { emit(node.initializer); } - write(" in "); + + if (node.kind === SyntaxKind.ForInStatement) { + write(" in "); + } + else { + write(" of "); + } emit(node.expression); emitToken(SyntaxKind.CloseParenToken, node.expression.end); emitEmbeddedStatement(node.statement); @@ -4351,8 +4357,9 @@ module ts { return emitWhileStatement(node); case SyntaxKind.ForStatement: return emitForStatement(node); + case SyntaxKind.ForOfStatement: case SyntaxKind.ForInStatement: - return emitForInStatement(node); + return emitForInOrForOfStatement(node); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return emitBreakOrContinueStatement(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 630418dc485..a90a4a9ab62 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1599,7 +1599,7 @@ module ts { // in the case where we're parsing the variable declarator of a 'for-in' statement, we // are done if we see an 'in' keyword in front of us. - if (token === SyntaxKind.InKeyword) { + if (isInOrOfKeyword(token)) { return true; } @@ -3159,6 +3159,10 @@ module ts { return parseBinaryExpressionRest(precedence, leftOperand); } + function isInOrOfKeyword(t: SyntaxKind) { + return t === SyntaxKind.InKeyword || t === SyntaxKind.OfKeyword; + } + function parseBinaryExpressionRest(precedence: number, leftOperand: Expression): Expression { while (true) { // We either have a binary operator here, or we're finished. We call @@ -3788,7 +3792,7 @@ module ts { return finishNode(node); } - function parseForOrForInStatement(): Statement { + function parseForOrForInOrForOfStatement(): Statement { var pos = getNodePos(); parseExpected(SyntaxKind.ForKeyword); parseExpected(SyntaxKind.OpenParenToken); @@ -3802,15 +3806,21 @@ module ts { initializer = disallowInAnd(parseExpression); } } - var forOrForInStatement: IterationStatement; + var forOrForInOrForOfStatement: IterationStatement; if (parseOptional(SyntaxKind.InKeyword)) { var forInStatement = createNode(SyntaxKind.ForInStatement, pos); forInStatement.initializer = initializer; forInStatement.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); - forOrForInStatement = forInStatement; + forOrForInOrForOfStatement = forInStatement; } - else { + else if (parseOptional(SyntaxKind.OfKeyword)) { + var forOfStatement = createNode(SyntaxKind.ForOfStatement, pos); + forOfStatement.initializer = initializer; + forOfStatement.expression = allowInAnd(parseAssignmentExpressionOrHigher); + parseExpected(SyntaxKind.CloseParenToken); + forOrForInOrForOfStatement = forOfStatement; + } else { var forStatement = createNode(SyntaxKind.ForStatement, pos); forStatement.initializer = initializer; parseExpected(SyntaxKind.SemicolonToken); @@ -3822,12 +3832,12 @@ module ts { forStatement.iterator = allowInAnd(parseExpression); } parseExpected(SyntaxKind.CloseParenToken); - forOrForInStatement = forStatement; + forOrForInOrForOfStatement = forStatement; } - forOrForInStatement.statement = parseStatement(); + forOrForInOrForOfStatement.statement = parseStatement(); - return finishNode(forOrForInStatement); + return finishNode(forOrForInOrForOfStatement); } function parseBreakOrContinueStatement(kind: SyntaxKind): BreakOrContinueStatement { @@ -4073,7 +4083,7 @@ module ts { case SyntaxKind.WhileKeyword: return parseWhileStatement(); case SyntaxKind.ForKeyword: - return parseForOrForInStatement(); + return parseForOrForInOrForOfStatement(); case SyntaxKind.ContinueKeyword: return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement); case SyntaxKind.BreakKeyword: @@ -4215,7 +4225,9 @@ module ts { var node = createNode(SyntaxKind.VariableDeclaration); node.name = parseIdentifierOrPattern(); node.type = parseTypeAnnotation(); - node.initializer = parseInitializer(/*inParameter*/ false); + if (!isInOrOfKeyword(token)) { + node.initializer = parseInitializer(/*inParameter*/ false); + } return finishNode(node); } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index fbbd69bbd69..7700d2389a0 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -93,6 +93,7 @@ module ts { "while": SyntaxKind.WhileKeyword, "with": SyntaxKind.WithKeyword, "yield": SyntaxKind.YieldKeyword, + "of": SyntaxKind.OfKeyword, "{": SyntaxKind.OpenBraceToken, "}": SyntaxKind.CloseBraceToken, "(": SyntaxKind.OpenParenToken, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 12c5af21e12..04b44a5c1c0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -141,7 +141,7 @@ module ts { SetKeyword, StringKeyword, TypeKeyword, - + OfKeyword, // Parse tree nodes // Names @@ -210,6 +210,7 @@ module ts { WhileStatement, ForStatement, ForInStatement, + ForOfStatement, ContinueStatement, BreakStatement, ReturnStatement, @@ -259,7 +260,7 @@ module ts { FirstReservedWord = BreakKeyword, LastReservedWord = WithKeyword, FirstKeyword = BreakKeyword, - LastKeyword = TypeKeyword, + LastKeyword = OfKeyword, FirstFutureReservedWord = ImplementsKeyword, LastFutureReservedWord = YieldKeyword, FirstTypeNode = TypeReference, @@ -752,6 +753,11 @@ module ts { expression: Expression; } + export interface ForOfStatement extends IterationStatement { + initializer: VariableDeclarationList | Expression; + expression: Expression; + } + export interface BreakOrContinueStatement extends Statement { label?: Identifier; }