mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
9d0bca98e9
Previously, we boosted the precedence of `as` inside patterns, to make it higher than `|` and `&`. Now, we reduce the precedence of `|` and `&` to match that of `||` and `&&`. The new approach should make it easier to transition to using `||` and `&&` in patterns. Bug: https://github.com/dart-lang/language/issues/2501 Change-Id: I6658484860ef809bc9029fa75747fdd7ce986836 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272700 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
416 lines
11 KiB
Dart
416 lines
11 KiB
Dart
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
|
|
import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart'
|
|
show AbstractScanner;
|
|
import 'package:_fe_analyzer_shared/src/scanner/token_impl.dart' as fasta;
|
|
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(PrecedenceInfoTest);
|
|
});
|
|
}
|
|
|
|
/// Assert that fasta PrecedenceInfo implements analyzer TokenType.
|
|
@reflectiveTest
|
|
class PrecedenceInfoTest {
|
|
void assertInfo(check(String source, Token token)) {
|
|
void assertLexeme(String? source) {
|
|
if (source == null || source.isEmpty) return;
|
|
var token = scanString(source, includeComments: true).tokens;
|
|
while (token is ErrorToken) {
|
|
token = token.next!;
|
|
}
|
|
check(source, token);
|
|
}
|
|
|
|
for (TokenType type in TokenType.all) {
|
|
assertLexeme(type.lexeme);
|
|
}
|
|
assertLexeme('1.0'); // DOUBLE
|
|
assertLexeme('0xA'); // HEXADECIMAL
|
|
assertLexeme('1'); // INT
|
|
assertLexeme('var'); // KEYWORD
|
|
assertLexeme('#!/'); // SCRIPT_TAG
|
|
assertLexeme('"foo"'); // STRING
|
|
assertLexeme('bar'); // IDENTIFIER
|
|
if (AbstractScanner.LAZY_ASSIGNMENT_ENABLED) {
|
|
assertLexeme('&&=');
|
|
assertLexeme('||=');
|
|
}
|
|
}
|
|
|
|
void test_isOperator() {
|
|
var operatorLexemes = new Set<String>.from(const [
|
|
'&',
|
|
'&&',
|
|
'&&=',
|
|
'&=',
|
|
'!',
|
|
'!=',
|
|
'|',
|
|
'||',
|
|
'||=',
|
|
'|=',
|
|
'^',
|
|
'^=',
|
|
'=',
|
|
'==',
|
|
'>',
|
|
'>=',
|
|
'>>',
|
|
'>>=',
|
|
'[]',
|
|
'[]=',
|
|
'<',
|
|
'<=',
|
|
'<<',
|
|
'<<=',
|
|
'-',
|
|
'-=',
|
|
'--',
|
|
'%',
|
|
'%=',
|
|
'..',
|
|
'+',
|
|
'+=',
|
|
'++',
|
|
'?',
|
|
'?.',
|
|
'??',
|
|
'??=',
|
|
'/',
|
|
'/=',
|
|
'*',
|
|
'*=',
|
|
'~',
|
|
'~/',
|
|
'~/=',
|
|
]);
|
|
|
|
assertInfo((String source, Token token) {
|
|
expect(token.isOperator, operatorLexemes.contains(source),
|
|
reason: source);
|
|
expect(token.type.isOperator, operatorLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isAdditiveOperator() {
|
|
var additiveLexemes = [
|
|
'-',
|
|
'+',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isAdditiveOperator, additiveLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isAssignmentOperator() {
|
|
const assignmentLexemes = const [
|
|
'&=',
|
|
'&&=',
|
|
'|=',
|
|
'||=',
|
|
'^=',
|
|
'=',
|
|
'>>=',
|
|
'<<=',
|
|
'-=',
|
|
'%=',
|
|
'+=',
|
|
'??=',
|
|
'/=',
|
|
'*=',
|
|
'~/=',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(
|
|
token.type.isAssignmentOperator, assignmentLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isAssociativeOperator() {
|
|
const associativeLexemes = const [
|
|
'&',
|
|
'&&',
|
|
'|',
|
|
'||',
|
|
'^',
|
|
'+',
|
|
'*',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(
|
|
token.type.isAssociativeOperator, associativeLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isEqualityOperator() {
|
|
const equalityLexemes = const [
|
|
'!=',
|
|
'==',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isEqualityOperator, equalityLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isIncrementOperator() {
|
|
const incrementLexemes = const [
|
|
'--',
|
|
'++',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isIncrementOperator, incrementLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isMultiplicativeOperator() {
|
|
const multiplicativeLexemes = const [
|
|
'%',
|
|
'/',
|
|
'*',
|
|
'~/',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isMultiplicativeOperator,
|
|
multiplicativeLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isRelationalOperator() {
|
|
const relationalLexemes = const [
|
|
'>',
|
|
'>=',
|
|
'<',
|
|
'<=',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(
|
|
token.type.isRelationalOperator, relationalLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isShiftOperator() {
|
|
const shiftLexemes = const [
|
|
'>>',
|
|
'<<',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isShiftOperator, shiftLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isUnaryPostfixOperator() {
|
|
const unaryPostfixLexemes = const [
|
|
'--',
|
|
'++',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isUnaryPostfixOperator,
|
|
unaryPostfixLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isUnaryPrefixOperator() {
|
|
const unaryPrefixLexemes = const [
|
|
'!',
|
|
'-',
|
|
'--',
|
|
'++',
|
|
'~',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(
|
|
token.type.isUnaryPrefixOperator, unaryPrefixLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isSelectorOperator() {
|
|
const selectorLexemes = const [
|
|
'(',
|
|
'[',
|
|
'.',
|
|
'?.',
|
|
'[]',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
expect(token.type.isSelectorOperator, selectorLexemes.contains(source),
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_isUserDefinableOperator() {
|
|
const userDefinableOperatorLexemes = const [
|
|
'&',
|
|
'|',
|
|
'^',
|
|
'==',
|
|
'>',
|
|
'>=',
|
|
'>>',
|
|
'[]',
|
|
'[]=',
|
|
'<',
|
|
'<=',
|
|
'<<',
|
|
'-',
|
|
'%',
|
|
'+',
|
|
'/',
|
|
'*',
|
|
'~',
|
|
'~/',
|
|
];
|
|
assertInfo((String source, Token token) {
|
|
var userDefinable = userDefinableOperatorLexemes.contains(source);
|
|
expect(token.type.isUserDefinableOperator, userDefinable, reason: source);
|
|
expect(token.isUserDefinableOperator, userDefinable, reason: source);
|
|
expect(fasta.isUserDefinableOperator(token.lexeme), userDefinable,
|
|
reason: source);
|
|
});
|
|
}
|
|
|
|
void test_name() {
|
|
void assertName(String? source, String name, {int offset = 0}) {
|
|
if (source == null || source.isEmpty) return;
|
|
var token = scanString(source, includeComments: true).tokens;
|
|
while (token is ErrorToken || token.offset < offset) {
|
|
token = token.next!;
|
|
}
|
|
expect(token.type.name, name,
|
|
reason: 'source: $source\ntoken: ${token.lexeme}');
|
|
}
|
|
|
|
assertName('&', 'AMPERSAND');
|
|
assertName('&&', 'AMPERSAND_AMPERSAND');
|
|
assertName('&=', 'AMPERSAND_EQ');
|
|
assertName('@', 'AT');
|
|
assertName('!', 'BANG');
|
|
assertName('!=', 'BANG_EQ');
|
|
assertName('|', 'BAR');
|
|
assertName('||', 'BAR_BAR');
|
|
assertName('|=', 'BAR_EQ');
|
|
assertName(':', 'COLON');
|
|
assertName(',', 'COMMA');
|
|
assertName('^', 'CARET');
|
|
assertName('^=', 'CARET_EQ');
|
|
assertName('}', 'CLOSE_CURLY_BRACKET');
|
|
assertName(')', 'CLOSE_PAREN');
|
|
assertName(']', 'CLOSE_SQUARE_BRACKET');
|
|
assertName('=', 'EQ');
|
|
assertName('==', 'EQ_EQ');
|
|
assertName('=>', 'FUNCTION');
|
|
assertName('>', 'GT');
|
|
assertName('>=', 'GT_EQ');
|
|
assertName('>>', 'GT_GT');
|
|
assertName('>>=', 'GT_GT_EQ');
|
|
assertName('#', 'HASH');
|
|
assertName('[]', 'INDEX');
|
|
assertName('[]=', 'INDEX_EQ');
|
|
assertName('<', 'LT');
|
|
assertName('<=', 'LT_EQ');
|
|
assertName('<<', 'LT_LT');
|
|
assertName('<<=', 'LT_LT_EQ');
|
|
assertName('-', 'MINUS');
|
|
assertName('-=', 'MINUS_EQ');
|
|
assertName('--', 'MINUS_MINUS');
|
|
assertName('{', 'OPEN_CURLY_BRACKET');
|
|
assertName('(', 'OPEN_PAREN');
|
|
assertName('[', 'OPEN_SQUARE_BRACKET');
|
|
assertName('%', 'PERCENT');
|
|
assertName('%=', 'PERCENT_EQ');
|
|
assertName('.', 'PERIOD');
|
|
assertName('..', 'PERIOD_PERIOD');
|
|
assertName('+', 'PLUS');
|
|
assertName('+=', 'PLUS_EQ');
|
|
assertName('++', 'PLUS_PLUS');
|
|
assertName('?', 'QUESTION');
|
|
assertName('?.', 'QUESTION_PERIOD');
|
|
assertName('??', 'QUESTION_QUESTION');
|
|
assertName('??=', 'QUESTION_QUESTION_EQ');
|
|
assertName(';', 'SEMICOLON');
|
|
assertName('/', 'SLASH');
|
|
assertName('/=', 'SLASH_EQ');
|
|
assertName('*', 'STAR');
|
|
assertName('*=', 'STAR_EQ');
|
|
assertName('"\${', 'STRING_INTERPOLATION_EXPRESSION', offset: 1);
|
|
assertName('"\$', 'STRING_INTERPOLATION_IDENTIFIER', offset: 1);
|
|
assertName('~', 'TILDE');
|
|
assertName('~/', 'TILDE_SLASH');
|
|
assertName('~/=', 'TILDE_SLASH_EQ');
|
|
assertName('`', 'BACKPING');
|
|
assertName('\\', 'BACKSLASH');
|
|
assertName('...', 'PERIOD_PERIOD_PERIOD');
|
|
assertName('...?', 'PERIOD_PERIOD_PERIOD_QUESTION');
|
|
}
|
|
|
|
/// Assert precedence as per the Dart language spec
|
|
///
|
|
/// Prefix "++" and "--" are excluded from the prefix (15) list
|
|
/// because they are interpreted as being in the postfix (16) list.
|
|
/// Leading "-" is excluded from the precedence 15 list
|
|
/// because it is interpreted as a minus token (precedence 13).
|
|
void test_precedence() {
|
|
const precedenceTable = const <int, List<String>>{
|
|
17: const <String>['.', '?.', '[', '('],
|
|
16: const <String>['++', '--'],
|
|
15: const <String>['!', '~'], // excluded '-', '++', '--'
|
|
14: const <String>['*', '/', '~/', '%'],
|
|
13: const <String>['+', '-'],
|
|
12: const <String>['<<', '>>'],
|
|
11: const <String>['&'],
|
|
10: const <String>['^'],
|
|
9: const <String>['|'],
|
|
8: const <String>['<', '>', '<=', '>=', 'as', 'is', 'is!'],
|
|
7: const <String>['==', '!='],
|
|
6: const <String>['&&'],
|
|
5: const <String>['||'],
|
|
4: const <String>['??'],
|
|
3: const <String>['? :'],
|
|
2: const <String>['..'],
|
|
1: const <String>['=', '*=', '/=', '+=', '-=', '&=', '^='],
|
|
};
|
|
precedenceTable.forEach((precedence, lexemes) {
|
|
for (String source in lexemes) {
|
|
var token = scanString(source, includeComments: true).tokens;
|
|
while (token is ErrorToken) {
|
|
token = token.next!;
|
|
}
|
|
expect(token.type.precedence, precedence, reason: source);
|
|
}
|
|
});
|
|
}
|
|
|
|
void test_type() {
|
|
void assertLexeme(String source, TokenType tt) {
|
|
var token = scanString(source, includeComments: true).tokens;
|
|
expect(token.type, same(tt), reason: source);
|
|
}
|
|
|
|
assertLexeme('1.0', TokenType.DOUBLE);
|
|
assertLexeme('0xA', TokenType.HEXADECIMAL);
|
|
assertLexeme('1', TokenType.INT);
|
|
assertLexeme('var', Keyword.VAR);
|
|
assertLexeme('#!/', TokenType.SCRIPT_TAG);
|
|
assertLexeme('foo', TokenType.IDENTIFIER);
|
|
assertLexeme('"foo"', TokenType.STRING);
|
|
}
|
|
}
|