diff --git a/js/js2/parser.cpp b/js/js2/parser.cpp index 078769cde79..f3ab0d6059a 100644 --- a/js/js2/parser.cpp +++ b/js/js2/parser.cpp @@ -321,6 +321,7 @@ const char *const JS::Token::kindNames[kindsEnd] = { "import", // Import "in", // In "instanceof", // Instanceof + "interface", // Interface "native", // Native "new", // New "null", // Null @@ -459,6 +460,7 @@ const uchar JS::Token::kindFlags[kindsEnd] = { 0, // Import 0, // In 0, // Instanceof + followAttr, // Interface followAttr, // Native 0, // New 0, // Null @@ -1199,6 +1201,7 @@ const int32 caseIndent = basicIndent/2; // Indentation before a case or default const int32 varIndent = 2; // Indentation of var or const statement bindings const int32 subexpressionIndent = 4; // Size of one level of expression indentation const int32 functionHeaderIndent = 9; // Indentation of function signature +const int32 namespaceHeaderIndent = 4; // Indentation of class, interface, or namespace header static const char functionPrefixNames[3][5] = {"", "get ", "set "}; @@ -1283,6 +1286,35 @@ void JS::FunctionDefinition::print(PrettyPrinter &f, bool isConstructor, const A } +// Print a comma-separated ExprList on to f. Since a method can't be called on nil, the list +// has at least one element. +void JS::ExprList::printCommaList(PrettyPrinter &f) const +{ + PrettyPrinter::Block b(f); + const ExprList *list = this; + while (true) { + f << list->expr; + list = list->next; + if (!list) + break; + f << ','; + f.fillBreak(1); + } +} + + +// If list is nil, do nothing. Otherwise, emit a linear line break of size 1, the name, a space, and the +// contents of list separated by commas. +void JS::ExprList::printOptionalCommaList(PrettyPrinter &f, const char *name, const ExprList *list) +{ + if (list) { + f.linearBreak(1); + f << name << ' '; + list->printCommaList(f); + } +} + + // // ExprNode // @@ -1879,6 +1911,50 @@ void JS::FunctionStmtNode::print(PrettyPrinter &f, bool noSemi) const function.print(f, hasKind(Constructor), this, noSemi); } +void JS::NamespaceStmtNode::print(PrettyPrinter &f, bool noSemi) const +{ + printAttributes(f); + ASSERT(hasKind(Namespace)); + + PrettyPrinter::Block b(f, namespaceHeaderIndent); + f << "namespace "; + f << name; + ExprList::printOptionalCommaList(f, "extends", supers); + printSemi(f, noSemi); +} + +void JS::ClassStmtNode::print(PrettyPrinter &f, bool noSemi) const +{ + printAttributes(f); + ASSERT(hasKind(Class) || hasKind(Interface)); + + { + PrettyPrinter::Block b(f, namespaceHeaderIndent); + const char *keyword; + const char *superlistKeyword; + if (hasKind(Class)) { + keyword = "class "; + superlistKeyword = "implements"; + } else { + keyword = "interface "; + superlistKeyword = "extends"; + } + f << keyword; + f << name; + if (superclass) { + f.linearBreak(1); + f << "extends "; + f << superclass; + } + ExprList::printOptionalCommaList(f, superlistKeyword, supers); + } + if (body) { + f.requiredBreak(); + body->printBlock(f, true); + } else + printSemi(f, noSemi); +} + // // Parser @@ -2169,7 +2245,7 @@ JS::ExprNode *JS::Parser::parsePrimaryExpression() if (!(f->function.name = makeIdentifierExpression(t2))) lexer.unget(); parseFunctionSignature(f->function); - f->function.body = parseBlockStatement(require(true, Token::openBrace).getPos(), 0); + f->function.body = parseBody(0); e = f; } break; @@ -2466,6 +2542,7 @@ const JS::Parser::BinaryOperatorInfo JS::Parser::tokenBinaryOperatorInfos[Token: {ExprNode::none, pExpression, pNone}, // Token::Import {ExprNode::In, pRelational, pRelational}, // Token::In {ExprNode::Instanceof, pRelational, pRelational}, // Token::Instanceof + {ExprNode::none, pExpression, pNone}, // Token::Interface {ExprNode::none, pExpression, pNone}, // Token::Native {ExprNode::none, pExpression, pNone}, // Token::New {ExprNode::none, pExpression, pNone}, // Token::Null @@ -2589,6 +2666,21 @@ JS::ExprNode *JS::Parser::parseParenthesizedExpression() } +// Parse and return a TypeExpression. If noIn is false, allow the in operator. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseTypeExpression finishes, the next token might have been peeked with preferRegExp set to true. +JS::ExprNode *JS::Parser::parseTypeExpression(bool noIn) +{ + ExprNode *type = parseNonAssignmentExpression(noIn); + if (lexer.peek(false).hasKind(Token::divideEquals)) + syntaxError("'/=' not allowed here", 0); + lexer.redesignate(true); // Safe: a '/' would have been interpreted as an operator, so it can't be the next token; + // a '/=' was outlawed by the check above. + return type; +} + + // Parse a TypedIdentifier. Return the identifier's name. // If a type was provided, set type to it; otherwise, set type to nil. // After parseTypedIdentifier finishes, the next token might have been peeked with preferRegExp set to false. @@ -2606,25 +2698,36 @@ const JS::StringAtom &JS::Parser::parseTypedIdentifier(ExprNode *&type) } -// If the next token is ':', eat it and parse and return the following TypeExpression. Otherwise return nil. +// If the next token has the given kind, eat it and parse and return the following TypeExpression; +// otherwise return nil. // If noIn is false, allow the in operator. // // If the first token was peeked, it should be have been done with preferRegExp set to true. // After parseTypeBinding finishes, the next token might have been peeked with preferRegExp set to true. -JS::ExprNode *JS::Parser::parseTypeBinding(bool noIn) +JS::ExprNode *JS::Parser::parseTypeBinding(Token::Kind kind, bool noIn) { ExprNode *type = 0; - if (lexer.eat(true, Token::colon)) { - type = parseNonAssignmentExpression(noIn); - if (lexer.peek(false).hasKind(Token::divideEquals)) - syntaxError("'/=' not allowed here", 0); - lexer.redesignate(true); // Safe: a '/' would have been interpreted as an operator, so it can't be the next token; - // a '/=' was outlawed by the check above. - } + if (lexer.eat(true, kind)) + type = parseTypeExpression(noIn); return type; } +// If the next token has the given kind, eat it and parse and return the following TypeExpressionList; +// otherwise return nil. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseTypeListBinding finishes, the next token might have been peeked with preferRegExp set to true. +JS::ExprList *JS::Parser::parseTypeListBinding(Token::Kind kind) +{ + NodeQueue types; + if (lexer.eat(true, kind)) + do types += new(arena) ExprList(parseTypeExpression(false)); + while (lexer.eat(true, Token::comma)); + return types.first; +} + + // Parse and return a VariableBinding. // If noQualifiers is false, allow a QualifiedIdentifier as the variable name; otherwise, restrict the // variable name to be a simple Identifier. @@ -2645,7 +2748,7 @@ JS::VariableBinding *JS::Parser::parseVariableBinding(bool noQualifiers, bool no } else name = parseQualifiedIdentifier(t, true); - ExprNode *type = parseTypeBinding(noIn); + ExprNode *type = parseTypeBinding(Token::colon, noIn); ExprNode *initializer = 0; if (lexer.eat(true, Token::assignment)) { @@ -2720,7 +2823,7 @@ void JS::Parser::parseFunctionSignature(FunctionDefinition &fd) fd.parameters = parameters.first; fd.optParameters = optParameters; fd.restParameter = restParameter; - fd.resultType = parseTypeBinding(false); + fd.resultType = parseTypeBinding(Token::colon, false); } @@ -2759,12 +2862,25 @@ JS::StmtNode *JS::Parser::parseBlock(bool inSwitch, bool noCloseBrace) } -// Parse a list of statements ending with a '}'. Return these statements as a BlockStmtNode -// with the given attributes. The opening '{' has already been read. -// pos is the position of the beginning of the statement (its first attribute if it has attributes). -JS::BlockStmtNode *JS::Parser::parseBlockStatement(uint32 pos, IdentifierList *attributes) +// Parse an optional block of statements beginning with a '{' and ending with a '}'. +// Return these statements as a BlockStmtNode. +// If semicolonState is nil, the block is required; otherwise, the block is optional and if it is +// omitted, *semicolonState is set to semiInsertable. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseBody finishes, the next token might have been peeked with preferRegExp set to true. +JS::BlockStmtNode *JS::Parser::parseBody(SemicolonState *semicolonState) { - return new(arena) BlockStmtNode(pos, StmtNode::block, attributes, parseBlock(false, false)); + const Token *tBrace = lexer.eat(true, Token::openBrace); + if (tBrace) { + uint32 pos = tBrace->getPos(); + return new(arena) BlockStmtNode(pos, StmtNode::block, 0, parseBlock(false, false)); + } else { + if (!semicolonState) + syntaxError("'{' expected", 0); + *semicolonState = semiInsertable; + return 0; + } } @@ -2788,7 +2904,7 @@ JS::StmtNode *JS::Parser::parseAttributeStatement(uint32 pos, IdentifierList *at switch (t.getKind()) { case Token::openBrace: - return parseBlockStatement(pos, attributes); + return new(arena) BlockStmtNode(pos, StmtNode::block, attributes, parseBlock(false, false)); case Token::Const: sKind = StmtNode::Const; @@ -2821,16 +2937,37 @@ JS::StmtNode *JS::Parser::parseAttributeStatement(uint32 pos, IdentifierList *at } else parseFunctionName(f->function); parseFunctionSignature(f->function); - const Token *t3 = lexer.eat(true, Token::openBrace); - if (t3) - f->function.body = parseBlockStatement(t3->getPos(), 0); - else { - f->function.body = 0; - semicolonState = semiInsertable; - } + f->function.body = parseBody(&semicolonState); return f; } + case Token::Interface: + sKind = StmtNode::Interface; + goto classOrInterface; + case Token::Class: + sKind = StmtNode::Class; + classOrInterface: + { + ExprNode *name = parseQualifiedIdentifier(lexer.get(true), true); + ExprNode *superclass = 0; + if (sKind == StmtNode::Class) + superclass = parseTypeBinding(Token::Extends, false); + ExprList *superinterfaces = parseTypeListBinding(sKind == StmtNode::Class ? Token::Implements : Token::Extends); + BlockStmtNode *body = parseBody(superclass || superinterfaces ? 0 : &semicolonState); + return new(arena) ClassStmtNode(pos, sKind, attributes, name, superclass, superinterfaces, body); + } + + case Token::Namespace: + { + const Token &t2 = lexer.get(false); + ExprNode *name; + if (lineBreakBefore(t2) || !(name = makeIdentifierExpression(t2))) + syntaxError("Namespace name expected"); + ExprList *supernamespaces = parseTypeListBinding(Token::Extends); + semicolonState = semiInsertable; + return new(arena) NamespaceStmtNode(pos, StmtNode::Namespace, attributes, name, supernamespaces); + } + default: syntaxError("Bad declaration"); return 0; @@ -3024,6 +3161,7 @@ JS::StmtNode *JS::Parser::parseStatement(bool topLevel, bool inSwitch, Semicolon break; case Token::Constructor: + case Token::Namespace: t2 = &lexer.peek(false); if (lineBreakBefore(*t2) || !t2->hasIdentifierKind()) goto makeExpression; @@ -3031,6 +3169,8 @@ JS::StmtNode *JS::Parser::parseStatement(bool topLevel, bool inSwitch, Semicolon case Token::Const: case Token::Var: case Token::Function: + case Token::Class: + case Token::Interface: s = parseAttributeStatement(pos, 0, t, false, semicolonState); break; diff --git a/js/js2/parser.h b/js/js2/parser.h index 75086259df7..40ec0851fa6 100644 --- a/js/js2/parser.h +++ b/js/js2/parser.h @@ -195,6 +195,7 @@ namespace JavaScript { Import, // import In, // in Instanceof, // instanceof + Interface, // interface Native, // native New, // new Null, // null @@ -881,6 +882,9 @@ namespace JavaScript { ExprNode *expr; // Attribute expression; non-nil only explicit ExprList(ExprNode *expr): next(0), expr(expr) {ASSERT(expr);} + + void printCommaList(PrettyPrinter &f) const; + static void printOptionalCommaList(PrettyPrinter &f, const char *name, const ExprList *list); }; struct UseStmtNode: StmtNode { @@ -927,15 +931,19 @@ namespace JavaScript { NamespaceStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprList *supers): AttributeStmtNode(pos, kind, attributes), name(name), supers(supers) {ASSERT(name);} + + void print(PrettyPrinter &f, bool noSemi) const; }; struct ClassStmtNode: NamespaceStmtNode { - ExprNode *superclass; // Superclass expression (classes only) - BlockStmtNode *body; // The class's body; non-nil only + ExprNode *superclass; // Superclass expression (classes only); nil if omitted + BlockStmtNode *body; // The class's body; nil if omitted ClassStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprNode *superclass, ExprList *superinterfaces, BlockStmtNode *body): - NamespaceStmtNode(pos, kind, attributes, name, superinterfaces), superclass(superclass), body(body) {ASSERT(body);} + NamespaceStmtNode(pos, kind, attributes, name, superinterfaces), superclass(superclass), body(body) {} + + void print(PrettyPrinter &f, bool noSemi) const; }; struct LanguageStmtNode: StmtNode { @@ -1022,8 +1030,10 @@ namespace JavaScript { ExprNode *parseAssignmentExpression(bool noIn) {return parseExpression(noIn, false, true);} private: ExprNode *parseParenthesizedExpression(); + ExprNode *parseTypeExpression(bool noIn); const StringAtom &parseTypedIdentifier(ExprNode *&type); - ExprNode *parseTypeBinding(bool noIn); + ExprNode *parseTypeBinding(Token::Kind kind, bool noIn); + ExprList *parseTypeListBinding(Token::Kind kind); VariableBinding *parseVariableBinding(bool noQualifiers, bool noIn); void parseFunctionName(FunctionName &fn); void parseFunctionSignature(FunctionDefinition &fd); @@ -1031,7 +1041,7 @@ namespace JavaScript { enum SemicolonState {semiNone, semiNoninsertable, semiInsertable}; enum AttributeStatement {asAny, asBlock, asConstVar}; StmtNode *parseBlock(bool inSwitch, bool noCloseBrace); - BlockStmtNode *parseBlockStatement(uint32 pos, IdentifierList *attributes); + BlockStmtNode *parseBody(SemicolonState *semicolonState); StmtNode *parseAttributeStatement(uint32 pos, IdentifierList *attributes, const Token &t, bool noIn, SemicolonState &semicolonState); StmtNode *parseAttributesAndStatement(const Token *t, AttributeStatement as, SemicolonState &semicolonState); StmtNode *parseAnnotatedBlock(); diff --git a/js2/src/parser.cpp b/js2/src/parser.cpp index 078769cde79..f3ab0d6059a 100644 --- a/js2/src/parser.cpp +++ b/js2/src/parser.cpp @@ -321,6 +321,7 @@ const char *const JS::Token::kindNames[kindsEnd] = { "import", // Import "in", // In "instanceof", // Instanceof + "interface", // Interface "native", // Native "new", // New "null", // Null @@ -459,6 +460,7 @@ const uchar JS::Token::kindFlags[kindsEnd] = { 0, // Import 0, // In 0, // Instanceof + followAttr, // Interface followAttr, // Native 0, // New 0, // Null @@ -1199,6 +1201,7 @@ const int32 caseIndent = basicIndent/2; // Indentation before a case or default const int32 varIndent = 2; // Indentation of var or const statement bindings const int32 subexpressionIndent = 4; // Size of one level of expression indentation const int32 functionHeaderIndent = 9; // Indentation of function signature +const int32 namespaceHeaderIndent = 4; // Indentation of class, interface, or namespace header static const char functionPrefixNames[3][5] = {"", "get ", "set "}; @@ -1283,6 +1286,35 @@ void JS::FunctionDefinition::print(PrettyPrinter &f, bool isConstructor, const A } +// Print a comma-separated ExprList on to f. Since a method can't be called on nil, the list +// has at least one element. +void JS::ExprList::printCommaList(PrettyPrinter &f) const +{ + PrettyPrinter::Block b(f); + const ExprList *list = this; + while (true) { + f << list->expr; + list = list->next; + if (!list) + break; + f << ','; + f.fillBreak(1); + } +} + + +// If list is nil, do nothing. Otherwise, emit a linear line break of size 1, the name, a space, and the +// contents of list separated by commas. +void JS::ExprList::printOptionalCommaList(PrettyPrinter &f, const char *name, const ExprList *list) +{ + if (list) { + f.linearBreak(1); + f << name << ' '; + list->printCommaList(f); + } +} + + // // ExprNode // @@ -1879,6 +1911,50 @@ void JS::FunctionStmtNode::print(PrettyPrinter &f, bool noSemi) const function.print(f, hasKind(Constructor), this, noSemi); } +void JS::NamespaceStmtNode::print(PrettyPrinter &f, bool noSemi) const +{ + printAttributes(f); + ASSERT(hasKind(Namespace)); + + PrettyPrinter::Block b(f, namespaceHeaderIndent); + f << "namespace "; + f << name; + ExprList::printOptionalCommaList(f, "extends", supers); + printSemi(f, noSemi); +} + +void JS::ClassStmtNode::print(PrettyPrinter &f, bool noSemi) const +{ + printAttributes(f); + ASSERT(hasKind(Class) || hasKind(Interface)); + + { + PrettyPrinter::Block b(f, namespaceHeaderIndent); + const char *keyword; + const char *superlistKeyword; + if (hasKind(Class)) { + keyword = "class "; + superlistKeyword = "implements"; + } else { + keyword = "interface "; + superlistKeyword = "extends"; + } + f << keyword; + f << name; + if (superclass) { + f.linearBreak(1); + f << "extends "; + f << superclass; + } + ExprList::printOptionalCommaList(f, superlistKeyword, supers); + } + if (body) { + f.requiredBreak(); + body->printBlock(f, true); + } else + printSemi(f, noSemi); +} + // // Parser @@ -2169,7 +2245,7 @@ JS::ExprNode *JS::Parser::parsePrimaryExpression() if (!(f->function.name = makeIdentifierExpression(t2))) lexer.unget(); parseFunctionSignature(f->function); - f->function.body = parseBlockStatement(require(true, Token::openBrace).getPos(), 0); + f->function.body = parseBody(0); e = f; } break; @@ -2466,6 +2542,7 @@ const JS::Parser::BinaryOperatorInfo JS::Parser::tokenBinaryOperatorInfos[Token: {ExprNode::none, pExpression, pNone}, // Token::Import {ExprNode::In, pRelational, pRelational}, // Token::In {ExprNode::Instanceof, pRelational, pRelational}, // Token::Instanceof + {ExprNode::none, pExpression, pNone}, // Token::Interface {ExprNode::none, pExpression, pNone}, // Token::Native {ExprNode::none, pExpression, pNone}, // Token::New {ExprNode::none, pExpression, pNone}, // Token::Null @@ -2589,6 +2666,21 @@ JS::ExprNode *JS::Parser::parseParenthesizedExpression() } +// Parse and return a TypeExpression. If noIn is false, allow the in operator. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseTypeExpression finishes, the next token might have been peeked with preferRegExp set to true. +JS::ExprNode *JS::Parser::parseTypeExpression(bool noIn) +{ + ExprNode *type = parseNonAssignmentExpression(noIn); + if (lexer.peek(false).hasKind(Token::divideEquals)) + syntaxError("'/=' not allowed here", 0); + lexer.redesignate(true); // Safe: a '/' would have been interpreted as an operator, so it can't be the next token; + // a '/=' was outlawed by the check above. + return type; +} + + // Parse a TypedIdentifier. Return the identifier's name. // If a type was provided, set type to it; otherwise, set type to nil. // After parseTypedIdentifier finishes, the next token might have been peeked with preferRegExp set to false. @@ -2606,25 +2698,36 @@ const JS::StringAtom &JS::Parser::parseTypedIdentifier(ExprNode *&type) } -// If the next token is ':', eat it and parse and return the following TypeExpression. Otherwise return nil. +// If the next token has the given kind, eat it and parse and return the following TypeExpression; +// otherwise return nil. // If noIn is false, allow the in operator. // // If the first token was peeked, it should be have been done with preferRegExp set to true. // After parseTypeBinding finishes, the next token might have been peeked with preferRegExp set to true. -JS::ExprNode *JS::Parser::parseTypeBinding(bool noIn) +JS::ExprNode *JS::Parser::parseTypeBinding(Token::Kind kind, bool noIn) { ExprNode *type = 0; - if (lexer.eat(true, Token::colon)) { - type = parseNonAssignmentExpression(noIn); - if (lexer.peek(false).hasKind(Token::divideEquals)) - syntaxError("'/=' not allowed here", 0); - lexer.redesignate(true); // Safe: a '/' would have been interpreted as an operator, so it can't be the next token; - // a '/=' was outlawed by the check above. - } + if (lexer.eat(true, kind)) + type = parseTypeExpression(noIn); return type; } +// If the next token has the given kind, eat it and parse and return the following TypeExpressionList; +// otherwise return nil. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseTypeListBinding finishes, the next token might have been peeked with preferRegExp set to true. +JS::ExprList *JS::Parser::parseTypeListBinding(Token::Kind kind) +{ + NodeQueue types; + if (lexer.eat(true, kind)) + do types += new(arena) ExprList(parseTypeExpression(false)); + while (lexer.eat(true, Token::comma)); + return types.first; +} + + // Parse and return a VariableBinding. // If noQualifiers is false, allow a QualifiedIdentifier as the variable name; otherwise, restrict the // variable name to be a simple Identifier. @@ -2645,7 +2748,7 @@ JS::VariableBinding *JS::Parser::parseVariableBinding(bool noQualifiers, bool no } else name = parseQualifiedIdentifier(t, true); - ExprNode *type = parseTypeBinding(noIn); + ExprNode *type = parseTypeBinding(Token::colon, noIn); ExprNode *initializer = 0; if (lexer.eat(true, Token::assignment)) { @@ -2720,7 +2823,7 @@ void JS::Parser::parseFunctionSignature(FunctionDefinition &fd) fd.parameters = parameters.first; fd.optParameters = optParameters; fd.restParameter = restParameter; - fd.resultType = parseTypeBinding(false); + fd.resultType = parseTypeBinding(Token::colon, false); } @@ -2759,12 +2862,25 @@ JS::StmtNode *JS::Parser::parseBlock(bool inSwitch, bool noCloseBrace) } -// Parse a list of statements ending with a '}'. Return these statements as a BlockStmtNode -// with the given attributes. The opening '{' has already been read. -// pos is the position of the beginning of the statement (its first attribute if it has attributes). -JS::BlockStmtNode *JS::Parser::parseBlockStatement(uint32 pos, IdentifierList *attributes) +// Parse an optional block of statements beginning with a '{' and ending with a '}'. +// Return these statements as a BlockStmtNode. +// If semicolonState is nil, the block is required; otherwise, the block is optional and if it is +// omitted, *semicolonState is set to semiInsertable. +// +// If the first token was peeked, it should be have been done with preferRegExp set to true. +// After parseBody finishes, the next token might have been peeked with preferRegExp set to true. +JS::BlockStmtNode *JS::Parser::parseBody(SemicolonState *semicolonState) { - return new(arena) BlockStmtNode(pos, StmtNode::block, attributes, parseBlock(false, false)); + const Token *tBrace = lexer.eat(true, Token::openBrace); + if (tBrace) { + uint32 pos = tBrace->getPos(); + return new(arena) BlockStmtNode(pos, StmtNode::block, 0, parseBlock(false, false)); + } else { + if (!semicolonState) + syntaxError("'{' expected", 0); + *semicolonState = semiInsertable; + return 0; + } } @@ -2788,7 +2904,7 @@ JS::StmtNode *JS::Parser::parseAttributeStatement(uint32 pos, IdentifierList *at switch (t.getKind()) { case Token::openBrace: - return parseBlockStatement(pos, attributes); + return new(arena) BlockStmtNode(pos, StmtNode::block, attributes, parseBlock(false, false)); case Token::Const: sKind = StmtNode::Const; @@ -2821,16 +2937,37 @@ JS::StmtNode *JS::Parser::parseAttributeStatement(uint32 pos, IdentifierList *at } else parseFunctionName(f->function); parseFunctionSignature(f->function); - const Token *t3 = lexer.eat(true, Token::openBrace); - if (t3) - f->function.body = parseBlockStatement(t3->getPos(), 0); - else { - f->function.body = 0; - semicolonState = semiInsertable; - } + f->function.body = parseBody(&semicolonState); return f; } + case Token::Interface: + sKind = StmtNode::Interface; + goto classOrInterface; + case Token::Class: + sKind = StmtNode::Class; + classOrInterface: + { + ExprNode *name = parseQualifiedIdentifier(lexer.get(true), true); + ExprNode *superclass = 0; + if (sKind == StmtNode::Class) + superclass = parseTypeBinding(Token::Extends, false); + ExprList *superinterfaces = parseTypeListBinding(sKind == StmtNode::Class ? Token::Implements : Token::Extends); + BlockStmtNode *body = parseBody(superclass || superinterfaces ? 0 : &semicolonState); + return new(arena) ClassStmtNode(pos, sKind, attributes, name, superclass, superinterfaces, body); + } + + case Token::Namespace: + { + const Token &t2 = lexer.get(false); + ExprNode *name; + if (lineBreakBefore(t2) || !(name = makeIdentifierExpression(t2))) + syntaxError("Namespace name expected"); + ExprList *supernamespaces = parseTypeListBinding(Token::Extends); + semicolonState = semiInsertable; + return new(arena) NamespaceStmtNode(pos, StmtNode::Namespace, attributes, name, supernamespaces); + } + default: syntaxError("Bad declaration"); return 0; @@ -3024,6 +3161,7 @@ JS::StmtNode *JS::Parser::parseStatement(bool topLevel, bool inSwitch, Semicolon break; case Token::Constructor: + case Token::Namespace: t2 = &lexer.peek(false); if (lineBreakBefore(*t2) || !t2->hasIdentifierKind()) goto makeExpression; @@ -3031,6 +3169,8 @@ JS::StmtNode *JS::Parser::parseStatement(bool topLevel, bool inSwitch, Semicolon case Token::Const: case Token::Var: case Token::Function: + case Token::Class: + case Token::Interface: s = parseAttributeStatement(pos, 0, t, false, semicolonState); break; diff --git a/js2/src/parser.h b/js2/src/parser.h index 75086259df7..40ec0851fa6 100644 --- a/js2/src/parser.h +++ b/js2/src/parser.h @@ -195,6 +195,7 @@ namespace JavaScript { Import, // import In, // in Instanceof, // instanceof + Interface, // interface Native, // native New, // new Null, // null @@ -881,6 +882,9 @@ namespace JavaScript { ExprNode *expr; // Attribute expression; non-nil only explicit ExprList(ExprNode *expr): next(0), expr(expr) {ASSERT(expr);} + + void printCommaList(PrettyPrinter &f) const; + static void printOptionalCommaList(PrettyPrinter &f, const char *name, const ExprList *list); }; struct UseStmtNode: StmtNode { @@ -927,15 +931,19 @@ namespace JavaScript { NamespaceStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprList *supers): AttributeStmtNode(pos, kind, attributes), name(name), supers(supers) {ASSERT(name);} + + void print(PrettyPrinter &f, bool noSemi) const; }; struct ClassStmtNode: NamespaceStmtNode { - ExprNode *superclass; // Superclass expression (classes only) - BlockStmtNode *body; // The class's body; non-nil only + ExprNode *superclass; // Superclass expression (classes only); nil if omitted + BlockStmtNode *body; // The class's body; nil if omitted ClassStmtNode(uint32 pos, Kind kind, IdentifierList *attributes, ExprNode *name, ExprNode *superclass, ExprList *superinterfaces, BlockStmtNode *body): - NamespaceStmtNode(pos, kind, attributes, name, superinterfaces), superclass(superclass), body(body) {ASSERT(body);} + NamespaceStmtNode(pos, kind, attributes, name, superinterfaces), superclass(superclass), body(body) {} + + void print(PrettyPrinter &f, bool noSemi) const; }; struct LanguageStmtNode: StmtNode { @@ -1022,8 +1030,10 @@ namespace JavaScript { ExprNode *parseAssignmentExpression(bool noIn) {return parseExpression(noIn, false, true);} private: ExprNode *parseParenthesizedExpression(); + ExprNode *parseTypeExpression(bool noIn); const StringAtom &parseTypedIdentifier(ExprNode *&type); - ExprNode *parseTypeBinding(bool noIn); + ExprNode *parseTypeBinding(Token::Kind kind, bool noIn); + ExprList *parseTypeListBinding(Token::Kind kind); VariableBinding *parseVariableBinding(bool noQualifiers, bool noIn); void parseFunctionName(FunctionName &fn); void parseFunctionSignature(FunctionDefinition &fd); @@ -1031,7 +1041,7 @@ namespace JavaScript { enum SemicolonState {semiNone, semiNoninsertable, semiInsertable}; enum AttributeStatement {asAny, asBlock, asConstVar}; StmtNode *parseBlock(bool inSwitch, bool noCloseBrace); - BlockStmtNode *parseBlockStatement(uint32 pos, IdentifierList *attributes); + BlockStmtNode *parseBody(SemicolonState *semicolonState); StmtNode *parseAttributeStatement(uint32 pos, IdentifierList *attributes, const Token &t, bool noIn, SemicolonState &semicolonState); StmtNode *parseAttributesAndStatement(const Token *t, AttributeStatement as, SemicolonState &semicolonState); StmtNode *parseAnnotatedBlock();