Skip to content

Commit 02a9c15

Browse files
authored
Fix lexer issue where select/plural/other/underscores cannot be in identifier names. (#119190)
1 parent a45727d commit 02a9c15

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

packages/flutter_tools/lib/src/localizations/message_parser.dart

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,14 @@ RegExp normalString = RegExp(r'[^{}]+');
158158
RegExp brace = RegExp(r'{|}');
159159

160160
RegExp whitespace = RegExp(r'\s+');
161-
RegExp pluralKeyword = RegExp(r'plural');
162-
RegExp selectKeyword = RegExp(r'select');
163-
RegExp otherKeyword = RegExp(r'other');
164161
RegExp numeric = RegExp(r'[0-9]+');
165-
RegExp alphanumeric = RegExp(r'[a-zA-Z0-9]+');
162+
RegExp alphanumeric = RegExp(r'[a-zA-Z0-9|_]+');
166163
RegExp comma = RegExp(r',');
167164
RegExp equalSign = RegExp(r'=');
168165

169166
// List of token matchers ordered by precedence
170167
Map<ST, RegExp> matchers = <ST, RegExp>{
171168
ST.empty: whitespace,
172-
ST.plural: pluralKeyword,
173-
ST.select: selectKeyword,
174-
ST.other: otherKeyword,
175169
ST.number: numeric,
176170
ST.comma: comma,
177171
ST.equalSign: equalSign,
@@ -303,12 +297,25 @@ class Parser {
303297
// Do not add whitespace as a token.
304298
startIndex = match.end;
305299
continue;
306-
} else if (<ST>[ST.plural, ST.select].contains(matchedType) && tokens.last.type == ST.openBrace) {
307-
// Treat "plural" or "select" as identifier if it comes right after an open brace.
300+
} else if (<ST>[ST.identifier].contains(matchedType) && tokens.last.type == ST.openBrace) {
301+
// Treat any token as identifier if it comes right after an open brace, whether it's a keyword or not.
308302
tokens.add(Node(ST.identifier, startIndex, value: match.group(0)));
309303
startIndex = match.end;
310304
continue;
311305
} else {
306+
// Handle keywords separately. Otherwise, lexer will assume parts of identifiers may be keywords.
307+
final String tokenStr = match.group(0)!;
308+
switch(tokenStr) {
309+
case 'plural':
310+
matchedType = ST.plural;
311+
break;
312+
case 'select':
313+
matchedType = ST.select;
314+
break;
315+
case 'other':
316+
matchedType = ST.other;
317+
break;
318+
}
312319
tokens.add(Node(matchedType!, startIndex, value: match.group(0)));
313320
startIndex = match.end;
314321
continue;

packages/flutter_tools/test/general.shard/message_parser_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,22 @@ void main() {
226226
expect(tokens[5].type, equals(ST.identifier));
227227
});
228228

229+
testWithoutContext('lexer identifier names can contain underscores', () {
230+
final List<Node> tokens = Parser('keywords', 'app_en.arb', '{ test_placeholder } { test_select, select, singular{test} other{hmm} }').lexIntoTokens();
231+
expect(tokens[1].value, equals('test_placeholder'));
232+
expect(tokens[1].type, equals(ST.identifier));
233+
expect(tokens[5].value, equals('test_select'));
234+
expect(tokens[5].type, equals(ST.identifier));
235+
});
236+
237+
testWithoutContext('lexer identifier names can contain the strings select or plural', () {
238+
final List<Node> tokens = Parser('keywords', 'app_en.arb', '{ selectTest } { pluralTest, select, singular{test} other{hmm} }').lexIntoTokens();
239+
expect(tokens[1].value, equals('selectTest'));
240+
expect(tokens[1].type, equals(ST.identifier));
241+
expect(tokens[5].value, equals('pluralTest'));
242+
expect(tokens[5].type, equals(ST.identifier));
243+
});
244+
229245
testWithoutContext('lexer: lexically correct but syntactically incorrect', () {
230246
final List<Node> tokens = Parser(
231247
'syntax',

0 commit comments

Comments
 (0)