Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 6945a72

Browse files
stereotype441Commit Queue
authored andcommitted
Patterns: initial parser support.
The parser now supports the following subset of the parser grammar: - logical-or and logical-and patterns (called "binary patterns" to reflect analyzer nomenclature) - extractor patterns - cast patterns - list patterns - map patterns - null-assert patterns - null-check patterns - variable patterns where the variable is preceded by `var`, `final`, <type>, or `final <type>` - if-case statements (and if-case within collections); without guards - record patterns - parenthesized patterns - constant patterns where the constant is a plain expression not beginning with `const` (booleanLiteral, nullLiteral, numericLiteral, stringLiteral, identifier, or qualifiedName). - relational patterns - patterns in switch statements - integration with the analyzer's AstBuilder class Not implemented yet: - constant patterns beginning with `const` - variable patterns where the variable is a single identifier (note: this means that `_` is currently interpreted as a constant pattern rather than a "wildcard" variable pattern) - guards (a.k.a. "when clauses") - switch expressions - pattern variable declarations - patterns appearing in "for loop parts" - pattern assignment - several error checking and error recovery scenarios (see TODO comments) - integration with the front_end's BodyBuilder class - front_end style parser tests (currently the feature is tested using analyzer unit tests only) Note that in patterns, `as` binds has higher precedence than `&` and `|`, whereas in expressions, `&` and `|` have higher precedence than `as`. To reflect this, a new precedence has been added, CAST_PATTERN_PRECEDENCE. To reduce the risk to users during parser development, the parser currently only attempts to parse patterns when instructed to do so (i.e. when the language feature is enabled). In the long term, I intend to change the parser so that it always attempts to parse patterns, and it is the responsibility of its listener to report errors if patterns are used without enabling the language feature. Change-Id: I360b535d2a6ebd35a0ee4d066b06e3ae8e3121ef Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/261020 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Jens Johansen <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent d7e130e commit 6945a72

22 files changed

+4183
-57
lines changed

pkg/_fe_analyzer_shared/lib/src/parser/class_member_parser.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ class ClassMemberParser extends Parser {
4545
// This method is overridden for two reasons:
4646
// 1. Avoid generating events for arguments.
4747
// 2. Avoid calling skip expression for each argument (which doesn't work).
48-
Token parseArgumentsOpt(Token token) => skipArgumentsOpt(token);
48+
Token parseArgumentsOpt(Token token, {bool forPattern = false}) =>
49+
skipArgumentsOpt(token);
4950

5051
Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) {
5152
return skipFunctionBody(token, isExpression, allowAbstract);

pkg/_fe_analyzer_shared/lib/src/parser/forwarding_listener.dart

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class ForwardingListener implements Listener {
3535
listener?.beginBinaryExpression(token);
3636
}
3737

38+
@override
39+
void beginBinaryPattern(Token token) {
40+
listener?.beginBinaryPattern(token);
41+
}
42+
3843
@override
3944
void beginBlock(Token token, BlockKind blockKind) {
4045
listener?.beginBlock(token, blockKind);
@@ -522,6 +527,12 @@ class ForwardingListener implements Listener {
522527
listener?.endArguments(count, beginToken, endToken);
523528
}
524529

530+
@override
531+
void handleExtractorPatternFields(
532+
int count, Token beginToken, Token endToken) {
533+
listener?.handleExtractorPatternFields(count, beginToken, endToken);
534+
}
535+
525536
@override
526537
void endAssert(Token assertKeyword, Assert kind, Token leftParenthesis,
527538
Token? commaToken, Token semicolonToken) {
@@ -539,6 +550,11 @@ class ForwardingListener implements Listener {
539550
listener?.endBinaryExpression(token);
540551
}
541552

553+
@override
554+
void endBinaryPattern(Token token) {
555+
listener?.endBinaryPattern(token);
556+
}
557+
542558
@override
543559
void handleEndingBinaryExpression(Token token) {
544560
listener?.handleEndingBinaryExpression(token);
@@ -1254,6 +1270,11 @@ class ForwardingListener implements Listener {
12541270
listener?.handleAsOperator(operator);
12551271
}
12561272

1273+
@override
1274+
void handleCastPattern(Token operator) {
1275+
listener?.handleCastPattern(operator);
1276+
}
1277+
12571278
@override
12581279
void handleAssignmentExpression(Token token) {
12591280
listener?.handleAssignmentExpression(token);
@@ -1561,11 +1582,21 @@ class ForwardingListener implements Listener {
15611582
listener?.handleLiteralList(count, beginToken, constKeyword, endToken);
15621583
}
15631584

1585+
@override
1586+
void handleListPattern(int count, Token beginToken, Token endToken) {
1587+
listener?.handleListPattern(count, beginToken, endToken);
1588+
}
1589+
15641590
@override
15651591
void handleLiteralMapEntry(Token colon, Token endToken) {
15661592
listener?.handleLiteralMapEntry(colon, endToken);
15671593
}
15681594

1595+
@override
1596+
void handleMapPatternEntry(Token colon, Token endToken) {
1597+
listener?.handleMapPatternEntry(colon, endToken);
1598+
}
1599+
15691600
@override
15701601
void handleLiteralNull(Token token) {
15711602
listener?.handleLiteralNull(token);
@@ -1585,6 +1616,11 @@ class ForwardingListener implements Listener {
15851616
count, leftBrace, constKeyword, rightBrace, hasSetEntry);
15861617
}
15871618

1619+
@override
1620+
void handleMapPattern(int count, Token leftBrace, Token rightBrace) {
1621+
listener?.handleMapPattern(count, leftBrace, rightBrace);
1622+
}
1623+
15881624
@override
15891625
void handleMixinHeader(Token mixinKeyword) {
15901626
listener?.handleMixinHeader(mixinKeyword);
@@ -1600,6 +1636,11 @@ class ForwardingListener implements Listener {
16001636
listener?.handleNamedArgument(colon);
16011637
}
16021638

1639+
@override
1640+
void handlePatternField(Token? colon) {
1641+
listener?.handlePatternField(colon);
1642+
}
1643+
16031644
@override
16041645
void handleNamedRecordField(Token colon) {
16051646
listener?.handleNamedRecordField(colon);
@@ -1680,6 +1721,21 @@ class ForwardingListener implements Listener {
16801721
listener?.handleNonNullAssertExpression(bang);
16811722
}
16821723

1724+
@override
1725+
void handleNullAssertPattern(Token bang) {
1726+
listener?.handleNullAssertPattern(bang);
1727+
}
1728+
1729+
@override
1730+
void handleNullCheckPattern(Token question) {
1731+
listener?.handleNullCheckPattern(question);
1732+
}
1733+
1734+
@override
1735+
void handleVariablePattern(Token? keyword, Token variable) {
1736+
listener?.handleVariablePattern(keyword, variable);
1737+
}
1738+
16831739
@override
16841740
void handleNoType(Token lastConsumed) {
16851741
listener?.handleNoType(lastConsumed);
@@ -1716,8 +1772,8 @@ class ForwardingListener implements Listener {
17161772
}
17171773

17181774
@override
1719-
void handleParenthesizedCondition(Token token, [Token? case_]) {
1720-
listener?.handleParenthesizedCondition(token);
1775+
void handleParenthesizedCondition(Token token, Token? case_) {
1776+
listener?.handleParenthesizedCondition(token, case_);
17211777
}
17221778

17231779
@override
@@ -1730,11 +1786,32 @@ class ForwardingListener implements Listener {
17301786
listener?.endRecordLiteral(token, count, constKeyword);
17311787
}
17321788

1789+
@override
1790+
void handleRecordPattern(Token token, int count) {
1791+
listener?.handleRecordPattern(token, count);
1792+
}
1793+
17331794
@override
17341795
void endParenthesizedExpression(Token token) {
17351796
listener?.endParenthesizedExpression(token);
17361797
}
17371798

1799+
@override
1800+
void handleParenthesizedPattern(Token token) {
1801+
listener?.handleParenthesizedPattern(token);
1802+
}
1803+
1804+
@override
1805+
void handleConstantPattern(Token? constKeyword) {
1806+
listener?.handleConstantPattern(constKeyword);
1807+
}
1808+
1809+
@override
1810+
void handleExtractorPattern(
1811+
Token firstIdentifier, Token? dot, Token? secondIdentifier) {
1812+
listener?.handleExtractorPattern(firstIdentifier, dot, secondIdentifier);
1813+
}
1814+
17381815
@override
17391816
void handleQualified(Token period) {
17401817
listener?.handleQualified(period);
@@ -1839,6 +1916,11 @@ class ForwardingListener implements Listener {
18391916
listener?.handleUnaryPrefixExpression(token);
18401917
}
18411918

1919+
@override
1920+
void handleRelationalPattern(Token token) {
1921+
listener?.handleRelationalPattern(token);
1922+
}
1923+
18421924
@override
18431925
void handleUnescapeError(
18441926
Message message, Token location, int offset, int length) {

pkg/_fe_analyzer_shared/lib/src/parser/listener.dart

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ class Listener implements UnescapeErrorListener {
5252
logEvent("Arguments");
5353
}
5454

55+
/// Called after the parser has consumed a sequence of patternFields that
56+
/// forms the arguments to an extractorPattern
57+
void handleExtractorPatternFields(
58+
int count, Token beginToken, Token endToken) {
59+
logEvent("ExtractorPatternFields");
60+
}
61+
5562
/// Handle async modifiers `async`, `async*`, `sync`.
5663
void handleAsyncModifier(Token? asyncToken, Token? starToken) {
5764
logEvent("AsyncModifier");
@@ -976,6 +983,12 @@ class Listener implements UnescapeErrorListener {
976983
logEvent("LiteralMapEntry");
977984
}
978985

986+
/// Called after the parser has consumed a mapPatternEntry, consisting of an
987+
/// expression, a colon, and a pattern.
988+
void handleMapPatternEntry(Token colon, Token endToken) {
989+
logEvent("MapPatternEntry");
990+
}
991+
979992
void beginLiteralString(Token token) {}
980993

981994
void handleInterpolationExpression(Token leftBracket, Token? rightBracket) {}
@@ -1371,6 +1384,25 @@ class Listener implements UnescapeErrorListener {
13711384
logEvent("NonNullAssertExpression");
13721385
}
13731386

1387+
/// Called after the parser has consumed a null-assert pattern, consisting of
1388+
/// a pattern followed by a `!` operator.
1389+
void handleNullAssertPattern(Token bang) {
1390+
logEvent("NullAssertPattern");
1391+
}
1392+
1393+
/// Called after the parser has consumed a null-check pattern, consisting of a
1394+
/// pattern followed by a `?` operator.
1395+
void handleNullCheckPattern(Token question) {
1396+
logEvent('NullCheckPattern');
1397+
}
1398+
1399+
/// Called after the parser has consumed a variable pattern, consisting of an
1400+
/// optional `var` or `final` keyword, an optional type annotation, and a
1401+
/// variable name identifier.
1402+
void handleVariablePattern(Token? keyword, Token variable) {
1403+
logEvent('VariablePattern');
1404+
}
1405+
13741406
void handleNoName(Token token) {
13751407
logEvent("NoName");
13761408
}
@@ -1518,6 +1550,12 @@ class Listener implements UnescapeErrorListener {
15181550
logEvent("AsOperator");
15191551
}
15201552

1553+
/// Called after the parser has consumed a cast pattern, consisting of a
1554+
/// pattern, `as` operator, and type annotation.
1555+
void handleCastPattern(Token operator) {
1556+
logEvent('CastPattern');
1557+
}
1558+
15211559
void handleAssignmentExpression(Token token) {
15221560
logEvent("AssignmentExpression");
15231561
}
@@ -1532,6 +1570,15 @@ class Listener implements UnescapeErrorListener {
15321570
logEvent("BinaryExpression");
15331571
}
15341572

1573+
/// Called when the parser has consumed the operator of a binary pattern.
1574+
void beginBinaryPattern(Token token) {}
1575+
1576+
/// Called when the parser has consumed a binary pattern, consisting of a LHS
1577+
/// pattern, `&` or `|` operator, and a RHS pattern.
1578+
void endBinaryPattern(Token token) {
1579+
logEvent("BinaryPattern");
1580+
}
1581+
15351582
/// Called for `.`, `?.` and `..`.
15361583
void handleEndingBinaryExpression(Token token) {
15371584
// TODO(jensj): push implementation into subclasses
@@ -1706,6 +1753,12 @@ class Listener implements UnescapeErrorListener {
17061753
logEvent("LiteralList");
17071754
}
17081755

1756+
/// Called after the parser has consumed a list pattern, consisting of a `[`,
1757+
/// a comma-separated sequence of patterns, and a `]`.
1758+
void handleListPattern(int count, Token leftBracket, Token rightBracket) {
1759+
logEvent("ListPattern");
1760+
}
1761+
17091762
void handleLiteralSetOrMap(
17101763
int count,
17111764
Token leftBrace,
@@ -1718,6 +1771,12 @@ class Listener implements UnescapeErrorListener {
17181771
logEvent('LiteralSetOrMap');
17191772
}
17201773

1774+
/// Called after the parser has consumed a map pattern, consisting of a `{`,
1775+
/// a comma-separated sequence of mapPatternEntry, and a `}`.
1776+
void handleMapPattern(int count, Token leftBrace, Token rightBrace) {
1777+
logEvent('MapPattern');
1778+
}
1779+
17211780
void handleLiteralNull(Token token) {
17221781
logEvent("LiteralNull");
17231782
}
@@ -1730,6 +1789,12 @@ class Listener implements UnescapeErrorListener {
17301789
logEvent("NamedArgument");
17311790
}
17321791

1792+
/// Called after the parser has consumed a patternField, consisting of an
1793+
/// optional identifier, optional `:`, and a pattern.
1794+
void handlePatternField(Token? colon) {
1795+
logEvent("PatternField");
1796+
}
1797+
17331798
void handleNamedRecordField(Token colon) {
17341799
logEvent("NamedRecordField");
17351800
}
@@ -1784,7 +1849,7 @@ class Listener implements UnescapeErrorListener {
17841849
/// - do while loop
17851850
/// - switch statement
17861851
/// - while loop
1787-
void handleParenthesizedCondition(Token token, [Token? case_]) {
1852+
void handleParenthesizedCondition(Token token, Token? case_) {
17881853
logEvent("ParenthesizedCondition");
17891854
}
17901855

@@ -1797,13 +1862,40 @@ class Listener implements UnescapeErrorListener {
17971862
logEvent("RecordLiteral");
17981863
}
17991864

1865+
/// Called after the parser has consumed a record pattern, consisting of a
1866+
/// `(`, a comma-separated sequence of patternFields, and a `)`.
1867+
void handleRecordPattern(Token token, int count) {
1868+
logEvent("RecordPattern");
1869+
}
1870+
18001871
/// End a parenthesized expression.
18011872
/// These may be within the condition expression of a control structure
18021873
/// but will not be the condition of a control structure.
18031874
void endParenthesizedExpression(Token token) {
18041875
logEvent("ParenthesizedExpression");
18051876
}
18061877

1878+
/// Called after the parser has consumed a parenthesized pattern, consisting
1879+
/// of a `(`, a pattern, and a `)`.
1880+
void handleParenthesizedPattern(Token token) {
1881+
logEvent("ParenthesizedPattern");
1882+
}
1883+
1884+
/// Called after the parser has consumed a constant pattern, consisting of an
1885+
/// optional `const` and an expression.
1886+
void handleConstantPattern(Token? constKeyword) {
1887+
logEvent("ConstantPattern");
1888+
}
1889+
1890+
/// Called after the parser has consumed an extractor pattern, consisting of
1891+
/// an identifier, optional dot and second identifier, optional type
1892+
/// arguments, and a parenthesized list of extractor pattern fields (see
1893+
/// [handleExtractorPatternFields]).
1894+
void handleExtractorPattern(
1895+
Token firstIdentifier, Token? dot, Token? secondIdentifier) {
1896+
logEvent("ExtractorPattern");
1897+
}
1898+
18071899
/// Handle a construct of the form "identifier.identifier" occurring in a part
18081900
/// of the grammar where expressions in general are not allowed.
18091901
/// Substructures:
@@ -1851,6 +1943,12 @@ class Listener implements UnescapeErrorListener {
18511943
logEvent("UnaryPrefixExpression");
18521944
}
18531945

1946+
/// Called after the parser has consumed a relational pattern, consisting of
1947+
/// an equality operator or relational operator, followed by an expression.
1948+
void handleRelationalPattern(Token token) {
1949+
logEvent("RelationalPattern");
1950+
}
1951+
18541952
void handleUnaryPrefixAssignmentExpression(Token token) {
18551953
logEvent("UnaryPrefixAssignmentExpression");
18561954
}

pkg/_fe_analyzer_shared/lib/src/parser/literal_entry_info_impl.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ class IfCondition extends LiteralEntryInfo {
144144
final Token ifToken = token.next!;
145145
assert(optional('if', ifToken));
146146
parser.listener.beginIfControlFlow(ifToken);
147-
Token result = parser.ensureParenthesizedCondition(ifToken);
147+
Token result = parser.ensureParenthesizedCondition(ifToken,
148+
allowCase: parser.allowPatterns);
148149
parser.listener.handleThenControlFlow(result);
149150
return result;
150151
}

0 commit comments

Comments
 (0)