/// <summary> /// Parses a for statement or a for-in statement. /// </summary> /// <returns> A for statement or a for-in statement. </returns> private Statement ParseFor() { // Consume the for keyword. this.Expect(KeywordToken.For); // Read the left parenthesis. this.Expect(PunctuatorToken.LeftParenthesis); // The initialization statement. Statement initializationStatement = null; // The for-in expression needs a variable to assign to. Is null for a regular for statement. IReferenceExpression forInReference = null; // Keep track of the start of the statement so that source debugging works correctly. var start = this.PositionAfterWhitespace; if (this.nextToken == KeywordToken.Var) { // Read past the var token. this.Expect(KeywordToken.Var); // There can be multiple initializers (but not for for-in statements). var varStatement = new VarStatement(this.labelsForCurrentStatement, this.currentScope); initializationStatement = varStatement; // Only a simple variable name is allowed for for-in statements. bool cannotBeForIn = false; while (true) { var declaration = new VariableDeclaration(); // The next token must be a variable name. declaration.VariableName = this.ExpectIdentifier(); ValidateVariableName(declaration.VariableName); // Add the variable to the current function's list of local variables. this.currentScope.DeclareVariable(declaration.VariableName, this.context == CodeContext.Function ? null : new LiteralExpression(Undefined.Value), writable: true, deletable: this.context == CodeContext.Eval); // The next token is either an equals sign (=), a semi-colon, a comma, or the "in" keyword. if (this.nextToken == PunctuatorToken.Assignment) { // Read past the equals token (=). this.Expect(PunctuatorToken.Assignment); // Read the setter expression. declaration.InitExpression = ParseExpression(PunctuatorToken.Semicolon, PunctuatorToken.Comma); // Record the portion of the source document that will be highlighted when debugging. declaration.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); // This is a regular for statement. cannotBeForIn = true; } // Add the declaration to the initialization statement. varStatement.Declarations.Add(declaration); if (this.nextToken == PunctuatorToken.Semicolon) { // This is a regular for statement. break; } else if (this.nextToken == KeywordToken.In && cannotBeForIn == false) { // This is a for-in statement. forInReference = new NameExpression(this.currentScope, declaration.VariableName); break; } else if (this.nextToken != PunctuatorToken.Comma) throw new JavaScriptException(this.engine, "SyntaxError", string.Format("Unexpected token {0}", Token.ToText(this.nextToken)), this.LineNumber, this.SourcePath); // Read past the comma token. this.Expect(PunctuatorToken.Comma); // Keep track of the start of the statement so that source debugging works correctly. start = this.PositionAfterWhitespace; // Multiple initializers are not allowed in for-in statements. cannotBeForIn = true; } // Record the portion of the source document that will be highlighted when debugging. varStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); } else { // Not a var initializer - can be a simple variable name then "in" or any expression ending with a semi-colon. // The expression can be empty. if (this.nextToken != PunctuatorToken.Semicolon) { // Parse an expression. var initializationExpression = ParseExpression(PunctuatorToken.Semicolon, KeywordToken.In); // Record debug info for the expression. initializationStatement = new ExpressionStatement(initializationExpression); initializationStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); if (this.nextToken == KeywordToken.In) { // This is a for-in statement. if ((initializationExpression is IReferenceExpression) == false) throw new JavaScriptException(this.engine, "ReferenceError", "Invalid left-hand side in for-in", this.LineNumber, this.SourcePath); forInReference = (IReferenceExpression)initializationExpression; } } } if (forInReference != null) { // for (x in y) // for (var x in y) var result = new ForInStatement(this.labelsForCurrentStatement); result.Variable = forInReference; result.VariableSourceSpan = initializationStatement.SourceSpan; // Consume the "in". this.Expect(KeywordToken.In); // Parse the right-hand-side expression. start = this.PositionAfterWhitespace; result.TargetObject = ParseExpression(PunctuatorToken.RightParenthesis); result.TargetObjectSourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); // Read the right parenthesis. this.Expect(PunctuatorToken.RightParenthesis); // Read the statements that will be executed in the loop body. result.Body = ParseStatement(); return result; } else { var result = new ForStatement(this.labelsForCurrentStatement); // Set the initialization statement. if (initializationStatement != null) result.InitStatement = initializationStatement; // Read the semicolon. this.Expect(PunctuatorToken.Semicolon); // Parse the optional condition expression. // Note: if the condition is omitted then it is considered to always be true. if (this.nextToken != PunctuatorToken.Semicolon) { start = this.PositionAfterWhitespace; result.ConditionStatement = new ExpressionStatement(ParseExpression(PunctuatorToken.Semicolon)); result.ConditionStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); } // Read the semicolon. // Note: automatic semicolon insertion never inserts a semicolon in the header of a // for statement. this.Expect(PunctuatorToken.Semicolon); // Parse the optional increment expression. if (this.nextToken != PunctuatorToken.RightParenthesis) { start = this.PositionAfterWhitespace; result.IncrementStatement = new ExpressionStatement(ParseExpression(PunctuatorToken.RightParenthesis)); result.IncrementStatement.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); } // Read the right parenthesis. this.Expect(PunctuatorToken.RightParenthesis); // Read the statements that will be executed in the loop body. result.Body = ParseStatement(); return result; } }
/// <summary> /// Parses a var statement. /// </summary> /// <returns> A var statement. </returns> private VarStatement ParseVar() { var result = new VarStatement(this.labelsForCurrentStatement, this.currentScope); // Read past the var token. this.Expect(KeywordToken.Var); // Keep track of the start of the statement so that source debugging works correctly. var start = this.PositionAfterWhitespace; // There can be multiple declarations. while (true) { var declaration = new VariableDeclaration(); // The next token must be a variable name. declaration.VariableName = this.ExpectIdentifier(); ValidateVariableName(declaration.VariableName); // Add the variable to the current function's list of local variables. this.currentScope.DeclareVariable(declaration.VariableName, this.context == CodeContext.Function ? null : new LiteralExpression(Undefined.Value), writable: true, deletable: this.context == CodeContext.Eval); // The next token is either an equals sign (=), a semi-colon or a comma. if (this.nextToken == PunctuatorToken.Assignment) { // Read past the equals token (=). this.Expect(PunctuatorToken.Assignment); // Read the setter expression. declaration.InitExpression = ParseExpression(PunctuatorToken.Semicolon, PunctuatorToken.Comma); // Record the portion of the source document that will be highlighted when debugging. declaration.SourceSpan = new SourceCodeSpan(start, this.PositionBeforeWhitespace); } // Add the declaration to the result. result.Declarations.Add(declaration); // Check if we are at the end of the statement. if (this.AtValidEndOfStatement() == true && this.nextToken != PunctuatorToken.Comma) break; // Read past the comma token. this.Expect(PunctuatorToken.Comma); // Keep track of the start of the statement so that source debugging works correctly. start = this.PositionAfterWhitespace; } // Consume the end of the statement. this.ExpectEndOfStatement(); return result; }