Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor parsing of optional chaining #1390

Merged
merged 1 commit into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 73 additions & 124 deletions src/parser/esprima_cpp/esprima.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2465,7 +2465,6 @@ class Parser {
if (this->context->inFunctionBody && this->matchKeyword(SuperKeyword)) {
throwIfSuperOperationIsNotAllowed();
this->currentScopeContext->m_hasSuperOrNewTarget = true;

exprNode = this->finishSuperExpression(builder, this->createNode(), this->lookahead.valuePunctuatorKind == LeftParenthesis);
} else if (UNLIKELY(this->matchKeyword(NewKeyword))) {
exprNode = this->inheritCoverGrammar(builder, &Parser::parseNewExpression<ASTBuilder>);
Expand All @@ -2477,86 +2476,65 @@ class Parser {
exprNode = this->inheritCoverGrammar(builder, &Parser::parsePrimaryExpression<ASTBuilder>);
}

bool seenOptionalExpression = false;
bool hasOptional = false;
while (true) {
bool isPunctuatorTokenLookahead = this->lookahead.type == Token::PunctuatorToken;
if (isPunctuatorTokenLookahead) {
if (this->lookahead.valuePunctuatorKind == Period) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !seenOptionalExpression;
this->nextToken();
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, this->startNode(startToken), exprNode);
} else if (this->lookahead.valuePunctuatorKind == LeftParenthesis) {
bool asyncArrow = maybeAsync && (startToken->lineNumber == this->lookahead.lineNumber);
this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
ASTNodeList args = asyncArrow ? this->parseAsyncArguments(builder) : this->parseArguments(builder);
// check callee of CallExpressionNode
if (exprNode->isIdentifier() && exprNode->asIdentifier()->name() == escargotContext->staticStrings().eval) {
this->currentScopeContext->m_hasEval = true;
}
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode, args, false));
if (asyncArrow && this->match(Arrow)) {
for (ASTSentinelNode arg = args.begin(); arg != args.end(); arg = arg->next()) {
arg->setASTNode(builder.reinterpretExpressionAsPattern(arg->astNode()));
}
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(args, true));
}
} else if (this->lookahead.valuePunctuatorKind == LeftSquareBracket) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !seenOptionalExpression;
this->nextToken();
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode, property, false));
this->expect(RightSquareBracket);
} else if (this->lookahead.valuePunctuatorKind == GuessDot) {
bool oldBindingElement = this->context->isBindingElement;
bool oldAssignmentTarget = this->context->isAssignmentTarget;
Marker startMarker = this->startMarker;
bool optional = false;
if (this->match(GuessDot)) {
Marker startMarker = this->startMarker;
bool oldBindingElement = this->context->isBindingElement;
bool oldAssignmentTarget = this->context->isAssignmentTarget;

this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
optional = true;
hasOptional = true;
this->nextToken();

if (this->lookahead.type == Token::NumericLiteralToken) {
// not an optional chainining, GuessMark followed by a number e.g) ?.30
// roll back to the start point of GuessDot
this->context->isBindingElement = oldBindingElement;
this->context->isAssignmentTarget = oldAssignmentTarget;
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
if (this->match(PunctuatorKind::LeftSquareBracket)) {
seenOptionalExpression = true;
this->nextToken();
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode, property, false, true));
this->expect(RightSquareBracket);
if (this->lookahead.type == Token::TemplateToken) {
this->throwUnexpectedToken(this->lookahead);
}
} else if (this->lookahead.type == Token::IdentifierToken || this->match(PunctuatorKind::Hash)) {
seenOptionalExpression = true;
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, this->startNode(startToken), exprNode, true);
if (this->lookahead.type == Token::TemplateToken) {
this->throwUnexpectedToken(this->lookahead);
}
} else if (this->match(PunctuatorKind::LeftParenthesis)) {
seenOptionalExpression = true;
ASTNodeList args = this->parseArguments(builder);
// check callee of CallExpressionNode
if (exprNode->isIdentifier() && exprNode->asIdentifier()->name() == escargotContext->staticStrings().eval) {
this->currentScopeContext->m_hasEval = true;
}
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode, args, true));
} else {
this->context->isBindingElement = oldBindingElement;
this->context->isAssignmentTarget = oldAssignmentTarget;
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
ASSERT(this->match(PunctuatorKind::GuessDot));
ASSERT(this->match(PunctuatorKind::GuessDot));

this->scanner->index--;
this->lookahead.valuePunctuatorKind = GuessMark;
break;
}
} else {
this->scanner->index--;
this->lookahead.valuePunctuatorKind = GuessMark;
hasOptional = false;
optional = false;
break;
}
}

if (this->match(LeftParenthesis)) {
bool asyncArrow = maybeAsync && (startToken->lineNumber == this->lookahead.lineNumber);
this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
ASTNodeList args = asyncArrow ? this->parseAsyncArguments(builder) : this->parseArguments(builder);
// check callee of CallExpressionNode
if (exprNode->isIdentifier() && exprNode->asIdentifier()->name() == escargotContext->staticStrings().eval) {
this->currentScopeContext->m_hasEval = true;
}
exprNode = this->finalize(this->startNode(startToken), builder.createCallExpressionNode(exprNode, args, optional));
if (asyncArrow && this->match(Arrow)) {
for (ASTSentinelNode arg = args.begin(); arg != args.end(); arg = arg->next()) {
arg->setASTNode(builder.reinterpretExpressionAsPattern(arg->astNode()));
}
exprNode = this->finalize(this->createNode(), builder.createArrowParameterPlaceHolderNode(args, true));
}
} else if (this->match(LeftSquareBracket)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !hasOptional;
this->expect(LeftSquareBracket);
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
this->expect(RightSquareBracket);
exprNode = this->finalize(this->startNode(startToken), builder.createMemberExpressionNode(exprNode, property, false, optional));
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
if (hasOptional) {
this->throwUnexpectedToken(this->lookahead);
}

ASTNode quasi = this->parseTemplateLiteral(builder, true);
ASTNode convertedNode = builder.convertTaggedTemplateExpressionToCallExpression(quasi, exprNode, this->taggedTemplateExpressionIndex, this->currentScopeContext, escargotContext->staticStrings().raw);
if (builder.willGenerateByteCode()) {
Expand All @@ -2566,13 +2544,23 @@ class Parser {
// CodeBlock should allocate a RareData for caching
this->currentScopeContext->m_needRareData = true;
exprNode = this->finalize(this->startNode(startToken), builder.createTaggedTemplateExpressionNode(exprNode, quasi, convertedNode));
} else if (this->match(Period)) {
ASSERT(!optional);
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !hasOptional;
this->nextToken();
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, this->startNode(startToken), exprNode, optional);
} else if (optional) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !hasOptional;
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, this->startNode(startToken), exprNode, optional);
} else {
break;
}
}
this->context->allowIn = previousAllowIn;

if (seenOptionalExpression) {
this->context->allowIn = previousAllowIn;
if (hasOptional) {
ASSERT(exprNode->type() == MemberExpression || exprNode->type() == CallExpression);
exprNode->setStartOfOptionalChaining();
}
Expand Down Expand Up @@ -2656,20 +2644,19 @@ class Parser {
}

MetaNode node = this->startNode(&this->lookahead);
bool seenOptionalExpression = false;

while (true) {
if (this->match(GuessDot)) {
this->throwUnexpectedToken(this->lookahead);
}

if (this->match(LeftSquareBracket)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !seenOptionalExpression;
this->context->isAssignmentTarget = false;
this->nextToken();
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, false));
this->expect(RightSquareBracket);
} else if (this->match(Period)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = !seenOptionalExpression;
this->nextToken();
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, node, exprNode);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, false));
} else if (this->lookahead.type == Token::TemplateToken && this->lookahead.valueTemplate->head) {
ASTNode quasi = this->parseTemplateLiteral(builder, true);
ASTNode convertedNode = builder.convertTaggedTemplateExpressionToCallExpression(quasi, exprNode, this->taggedTemplateExpressionIndex, this->currentScopeContext, escargotContext->staticStrings().raw);
Expand All @@ -2680,54 +2667,16 @@ class Parser {
// CodeBlock should allocate a RareData for caching
this->currentScopeContext->m_needRareData = true;
exprNode = this->finalize(node, builder.createTaggedTemplateExpressionNode(exprNode, quasi, convertedNode));
} else if (this->lookahead.type == Token::PunctuatorToken && this->lookahead.valuePunctuatorKind == GuessDot) {
bool oldBindingElement = this->context->isBindingElement;
bool oldAssignmentTarget = this->context->isAssignmentTarget;
Marker startMarker = this->startMarker;

} else if (this->match(Period)) {
this->context->isBindingElement = false;
this->context->isAssignmentTarget = false;
this->nextToken();
if (this->match(PunctuatorKind::LeftSquareBracket)) {
seenOptionalExpression = true;
this->nextToken();
ASTNode property = this->isolateCoverGrammar(builder, &Parser::parseExpression<ASTBuilder>);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, false, true));
this->expect(RightSquareBracket);
if (this->lookahead.type == Token::TemplateToken) {
this->throwUnexpectedToken(this->lookahead);
}
} else if (this->lookahead.type == Token::IdentifierToken) {
seenOptionalExpression = true;
TrackUsingNameBlocker blocker(this);
ASTNode property = this->parseIdentifierName(builder);
exprNode = this->finalize(node, builder.createMemberExpressionNode(exprNode, property, true, true));
if (this->lookahead.type == Token::TemplateToken) {
this->throwUnexpectedToken(this->lookahead);
}
} else {
this->context->isBindingElement = oldBindingElement;
this->context->isAssignmentTarget = oldAssignmentTarget;
this->scanner->index = startMarker.index;
this->scanner->lineNumber = startMarker.lineNumber;
this->scanner->lineStart = startMarker.lineStart;
this->nextToken();
ASSERT(this->match(PunctuatorKind::GuessDot));

this->scanner->index--;
this->lookahead.valuePunctuatorKind = GuessMark;
break;
}
exprNode = parseMemberExpressionPreComputedCase<ASTBuilder>(builder, node, exprNode);
} else {
break;
}
}

if (seenOptionalExpression) {
ASSERT(exprNode->type() == MemberExpression);
exprNode->setStartOfOptionalChaining();
}

return exprNode;
}

Expand Down
2 changes: 1 addition & 1 deletion test/vendortest
Loading