зеркало из https://github.com/mozilla/pjs.git
Added parsing and printing of classes, interfaces, and namespaces
This commit is contained in:
Родитель
eee9dbdf9b
Коммит
2c96ad5323
|
@ -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<ExprList> 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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<ExprList> 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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче