add scanner/parser support for >>>=
Change-Id: I8bbf19e70f35aba600cb768fc3c7537f2aa604e9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97602
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index f90130b..14663b1 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -56,42 +56,12 @@
class ClassMemberParserTest_Fasta extends FastaParserTestCase
with ClassMemberParserTestMixin {
void test_parseClassMember_operator_gtgtgt() {
- final sourceText = 'class C { bool operator >>>(other) => false; }';
-
- // ---------------------------------------------------
- // TODO(danrubel): Replace this section with a call to parseCompilationUnit
- // once '>>>' token support is enabled permanently.
-
- var source = new StringSource(sourceText, 'parser_test_StringSource.dart');
- GatheringErrorListener errorListener =
- new GatheringErrorListener(checkRanges: true);
-
- // Scan tokens
- StringScanner scanner = new StringScanner(sourceText, includeComments: true)
- ..enableGtGtGt = true;
- Token tokens = scanner.tokenize();
- expect(scanner.hasErrors, isFalse);
-
- // Run parser
- ErrorReporter errorReporter = new ErrorReporter(errorListener, source);
- fasta.Parser parser = new fasta.Parser(null);
- AstBuilder astBuilder = new AstBuilder(errorReporter, source.uri, true);
- parser.listener = astBuilder;
- astBuilder.parser = parser;
- parser.parseUnit(tokens);
-
- CompilationUnitImpl unit = astBuilder.pop();
- expect(unit, isNotNull);
- unit.localDeclarations = astBuilder.localDeclarations;
- errorListener.assertNoErrors();
-
- // ---------------------------------------------------
-
+ CompilationUnitImpl unit = parseCompilationUnit(
+ 'class C { bool operator >>>(other) => false; }',
+ enableGtGtGt: true);
ClassDeclaration declaration = unit.declarations[0];
- ClassMember member = declaration.members[0];
- expect(member, isNotNull);
- expect(member, new TypeMatcher<MethodDeclaration>());
- MethodDeclaration method = member;
+ MethodDeclaration method = declaration.members[0];
+
expect(method.documentationComment, isNull);
expect(method.externalKeyword, isNull);
expect(method.modifierKeyword, isNull);
@@ -103,6 +73,24 @@
expect(method.parameters, isNotNull);
expect(method.body, isNotNull);
}
+
+ void test_parseClassMember_operator_gtgtgteq() {
+ CompilationUnitImpl unit = parseCompilationUnit(
+ 'class C { foo(int value) { x >>>= value; } }',
+ enableGtGtGtEq: true);
+ ClassDeclaration declaration = unit.declarations[0];
+ MethodDeclaration method = declaration.members[0];
+ BlockFunctionBody blockFunctionBody = method.body;
+ NodeList<Statement> statements = blockFunctionBody.block.statements;
+ expect(statements, hasLength(1));
+ ExpressionStatement statement = statements[0];
+ AssignmentExpression assignment = statement.expression;
+ SimpleIdentifier leftHandSide = assignment.leftHandSide;
+ expect(leftHandSide.name, 'x');
+ expect(assignment.operator.lexeme, '>>>=');
+ SimpleIdentifier rightHandSide = assignment.rightHandSide;
+ expect(rightHandSide.name, 'value');
+ }
}
/**
@@ -1406,12 +1394,16 @@
CompilationUnit parseCompilationUnit(String content,
{List<ErrorCode> codes,
List<ExpectedError> errors,
- bool enableControlFlowCollections}) {
+ bool enableControlFlowCollections,
+ bool enableGtGtGt,
+ bool enableGtGtGtEq}) {
GatheringErrorListener listener =
new GatheringErrorListener(checkRanges: true);
CompilationUnit unit = parseCompilationUnit2(content, listener,
- enableControlFlowCollections: enableControlFlowCollections);
+ enableControlFlowCollections: enableControlFlowCollections,
+ enableGtGtGt: enableGtGtGt,
+ enableGtGtGtEq: enableGtGtGtEq);
// Assert and return result
if (codes != null) {
@@ -1427,7 +1419,9 @@
CompilationUnit parseCompilationUnit2(
String content, GatheringErrorListener listener,
- {bool enableControlFlowCollections}) {
+ {bool enableControlFlowCollections,
+ bool enableGtGtGt,
+ bool enableGtGtGtEq}) {
var source = new StringSource(content, 'parser_test_StringSource.dart');
void reportError(
@@ -1437,7 +1431,10 @@
}
// Scan tokens
- ScannerResult result = scanString(content, includeComments: true);
+ ScannerResult result = scanString(content,
+ includeComments: true,
+ enableGtGtGt: enableGtGtGt ?? enableGtGtGtEq ?? false,
+ enableGtGtGtEq: enableGtGtGtEq ?? false);
Token token = result.tokens;
if (result.hasErrors) {
// The default recovery strategy used by scanString
diff --git a/pkg/front_end/lib/src/fasta/parser/type_info.dart b/pkg/front_end/lib/src/fasta/parser/type_info.dart
index 8214825..cf59846 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart
@@ -34,26 +34,26 @@
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the type, inserting a synthetic type reference if
/// necessary. This may modify the token stream when parsing `>>` or `>>>`
- /// in valid code or during recovery.
+ /// or `>>>=` in valid code or during recovery.
Token ensureTypeNotVoid(Token token, Parser parser);
/// Call this function when the token after [token] must be a type or void.
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the type, inserting a synthetic type reference if
/// necessary. This may modify the token stream when parsing `>>` or `>>>`
- /// in valid code or during recovery.
+ /// or `>>>=` in valid code or during recovery.
Token ensureTypeOrVoid(Token token, Parser parser);
/// Call this function to parse an optional type (not void) after [token].
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the type. This may modify the token stream
- /// when parsing `>>` or `>>>` in valid code or during recovery.
+ /// when parsing `>>` or `>>>` or `>>>=` in valid code or during recovery.
Token parseTypeNotVoid(Token token, Parser parser);
/// Call this function to parse an optional type or void after [token].
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the type. This may modify the token stream
- /// when parsing `>>` or `>>>` in valid code or during recovery.
+ /// when parsing `>>` or `>>>` or `>>>=` in valid code or during recovery.
Token parseType(Token token, Parser parser);
/// Call this function with the [token] before the type to obtain
@@ -83,14 +83,14 @@
/// Call this function to parse optional type arguments after [token].
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the arguments. This may modify the token stream
- /// when parsing `>>` or `>>>` in valid code or during recovery.
+ /// when parsing `>>` or `>>>` or `>>>=` in valid code or during recovery.
Token parseArguments(Token token, Parser parser);
/// Call this function to parse optional type parameters
/// (also known as type variables) after [token].
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the parameters. This may modify the token stream
- /// when parsing `>>` or `>>>` in valid code or during recovery.
+ /// when parsing `>>` or `>>>` or `>>>=` in valid code or during recovery.
Token parseVariables(Token token, Parser parser);
/// Call this function with the [token] before the type var to obtain
diff --git a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
index 6d670a1..b6d698e 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
@@ -31,6 +31,7 @@
splitGtEq,
splitGtFromGtGtEq,
splitGtFromGtGtGt,
+ splitGtFromGtGtGtEq,
splitGtGt,
syntheticGt;
@@ -1163,17 +1164,18 @@
}
}
-/// Return `true` if [token] is one of `>`, `>>`, `>=`, `>>>`, or `>>=`.
+/// Return `true` if [token] is one of `>`, `>>`, `>>>`, `>=`, `>>=`, or `>>>=`.
bool isCloser(Token token) {
final value = token.stringValue;
return identical(value, '>') ||
identical(value, '>>') ||
identical(value, '>=') ||
identical(value, '>>>') ||
- identical(value, '>>=');
+ identical(value, '>>=') ||
+ identical(value, '>>>=');
}
-/// If [beforeCloser].next is one of `>`, `>>`, `>=`, `>>>`, or `>>=`,
+/// If [beforeCloser].next is one of `>`, `>>`, `>>>`, `>=`, `>>=`, or `>>>=`
/// then update the token stream and return `true`.
bool parseCloser(Token beforeCloser) {
Token unsplit = beforeCloser.next;
@@ -1189,8 +1191,8 @@
}
/// If [closer] is `>` then return it.
-/// If [closer] is one of `>>`, `>=`, `>>>`, or `>>=` then split then token
-/// and return the leading `>` without updating the token stream.
+/// If [closer] is one of `>>`, `>>>`, `>=`, `>>=`, or `>>>=` then split
+/// the token and return the leading `>` without updating the token stream.
/// If [closer] is none of the above, then return null;
Token splitCloser(Token closer) {
String value = closer.stringValue;
@@ -1204,6 +1206,8 @@
return splitGtFromGtGtGt(closer);
} else if (identical(value, '>>=')) {
return splitGtFromGtGtEq(closer);
+ } else if (identical(value, '>>>=')) {
+ return splitGtFromGtGtGtEq(closer);
}
return null;
}
diff --git a/pkg/front_end/lib/src/fasta/parser/util.dart b/pkg/front_end/lib/src/fasta/parser/util.dart
index e02385f..2e7b441 100644
--- a/pkg/front_end/lib/src/fasta/parser/util.dart
+++ b/pkg/front_end/lib/src/fasta/parser/util.dart
@@ -190,6 +190,18 @@
..next = token.next);
}
+/// Split `>>>=` into two separate tokens... `>` followed by `>>=`.
+/// Call [Token.setNext] to add the token to the stream.
+Token splitGtFromGtGtGtEq(Token token) {
+ assert(optional('>>>=', token));
+ return new SimpleToken(
+ TokenType.GT, token.charOffset, token.precedingComments)
+ ..setNext(new SimpleToken(TokenType.GT_GT_EQ, token.charOffset + 1)
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = token.next);
+}
+
/// Return a synthetic `<` followed by [next].
/// Call [Token.setNext] to add the token to the stream.
Token syntheticGt(Token next) {
diff --git a/pkg/front_end/lib/src/fasta/scanner.dart b/pkg/front_end/lib/src/fasta/scanner.dart
index bcd395a..8f8e495 100644
--- a/pkg/front_end/lib/src/fasta/scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner.dart
@@ -71,6 +71,7 @@
/// If [recover] is null, then the [defaultRecoveryStrategy] is used.
ScannerResult scanString(String source,
{bool enableGtGtGt: false,
+ bool enableGtGtGtEq: false,
bool includeComments: false,
bool scanLazyAssignmentOperators: false,
Recover recover}) {
@@ -80,6 +81,7 @@
StringScanner scanner =
new StringScanner(source, includeComments: includeComments);
scanner.enableGtGtGt = enableGtGtGt;
+ scanner.enableGtGtGtEq = enableGtGtGtEq;
return _tokenizeAndRecover(scanner, recover, source: source);
}
diff --git a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
index bbda63f..ddbf88a 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -43,11 +43,16 @@
final bool includeComments;
- /// Experimental flag for enabling parsing of `>>>`.
+ /// Experimental flag for enabling scanning of `>>>`.
/// See https://github.com/dart-lang/language/issues/61
/// and https://github.com/dart-lang/language/issues/60
bool enableGtGtGt = false;
+ /// Experimental flag for enabling scanning of `>>>=`.
+ /// See https://github.com/dart-lang/language/issues/61
+ /// and https://github.com/dart-lang/language/issues/60
+ bool enableGtGtGtEq = false;
+
/**
* The string offset for the next token that will be created.
*
@@ -642,7 +647,7 @@
}
int tokenizeGreaterThan(int next) {
- // > >= >> >>= >>>
+ // > >= >> >>= >>> >>>=
next = advance();
if (identical($EQ, next)) {
appendPrecedenceToken(TokenType.GT_EQ);
@@ -653,8 +658,13 @@
appendPrecedenceToken(TokenType.GT_GT_EQ);
return advance();
} else if (enableGtGtGt && identical($GT, next)) {
+ next = advance();
+ if (enableGtGtGtEq && identical($EQ, next)) {
+ appendPrecedenceToken(TokenType.GT_GT_GT_EQ);
+ return advance();
+ }
appendPrecedenceToken(TokenType.GT_GT_GT);
- return advance();
+ return next;
} else {
appendGtGt(TokenType.GT_GT);
return next;
diff --git a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
index f29765c..fff742e 100644
--- a/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/token_constants.dart
@@ -88,3 +88,4 @@
const int GENERIC_METHOD_TYPE_LIST_TOKEN = GENERIC_METHOD_TYPE_ASSIGN_TOKEN + 1;
const int GT_GT_GT_TOKEN = GENERIC_METHOD_TYPE_LIST_TOKEN + 1;
const int PERIOD_PERIOD_PERIOD_QUESTION_TOKEN = GT_GT_GT_TOKEN + 1;
+const int GT_GT_GT_EQ_TOKEN = PERIOD_PERIOD_PERIOD_QUESTION_TOKEN + 1;
diff --git a/pkg/front_end/lib/src/scanner/token.dart b/pkg/front_end/lib/src/scanner/token.dart
index 5a78cd2..62718b0 100644
--- a/pkg/front_end/lib/src/scanner/token.dart
+++ b/pkg/front_end/lib/src/scanner/token.dart
@@ -1279,6 +1279,10 @@
'>>>', 'GT_GT_GT', SHIFT_PRECEDENCE, GT_GT_GT_TOKEN,
isOperator: true, isUserDefinableOperator: true);
+ static const TokenType GT_GT_GT_EQ = const TokenType(
+ '>>>=', 'GT_GT_GT_EQ', ASSIGNMENT_PRECEDENCE, GT_GT_GT_EQ_TOKEN,
+ isOperator: true);
+
static const TokenType HASH =
const TokenType('#', 'HASH', NO_PRECEDENCE, HASH_TOKEN);
diff --git a/pkg/front_end/test/fasta/parser/type_info_test.dart b/pkg/front_end/test/fasta/parser/type_info_test.dart
index 531df46..5c37a69 100644
--- a/pkg/front_end/test/fasta/parser/type_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/type_info_test.dart
@@ -45,7 +45,8 @@
assert(source != null, 'source must not be null');
StringScanner scanner =
new StringScanner(source, includeComments: includeComments)
- ..enableGtGtGt = true;
+ ..enableGtGtGt = true
+ ..enableGtGtGtEq = true;
return _tokenizeAndRecover(scanner, recover, source: source);
}
@@ -1806,6 +1807,45 @@
'handleType S null',
'endTypeArguments 1 < >'
]);
+ expectComplexTypeArg('<S<T<U>>>=',
+ typeArgumentCount: 1,
+ expectedAfter: '=',
+ expectedCalls: [
+ 'beginTypeArguments <',
+ 'handleIdentifier S typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier T typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier U typeReference',
+ 'handleNoTypeArguments >>>=',
+ 'handleType U null',
+ 'endTypeArguments 1 < >',
+ 'handleType T null',
+ 'endTypeArguments 1 < >',
+ 'handleType S null',
+ 'endTypeArguments 1 < >'
+ ]);
+ expectComplexTypeArg('<S<T<U,V>>>=',
+ typeArgumentCount: 1,
+ expectedAfter: '=',
+ expectedCalls: [
+ 'beginTypeArguments <',
+ 'handleIdentifier S typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier T typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier U typeReference',
+ 'handleNoTypeArguments ,',
+ 'handleType U null',
+ 'handleIdentifier V typeReference',
+ 'handleNoTypeArguments >>>=',
+ 'handleType V null',
+ 'endTypeArguments 2 < >',
+ 'handleType T null',
+ 'endTypeArguments 1 < >',
+ 'handleType S null',
+ 'endTypeArguments 1 < >'
+ ]);
expectComplexTypeArg('<S<Function()>>',
typeArgumentCount: 1,
expectedCalls: [
@@ -1875,6 +1915,27 @@
'handleType S null',
'endTypeArguments 1 < >'
]);
+ expectComplexTypeArg('<S<T<void Function()>>>=',
+ typeArgumentCount: 1,
+ expectedAfter: '=',
+ expectedCalls: [
+ 'beginTypeArguments <',
+ 'handleIdentifier S typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier T typeReference',
+ 'beginTypeArguments <',
+ 'handleNoTypeVariables (',
+ 'beginFunctionType void', // was 'beginFunctionType Function'
+ 'handleVoidKeyword void', // was 'handleNoType <'
+ 'beginFormalParameters ( MemberKind.GeneralizedFunctionType',
+ 'endFormalParameters 0 ( ) MemberKind.GeneralizedFunctionType',
+ 'endFunctionType Function null',
+ 'endTypeArguments 1 < >',
+ 'handleType T null',
+ 'endTypeArguments 1 < >',
+ 'handleType S null',
+ 'endTypeArguments 1 < >'
+ ]);
}
void test_computeTypeArg_complex_recovery() {
@@ -2362,6 +2423,33 @@
'endTypeVariable > 0 extends',
'endTypeVariables < >'
]);
+ expectComplexTypeParam('<T extends List<Map<S, T>>>=',
+ typeArgumentCount: 1,
+ expectedAfter: '=',
+ expectedCalls: [
+ 'beginTypeVariables <',
+ 'beginMetadataStar T',
+ 'endMetadataStar 0',
+ 'handleIdentifier T typeVariableDeclaration',
+ 'beginTypeVariable T',
+ 'handleTypeVariablesDefined > 1',
+ 'handleIdentifier List typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier Map typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier S typeReference',
+ 'handleNoTypeArguments ,',
+ 'handleType S null',
+ 'handleIdentifier T typeReference',
+ 'handleNoTypeArguments >>>=',
+ 'handleType T null',
+ 'endTypeArguments 2 < >',
+ 'handleType Map null',
+ 'endTypeArguments 1 < >',
+ 'handleType List null',
+ 'endTypeVariable >= 0 extends',
+ 'endTypeVariables < >'
+ ]);
}
void test_computeTypeParam_34850() {