//--------------------------------------------------------------------------------------- // JSParser // // create a parser with a context. The context is the code that has to be compiled. // Typically used by the runtime //--------------------------------------------------------------------------------------- public JSParser(Context context){ this.sourceContext = context; this.currentToken = context.Clone(); this.scanner = new JSScanner(this.currentToken); this.noSkipTokenSet = new NoSkipTokenSet(); this.errorToken = null; this.program = null; this.blockType = new ArrayList(16); this.labelTable = new SimpleHashtable(16); this.finallyEscaped = 0; this.Globals = context.document.engine.Globals; this.Severity = 5; }
internal void UpdateToken(Context token) { this._token = token.Clone(); this._color = ColorFromToken(this._token); }
internal TokenColorInfo(Context token) { this._token = token.Clone(); this._color = ColorFromToken(this._token); this._next = this; }
//--------------------------------------------------------------------------------------- // ParsePackage // // Package : // 'package' QualifiedIdentifier '{' ClassList '}' // // ClassList : // <empty> | // Class ClassList | // Attributes Class ClassList | // Attributes Enum ClassList //--------------------------------------------------------------------------------------- // Because 'package' is not a reserved word in JS5 we have to deal with an ambiguity // in the grammar. A source sequence like the following // package // x // { } // can be legally parsed in two ways: // Identifier Identifier Block or // Package // we give Package priority in this situation. // Here is how we deal with some possible cases: // 1- ** package <no line break> QualifiedIdentifier ** is parsed unambiguously as a package production regardless of what comes after Identifier // 2- ** package <no line break> NotOneOf(Operator | '[' | '.' | '(' | Identifier) '{' ** is parsed as a package production with an error // 3- ** package <line break> '{' ** is parsed as a package (anonymous) with an error // 4- ** package <line break> Not(Identifier) ** is never parsed as a package private AST ParsePackage(Context packageContext){ GetNextToken(); AST qualid = null; bool gotLineBreak = this.scanner.GotEndOfLine(); // erroneous package production if (JSToken.Identifier != this.currentToken.token){ if (JSScanner.CanParseAsExpression(this.currentToken.token)){ // it's an expression. Report a warning. package and this.currentToken can be an expression (i.e. 'package +') ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); qualid = new Lookup("package", packageContext); // get the member expression qualid = MemberExpression(qualid, null); bool isLeftHandSide; qualid = ParsePostfixExpression(qualid, out isLeftHandSide); qualid = ParseExpression(qualid, false, isLeftHandSide, JSToken.None); return new Expression(qualid.context.Clone(), qualid); }else if (!gotLineBreak){ if (JSToken.Increment == this.currentToken.token || JSToken.Decrement == this.currentToken.token){ // it's a postfix expression. Report a warning ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); bool dummy; qualid = new Lookup("package", packageContext); qualid = ParsePostfixExpression(qualid, out dummy); qualid = ParseExpression(qualid, false, false, JSToken.None); return new Expression(qualid.context.Clone(), qualid); } }else{ // it's an expression. Report a warning which, as a side effect, will make the current token be the next token fetched ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); return new Lookup("package", packageContext); } }else{ // it is an identifier, parse it as a qualified identifier this.errorToken = this.currentToken; // this will make GetNextToken() in ParseQualifiedIdentifier() return this.currentToken qualid = ParseQualifiedIdentifier(JSError.NoIdentifier); } // if we are here we have: // ** package QualifiedIdentifier ** or // ** package SomeNonSenseToken **, that is a token that does not make an expression Context nonSenseToken = null; if (JSToken.LeftCurly != this.currentToken.token && qualid == null){ // we want to peek and see whether the next token is a LeftCurly nonSenseToken = this.currentToken.Clone(); GetNextToken(); } if (JSToken.LeftCurly == this.currentToken.token){ // sounds like a package, possibly with an error. If qualid is not null is actually a good package, otherwise we treat it // as an anonymous package and keep going. if (qualid == null){ if (nonSenseToken == null) nonSenseToken = this.currentToken.Clone(); ReportError(JSError.NoIdentifier, nonSenseToken, true); } }else{ if (qualid == null){ // this is pretty screwy, let's ignore the package keyword for a start ReportError(JSError.SyntaxError, packageContext); if (JSScanner.CanStartStatement(nonSenseToken.token)){ // this is tricky we assign nonSenseToken to this.currentToken and call ParseStatement, because we know it is a statement start token. // The parser should then call GetNextToken() which will return the this.currentToken that is assigned to this.errorToken this.currentToken = nonSenseToken; return ParseStatement(); }else{ //ReportError(JSError.SyntaxError, nonSenseToken); if (JSScanner.CanStartStatement(this.currentToken.token)){ this.errorToken = null; return ParseStatement(); }else{ ReportError(JSError.SyntaxError); SkipTokensAndThrow(); } } }else{ if (gotLineBreak){ // we are here with the following: 'package' <line break> QalifiedIdentifier' however we do not have a left curly. // if the token in our hand can start an expression we go with two expressions, otherwise we accept it as a package //if (JSScanner.CanParseAsExpression(this.currentToken.token)){ ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); Block block = new Block(packageContext.Clone()); block.Append(new Lookup("package", packageContext)); qualid = MemberExpression(qualid, null); bool isLeftHandSide; qualid = ParsePostfixExpression(qualid, out isLeftHandSide); qualid = ParseExpression(qualid, false, true, JSToken.None); block.Append(new Expression(qualid.context.Clone(), qualid)); block.context.UpdateWith(qualid.context); return block; //} } // the package production rule is entered regardless of the presence of a left curly. ReportError(JSError.NoLeftCurly); } } PackageScope pscope = new PackageScope(Globals.ScopeStack.Peek()); Globals.ScopeStack.Push(pscope); //Give declarations a place to go while building AST try{ string name = (qualid != null) ? qualid.ToString() : "anonymous package"; pscope.name = name; packageContext.UpdateWith(this.currentToken); ASTList classList = new ASTList(packageContext); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); try{ while (this.currentToken.token != JSToken.RightCurly){ AST ast = null; try{ switch (this.currentToken.token){ case JSToken.Interface: case JSToken.Class: classList.Append(ParseClass((FieldAttributes)0, false, this.currentToken.Clone(), false, false, null)); break; case JSToken.Enum: classList.Append(ParseEnum((FieldAttributes)0, this.currentToken.Clone(), null)); break; case JSToken.Internal: case JSToken.Public: case JSToken.Static: case JSToken.Private: case JSToken.Protected: case JSToken.Abstract: case JSToken.Final: bool parsedOK; ast = ParseAttributes(null, true, false, out parsedOK); if (parsedOK){ if (ast is Class){ classList.Append(ast); break; } } ReportError(JSError.OnlyClassesAllowed, ast.context.Clone(), true); SkipTokensAndThrow(); break; case JSToken.Identifier: bool bAssign, canBeAttribute = true; ast = ParseUnaryExpression(out bAssign, ref canBeAttribute, false); if (canBeAttribute){ bool parsed; ast = ParseAttributes(ast, true, false, out parsed); if (parsed){ if (ast is Class){ classList.Append(ast); break; } } } ReportError(JSError.OnlyClassesAllowed, ast.context.Clone(), true); SkipTokensAndThrow(); break; case JSToken.EndOfFile: EOFError(JSError.ErrEOF); throw new EndOfFile(); // abort parsing, get back to the main parse routine case JSToken.Semicolon: // ignore any spurious semicolon GetNextToken(); break; case JSToken.Import: // handle common error of using import in package ReportError(JSError.InvalidImport, true); try{ ParseImportStatement(); }catch(RecoveryTokenException){ } break; case JSToken.Package: // handle common error of using package in package Context nestedPackageContext = this.currentToken.Clone(); AST statement = ParsePackage(nestedPackageContext); if (statement is Package) ReportError(JSError.PackageInWrongContext, nestedPackageContext, true); break; default: ReportError(JSError.OnlyClassesAllowed, (ast != null) ? ast.context.Clone() : CurrentPositionContext(), true); SkipTokensAndThrow(); break; } }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null && exc._partiallyComputedNode is Class){ classList.Append((Class)exc._partiallyComputedNode); exc._partiallyComputedNode = null; } if (IndexOfToken(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet, exc) == -1) throw exc; } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ ReportError(JSError.NoRightCurly, CurrentPositionContext()); exc._partiallyComputedNode = new Package(name, qualid, classList, packageContext); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } GetNextToken(); return new Package(name, qualid, classList, packageContext); }finally{ Globals.ScopeStack.Pop(); } }
private AST ParsePackage(Context packageContext) { AST ast4; this.GetNextToken(); AST expression = null; bool flag = this.scanner.GotEndOfLine(); if (JSToken.Identifier != this.currentToken.token) { if (JSScanner.CanParseAsExpression(this.currentToken.token)) { bool flag2; this.ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); expression = new Lookup("package", packageContext); expression = this.MemberExpression(expression, null); expression = this.ParsePostfixExpression(expression, out flag2); expression = this.ParseExpression(expression, false, flag2, JSToken.None); return new Expression(expression.context.Clone(), expression); } if (flag) { this.ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); return new Lookup("package", packageContext); } if ((JSToken.Increment == this.currentToken.token) || (JSToken.Decrement == this.currentToken.token)) { bool flag3; this.ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); expression = new Lookup("package", packageContext); expression = this.ParsePostfixExpression(expression, out flag3); expression = this.ParseExpression(expression, false, false, JSToken.None); return new Expression(expression.context.Clone(), expression); } } else { this.errorToken = this.currentToken; expression = this.ParseQualifiedIdentifier(JSError.NoIdentifier); } Context context = null; if ((JSToken.LeftCurly != this.currentToken.token) && (expression == null)) { context = this.currentToken.Clone(); this.GetNextToken(); } if (JSToken.LeftCurly == this.currentToken.token) { if (expression == null) { if (context == null) { context = this.currentToken.Clone(); } this.ReportError(JSError.NoIdentifier, context, true); } } else if (expression == null) { this.ReportError(JSError.SyntaxError, packageContext); if (JSScanner.CanStartStatement(context.token)) { this.currentToken = context; return this.ParseStatement(); } if (JSScanner.CanStartStatement(this.currentToken.token)) { this.errorToken = null; return this.ParseStatement(); } this.ReportError(JSError.SyntaxError); this.SkipTokensAndThrow(); } else { if (flag) { bool flag4; this.ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); Block block = new Block(packageContext.Clone()); block.Append(new Lookup("package", packageContext)); expression = this.MemberExpression(expression, null); expression = this.ParsePostfixExpression(expression, out flag4); expression = this.ParseExpression(expression, false, true, JSToken.None); block.Append(new Expression(expression.context.Clone(), expression)); block.context.UpdateWith(expression.context); return block; } this.ReportError(JSError.NoLeftCurly); } PackageScope item = new PackageScope(this.Globals.ScopeStack.Peek()); this.Globals.ScopeStack.Push(item); try { string name = (expression != null) ? expression.ToString() : "anonymous package"; item.name = name; packageContext.UpdateWith(this.currentToken); ASTList classList = new ASTList(packageContext); this.GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); try { while (this.currentToken.token != JSToken.RightCurly) { AST statement = null; try { switch (this.currentToken.token) { case JSToken.Identifier: { bool flag6; bool canBeAttribute = true; statement = this.ParseUnaryExpression(out flag6, ref canBeAttribute, false); if (canBeAttribute) { bool flag8; statement = this.ParseAttributes(statement, true, false, out flag8); if (flag8 && (statement is Class)) { classList.Append(statement); continue; } } this.ReportError(JSError.OnlyClassesAllowed, statement.context.Clone(), true); this.SkipTokensAndThrow(); continue; } case JSToken.Interface: case JSToken.Class: { classList.Append(this.ParseClass(FieldAttributes.PrivateScope, false, this.currentToken.Clone(), false, false, null)); continue; } case JSToken.Enum: { classList.Append(this.ParseEnum(FieldAttributes.PrivateScope, this.currentToken.Clone(), null)); continue; } case JSToken.Import: { this.ReportError(JSError.InvalidImport, true); try { this.ParseImportStatement(); } catch (RecoveryTokenException) { } continue; } case JSToken.Package: { Context context2 = this.currentToken.Clone(); if (this.ParsePackage(context2) is Package) { this.ReportError(JSError.PackageInWrongContext, context2, true); } continue; } case JSToken.Internal: case JSToken.Abstract: case JSToken.Public: case JSToken.Static: case JSToken.Private: case JSToken.Protected: case JSToken.Final: { bool flag5; statement = this.ParseAttributes(null, true, false, out flag5); if (!flag5 || !(statement is Class)) { break; } classList.Append(statement); continue; } case JSToken.Semicolon: { this.GetNextToken(); continue; } case JSToken.EndOfFile: this.EOFError(JSError.ErrEOF); throw new EndOfFile(); default: goto Label_04D9; } this.ReportError(JSError.OnlyClassesAllowed, statement.context.Clone(), true); this.SkipTokensAndThrow(); continue; Label_04D9: this.ReportError(JSError.OnlyClassesAllowed, (statement != null) ? statement.context.Clone() : this.CurrentPositionContext(), true); this.SkipTokensAndThrow(); continue; } catch (RecoveryTokenException exception) { if ((exception._partiallyComputedNode != null) && (exception._partiallyComputedNode is Class)) { classList.Append((Class) exception._partiallyComputedNode); exception._partiallyComputedNode = null; } if (this.IndexOfToken(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet, exception) == -1) { throw exception; } continue; } } } catch (RecoveryTokenException exception2) { if (this.IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exception2) == -1) { this.ReportError(JSError.NoRightCurly, this.CurrentPositionContext()); exception2._partiallyComputedNode = new Package(name, expression, classList, packageContext); throw exception2; } } finally { this.noSkipTokenSet.Remove(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } this.GetNextToken(); ast4 = new Package(name, expression, classList, packageContext); } finally { this.Globals.ScopeStack.Pop(); } return ast4; }