Skip to content

Commit

Permalink
Parse Flow match core patterns
Browse files Browse the repository at this point in the history
Summary:
Adds parsing for Flow match core patterns (literals, binding, identifier, wildcard).

Parsing for object, array, etc. patterns in future diffs.

Reviewed By: avp

Differential Revision: D65763781

fbshipit-source-id: f199769c9cee3f706c67a1dd4621a0c3b70726ca
  • Loading branch information
gkz authored and facebook-github-bot committed Dec 7, 2024
1 parent 5c89fa3 commit 8e23cfa
Show file tree
Hide file tree
Showing 12 changed files with 666 additions and 29 deletions.
27 changes: 27 additions & 0 deletions include/hermes/AST/ESTree.def
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,33 @@ ESTREE_NODE_3_ARGS(
NodePtr, guard, true // Expression | null
)

// Match patterns
ESTREE_FIRST(MatchPattern, Base)

ESTREE_NODE_0_ARGS(
MatchWildcardPattern, MatchPattern
)
ESTREE_NODE_1_ARGS(
MatchLiteralPattern, MatchPattern,
NodePtr, literal, false // Literal
)
ESTREE_NODE_2_ARGS(
MatchUnaryPattern, MatchPattern,
NodePtr, argument, false, // Literal
NodeLabel, operator, false // "-" | "+"
)
ESTREE_NODE_1_ARGS(
MatchIdentifierPattern, MatchPattern,
NodePtr, id, false // Identifier
)
ESTREE_NODE_2_ARGS(
MatchBindingPattern, MatchPattern,
NodePtr, id, false, // Identifier
NodeLabel, kind, false // "let" | "const"
)

ESTREE_LAST(MatchPattern)

#endif

// ================================================
Expand Down
1 change: 1 addition & 0 deletions include/hermes/AST/ESTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ class JSXDecoration {};
class FlowDecoration {};
class TSDecoration {};
class PatternDecoration {};
class MatchPatternDecoration {};
class CoverDecoration {};

class CallExpressionLikeDecoration {};
Expand Down
184 changes: 182 additions & 2 deletions lib/Parser/JSParserImpl-flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ Optional<ESTree::Node *> JSParserImpl::tryParseMatchStatementFlow(Param param) {
while (!check(TokenKind::r_brace)) {
SMLoc caseStartLoc = tok_->getStartLoc();

auto optPattern = parseExpression(); // TODO:match: parse match patterns
auto optPattern = parseMatchPatternFlow();
if (!optPattern)
return None;

Expand Down Expand Up @@ -954,7 +954,7 @@ Optional<ESTree::Node *> JSParserImpl::parseMatchExpressionFlow(
while (!check(TokenKind::r_brace)) {
SMLoc caseStartLoc = tok_->getStartLoc();

auto optPattern = parseExpression(); // TODO:match: parse match patterns
auto optPattern = parseMatchPatternFlow();
if (!optPattern)
return None;

Expand Down Expand Up @@ -1003,6 +1003,186 @@ Optional<ESTree::Node *> JSParserImpl::parseMatchExpressionFlow(
new (context_) ESTree::MatchExpressionNode(argument, std::move(cases)));
}

Optional<ESTree::Node *> JSParserImpl::parseMatchPatternFlow() {
switch (tok_->getKind()) {
case TokenKind::rw_null: {
auto *lit =
setLocation(tok_, tok_, new (context_) ESTree::NullLiteralNode());
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchLiteralPatternNode(lit));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::rw_true:
case TokenKind::rw_false: {
auto *lit = setLocation(
tok_,
tok_,
new (context_) ESTree::BooleanLiteralNode(
tok_->getKind() == TokenKind::rw_true));
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchLiteralPatternNode(lit));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::numeric_literal: {
auto *lit = setLocation(
tok_,
tok_,
new (context_) ESTree::NumericLiteralNode(tok_->getNumericLiteral()));
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchLiteralPatternNode(lit));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::bigint_literal: {
auto *lit = setLocation(
tok_,
tok_,
new (context_) ESTree::BigIntLiteralNode(tok_->getBigIntLiteral()));
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchLiteralPatternNode(lit));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::string_literal: {
auto *lit = setLocation(
tok_,
tok_,
new (context_) ESTree::StringLiteralNode(tok_->getStringLiteral()));
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchLiteralPatternNode(lit));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::identifier: {
if (check(underscoreIdent_)) {
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchWildcardPatternNode());
advance(JSLexer::AllowDiv);
return pat;
}
if (check(letIdent_)) {
auto pat = parseMatchBindingPatternFlow();
if (!pat)
return None;
return pat.getValue();
}
auto *ident = setLocation(
tok_,
tok_,
new (context_)
ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false));
auto *pat = setLocation(
tok_, tok_, new (context_) ESTree::MatchIdentifierPatternNode(ident));
advance(JSLexer::AllowDiv);
return pat;
}

case TokenKind::plus:
case TokenKind::minus: {
UniqueString *op = getTokenIdent(tok_->getKind());
SMLoc startLoc = advance().Start;

ESTree::Node *argument;
switch (tok_->getKind()) {
case TokenKind::numeric_literal: {
argument = setLocation(
tok_,
tok_,
new (context_)
ESTree::NumericLiteralNode(tok_->getNumericLiteral()));
advance(JSLexer::AllowDiv);
break;
}
case TokenKind::bigint_literal: {
argument = setLocation(
tok_,
tok_,
new (context_)
ESTree::BigIntLiteralNode(tok_->getBigIntLiteral()));
advance(JSLexer::AllowDiv);
break;
}
default:
error(tok_->getStartLoc(), "invalid match unary pattern argument");
return None;
}
return setLocation(
startLoc,
getPrevTokenEndLoc(),
new (context_) ESTree::MatchUnaryPatternNode(argument, op));
}

case TokenKind::rw_const:
case TokenKind::rw_var: {
auto pat = parseMatchBindingPatternFlow();
if (!pat)
return None;
return pat.getValue();
}

case TokenKind::l_paren: {
SMLoc startLoc = advance().Start;
auto optPattern = parseMatchPatternFlow();
if (!optPattern)
return None;

if (!eat(
TokenKind::r_paren,
JSLexer::AllowDiv,
"at end of a match pattern group",
"location of '('",
startLoc))
return None;

return optPattern.getValue();
}

default:
error(tok_->getStartLoc(), "invalid match pattern");
return None;
}
}

Optional<ESTree::IdentifierNode *>
JSParserImpl::parseMatchBindingIdentifierFlow() {
UniqueString *id = tok_->getResWordOrIdentifier();
TokenKind kind = tok_->getKind();
if (!validateBindingIdentifier(Param{}, tok_->getSourceRange(), id, kind))
return None;
advance();
return setLocation(
tok_, tok_, new (context_) ESTree::IdentifierNode(id, nullptr, false));
}

Optional<ESTree::MatchBindingPatternNode *>
JSParserImpl::parseMatchBindingPatternFlow() {
assert(checkN(TokenKind::rw_const, TokenKind::rw_var, letIdent_));
auto kind = tok_->getResWordOrIdentifier();
SMLoc startLoc = advance().Start;
if (!check(TokenKind::identifier) && !tok_->isResWord()) {
errorExpected(
TokenKind::identifier,
"in match binding pattern",
"start of binding pattern",
startLoc);
}
auto optIdent = parseMatchBindingIdentifierFlow();
if (!optIdent)
return None;
return setLocation(
startLoc,
getPrevTokenEndLoc(),
new (context_)
ESTree::MatchBindingPatternNode(optIdent.getValue(), kind));
}

Optional<ESTree::Node *> JSParserImpl::parseTypeAliasFlow(
SMLoc start,
TypeAliasKind kind) {
Expand Down
1 change: 1 addition & 0 deletions lib/Parser/JSParserImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ void JSParserImpl::initializeIdentifiers() {

// Flow match expressions and statements
matchIdent_ = lexer_.getIdentifier("match");
underscoreIdent_ = lexer_.getIdentifier("_");
#endif

#if HERMES_PARSE_TS
Expand Down
4 changes: 4 additions & 0 deletions lib/Parser/JSParserImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class JSParserImpl {
UniqueString *rendersStarOperator_;

UniqueString *matchIdent_;
UniqueString *underscoreIdent_;
#endif

#if HERMES_PARSE_TS
Expand Down Expand Up @@ -1224,6 +1225,9 @@ class JSParserImpl {
Optional<ESTree::Node *> parseMatchExpressionFlow(
SMLoc start,
ESTree::Node *argument);
Optional<ESTree::Node *> parseMatchPatternFlow();
Optional<ESTree::IdentifierNode *> parseMatchBindingIdentifierFlow();
Optional<ESTree::MatchBindingPatternNode *> parseMatchBindingPatternFlow();

enum class TypeAliasKind { None, Declare, Opaque, DeclareOpaque };
Optional<ESTree::Node *> parseTypeAliasFlow(SMLoc start, TypeAliasKind kind);
Expand Down
9 changes: 6 additions & 3 deletions test/Parser/flow/match/expression-guards.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ const e = match (a) {
// CHECK-NEXT: {
// CHECK-NEXT: "type": "MatchExpressionCase",
// CHECK-NEXT: "pattern": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: "type": "MatchLiteralPattern",
// CHECK-NEXT: "literal": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "BooleanLiteral",
Expand Down
25 changes: 17 additions & 8 deletions test/Parser/flow/match/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ const e = match (a) {
// CHECK-NEXT: {
// CHECK-NEXT: "type": "MatchExpressionCase",
// CHECK-NEXT: "pattern": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: "type": "MatchLiteralPattern",
// CHECK-NEXT: "literal": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 1,
// CHECK-NEXT: "raw": "1"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "BooleanLiteral",
Expand All @@ -48,8 +51,11 @@ const e = match (a) {
// CHECK-NEXT: {
// CHECK-NEXT: "type": "MatchExpressionCase",
// CHECK-NEXT: "pattern": {
// CHECK-NEXT: "type": "StringLiteral",
// CHECK-NEXT: "value": "foo"
// CHECK-NEXT: "type": "MatchLiteralPattern",
// CHECK-NEXT: "literal": {
// CHECK-NEXT: "type": "StringLiteral",
// CHECK-NEXT: "value": "foo"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "BooleanLiteral",
Expand All @@ -63,9 +69,12 @@ const e = match (a) {
// CHECK-NEXT: {
// CHECK-NEXT: "type": "MatchExpressionCase",
// CHECK-NEXT: "pattern": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 2,
// CHECK-NEXT: "raw": "2"
// CHECK-NEXT: "type": "MatchLiteralPattern",
// CHECK-NEXT: "literal": {
// CHECK-NEXT: "type": "NumericLiteral",
// CHECK-NEXT: "value": 2,
// CHECK-NEXT: "raw": "2"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "body": {
// CHECK-NEXT: "type": "ObjectExpression",
Expand Down
Loading

0 comments on commit 8e23cfa

Please sign in to comment.