/// <summary> /// Creates a new SourceCodeSpan instance. /// </summary> /// <param name="start"> The start line and column of this SourceCodeSpan. </param> /// <param name="end"> The end line and column of this SourceCodeSpan. </param> public SourceCodeSpan(SourceCodePosition start, SourceCodePosition end) { this.StartLine = start.Line; this.StartColumn = start.Column; this.EndLine = end.Line; this.EndColumn = end.Column; }
// TOKEN HELPERS //_________________________________________________________________________________________ /// <summary> /// Discards the current token and reads the next one. /// </summary> /// <param name="expressionState"> Indicates whether the next token can be a literal or an /// operator. </param> private void Consume(ParserExpressionState expressionState = ParserExpressionState.Literal) { this.expressionState = expressionState; this.lexer.ParserExpressionState = expressionState; this.consumedLineTerminator = false; this.positionBeforeWhitespace = new SourceCodePosition(this.lexer.LineNumber, this.lexer.ColumnNumber); this.positionAfterWhitespace = this.positionBeforeWhitespace; while (true) { this.nextToken = this.lexer.NextToken(); if ((this.nextToken is WhiteSpaceToken) == false) break; if (((WhiteSpaceToken)this.nextToken).LineTerminatorCount > 0) this.consumedLineTerminator = true; this.positionAfterWhitespace = new SourceCodePosition(this.lexer.LineNumber, this.lexer.ColumnNumber); } }
/// <summary> /// Parses a function declaration or a function expression. /// </summary> /// <param name="functionType"> The type of function to parse. </param> /// <param name="parentScope"> The parent scope for the function. </param> /// <returns> A function expression. </returns> private FunctionExpression ParseFunction(FunctionType functionType, Scope parentScope) { if (functionType != FunctionType.Getter && functionType != FunctionType.Setter) { // Consume the function keyword. this.Expect(KeywordToken.Function); } // Read the function name. var functionName = string.Empty; if (functionType == FunctionType.Declaration) { functionName = this.ExpectIdentifier(); } else if (functionType == FunctionType.Expression) { // The function name is optional for function expressions. if (this.nextToken is IdentifierToken) functionName = this.ExpectIdentifier(); } else if (functionType == FunctionType.Getter || functionType == FunctionType.Setter) { // Getters and setters can have any name that is allowed of a property. bool wasIdentifier; functionName = ReadPropertyName(out wasIdentifier); } else throw new ArgumentOutOfRangeException("functionType"); ValidateVariableName(functionName); // Read the left parenthesis. this.Expect(PunctuatorToken.LeftParenthesis); // Read zero or more argument names. var argumentNames = new List<string>(); // Read the first argument name. if (this.nextToken != PunctuatorToken.RightParenthesis) { var argumentName = this.ExpectIdentifier(); ValidateVariableName(argumentName); argumentNames.Add(argumentName); } while (true) { if (this.nextToken == PunctuatorToken.Comma) { // Consume the comma. this.Consume(); // Read and validate the argument name. var argumentName = this.ExpectIdentifier(); ValidateVariableName(argumentName); argumentNames.Add(argumentName); } else if (this.nextToken == PunctuatorToken.RightParenthesis) break; else throw new JavaScriptException(this.engine, "SyntaxError", "Expected ',' or ')'", this.LineNumber, this.SourcePath); } // Getters must have zero arguments. if (functionType == FunctionType.Getter && argumentNames.Count != 0) throw new JavaScriptException(this.engine, "SyntaxError", "Getters cannot have arguments", this.LineNumber, this.SourcePath); // Setters must have one argument. if (functionType == FunctionType.Setter && argumentNames.Count != 1) throw new JavaScriptException(this.engine, "SyntaxError", "Setters must have a single argument", this.LineNumber, this.SourcePath); // Read the right parenthesis. this.Expect(PunctuatorToken.RightParenthesis); // Record the start of the function body. var startPosition = this.PositionBeforeWhitespace; // Since the parser reads one token in advance, start capturing the function body here. var bodyTextBuilder = new System.Text.StringBuilder(); var originalBodyTextBuilder = this.lexer.InputCaptureStringBuilder; this.lexer.InputCaptureStringBuilder = bodyTextBuilder; // Read the start brace. this.Expect(PunctuatorToken.LeftBrace); // This context has a nested function. this.methodOptimizationHints.HasNestedFunction = true; // Create a new scope and assign variables within the function body to the scope. bool includeNameInScope = functionType != FunctionType.Getter && functionType != FunctionType.Setter; var scope = DeclarativeScope.CreateFunctionScope(parentScope, includeNameInScope ? functionName : string.Empty, argumentNames); // Read the function body. var functionParser = Parser.CreateFunctionBodyParser(this, scope); var body = functionParser.Parse(); // Transfer state back from the function parser. this.nextToken = functionParser.nextToken; this.lexer.StrictMode = this.StrictMode; this.lexer.InputCaptureStringBuilder = originalBodyTextBuilder; if (originalBodyTextBuilder != null) originalBodyTextBuilder.Append(bodyTextBuilder); SourceCodePosition endPosition; if (functionType == FunctionType.Expression) { // The end token '}' will be consumed by the parent function. if (this.nextToken != PunctuatorToken.RightBrace) throw new JavaScriptException(this.engine, "SyntaxError", "Expected '}'", this.LineNumber, this.SourcePath); // Record the end of the function body. endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1); } else { // Consume the '}'. this.Expect(PunctuatorToken.RightBrace); // Record the end of the function body. endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1); } // Create a new function expression. var options = this.options.Clone(); options.ForceStrictMode = functionParser.StrictMode; var context = new FunctionMethodGenerator(this.engine, scope, functionName, includeNameInScope, argumentNames, bodyTextBuilder.ToString(0, bodyTextBuilder.Length - 1), body, this.SourcePath, options); context.MethodOptimizationHints = functionParser.methodOptimizationHints; return new FunctionExpression(context); }
/// <summary> /// Parses a function declaration or a function expression. /// </summary> /// <param name="functionType"> The type of function to parse. </param> /// <param name="parentScope"> The parent scope for the function. </param> /// <param name="functionName"> The name of the function (can be empty). </param> /// <returns> A function expression. </returns> private FunctionExpression ParseFunction(FunctionDeclarationType functionType, Scope parentScope, string functionName) { // Read the left parenthesis. this.Expect(PunctuatorToken.LeftParenthesis); // Create a new scope and assign variables within the function body to the scope. bool includeNameInScope = functionType != FunctionDeclarationType.Getter && functionType != FunctionDeclarationType.Setter; var scope = DeclarativeScope.CreateFunctionScope(parentScope, includeNameInScope ? functionName : string.Empty, null); // Replace scope and methodOptimizationHints. var originalScope = this.currentVarScope; var originalMethodOptimizationHints = this.methodOptimizationHints; var newMethodOptimizationHints = new MethodOptimizationHints(); this.methodOptimizationHints = newMethodOptimizationHints; this.currentVarScope = scope; // Read zero or more arguments. var arguments = ParseFunctionArguments(PunctuatorToken.RightParenthesis); // Restore scope and methodOptimizationHints. this.methodOptimizationHints = originalMethodOptimizationHints; this.currentVarScope = originalScope; // Getters must have zero arguments. if (functionType == FunctionDeclarationType.Getter && arguments.Count != 0) throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Getters cannot have arguments", this.LineNumber, this.SourcePath); // Setters must have one argument. if (functionType == FunctionDeclarationType.Setter && arguments.Count != 1) throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Setters must have a single argument", this.LineNumber, this.SourcePath); // Read the right parenthesis. this.Expect(PunctuatorToken.RightParenthesis); // Record the start of the function body. var startPosition = this.PositionBeforeWhitespace; // Since the parser reads one token in advance, start capturing the function body here. var bodyTextBuilder = new System.Text.StringBuilder(); var originalBodyTextBuilder = this.lexer.InputCaptureStringBuilder; this.lexer.InputCaptureStringBuilder = bodyTextBuilder; // Read the start brace. this.Expect(PunctuatorToken.LeftBrace); // This context has a nested function. this.methodOptimizationHints.HasNestedFunction = true; // Read the function body. var functionParser = CreateFunctionBodyParser(this, scope, newMethodOptimizationHints); var body = functionParser.Parse(); // Transfer state back from the function parser. this.nextToken = functionParser.nextToken; this.lexer.StrictMode = this.StrictMode; this.lexer.InputCaptureStringBuilder = originalBodyTextBuilder; if (originalBodyTextBuilder != null) originalBodyTextBuilder.Append(bodyTextBuilder); SourceCodePosition endPosition; if (functionType == FunctionDeclarationType.Expression) { // The end token '}' will be consumed by the parent function. if (this.nextToken != PunctuatorToken.RightBrace) throw new JavaScriptException(this.engine, ErrorType.SyntaxError, "Expected '}'", this.LineNumber, this.SourcePath); // Record the end of the function body. endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1); } else { // Consume the '}'. this.Expect(PunctuatorToken.RightBrace); // Record the end of the function body. endPosition = new SourceCodePosition(this.PositionAfterWhitespace.Line, this.PositionAfterWhitespace.Column + 1); } // Create a new function expression. var options = this.options.Clone(); options.ForceStrictMode = functionParser.StrictMode; var context = new FunctionMethodGenerator(this.engine, scope, functionName, functionType, arguments, bodyTextBuilder.ToString(0, bodyTextBuilder.Length - 1), body, this.SourcePath, options); context.MethodOptimizationHints = functionParser.methodOptimizationHints; return new FunctionExpression(context); }