// INITIALIZATION //_________________________________________________________________________________________ /// <summary> /// Creates a Parser instance with the given lexer supplying the tokens. /// </summary> /// <param name="engine"> The associated script engine. </param> /// <param name="lexer"> The lexical analyser that provides the tokens. </param> /// <param name="initialScope"> The initial variable scope. </param> /// <param name="options"> Options that influence the compiler. </param> /// <param name="context"> The context of the code (global, function or eval). </param> public Parser(ScriptEngine engine, Lexer lexer, Scope initialScope, CompilerOptions options, CodeContext context) { if (engine == null) throw new ArgumentNullException("engine"); if (lexer == null) throw new ArgumentNullException("lexer"); if (initialScope == null) throw new ArgumentNullException("initialScope"); this.engine = engine; this.lexer = lexer; this.lexer.ParserExpressionState = ParserExpressionState.Literal; this.currentScope = this.initialScope = initialScope; this.methodOptimizationHints = new MethodOptimizationHints(); this.options = options; this.context = context; this.StrictMode = options.ForceStrictMode; this.Consume(); }
/// <summary> /// Creates a parser that can read the body of a function. /// </summary> /// <param name="parser"> The parser for the parent context. </param> /// <param name="scope"> The function scope. </param> /// <param name="optimizationHints"> Hints about whether optimization is possible. </param> /// <returns> A new parser. </returns> private static Parser CreateFunctionBodyParser(Parser parser, Scope scope, MethodOptimizationHints optimizationHints) { var result = (Parser)parser.MemberwiseClone(); result.SetInitialScope(scope); result.methodOptimizationHints = optimizationHints; result.context = CodeContext.Function; result.endToken = PunctuatorToken.RightBrace; return result; }
/// <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); }