Ejemplo n.º 1
0
 /// <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;
 }
Ejemplo n.º 2
0
        //     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);
            }
        }
Ejemplo n.º 3
0
        /// <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);
        }
Ejemplo n.º 4
0
        /// <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);
        }