5
5
import 'package:charcode/charcode.dart' ;
6
6
import 'package:meta/meta.dart' ;
7
7
8
+ const _operatorKeyword = 'operator' ;
9
+ const Map <String , String > operatorNames = {
10
+ '[]' : 'get' ,
11
+ '[]=' : 'put' ,
12
+ '~' : 'bitwise_negate' ,
13
+ '==' : 'equals' ,
14
+ '-' : 'minus' ,
15
+ '+' : 'plus' ,
16
+ '*' : 'multiply' ,
17
+ '/' : 'divide' ,
18
+ '<' : 'less' ,
19
+ '>' : 'greater' ,
20
+ '>=' : 'greater_equal' ,
21
+ '<=' : 'less_equal' ,
22
+ '<<' : 'shift_left' ,
23
+ '>>' : 'shift_right' ,
24
+ '>>>' : 'triple_shift' ,
25
+ '^' : 'bitwise_exclusive_or' ,
26
+ 'unary-' : 'unary_minus' ,
27
+ '|' : 'bitwise_or' ,
28
+ '&' : 'bitwise_and' ,
29
+ '~/' : 'truncate_divide' ,
30
+ '%' : 'modulo'
31
+ };
32
+
33
+ class StringTrie {
34
+ final Map <int , StringTrie > children = {};
35
+
36
+ /// Does [this] node represent a valid entry in the trie?
37
+ bool valid = false ;
38
+
39
+ /// Greedily match on the string trie. Returns the index of the first
40
+ /// non-operator character if valid, otherwise -1.
41
+ int match (String toMatch, [int index = 0 ]) {
42
+ if (index < 0 || index >= toMatch.length) return valid ? index : 1 ;
43
+ var matchChar = toMatch.codeUnitAt (index);
44
+ if (children.containsKey (matchChar)) {
45
+ return children[matchChar].match (toMatch, index + 1 );
46
+ }
47
+ return valid ? index : - 1 ;
48
+ }
49
+
50
+ void addWord (String toAdd) {
51
+ var currentTrie = this ;
52
+ for (var i in toAdd.codeUnits) {
53
+ currentTrie.children.putIfAbsent (i, () => StringTrie ());
54
+ currentTrie = currentTrie.children[i];
55
+ }
56
+ currentTrie.valid = true ;
57
+ }
58
+ }
59
+
60
+ StringTrie _operatorParseTrie;
61
+ StringTrie get operatorParseTrie {
62
+ if (_operatorParseTrie == null ) {
63
+ _operatorParseTrie = StringTrie ();
64
+ for (var name in operatorNames.keys) {
65
+ _operatorParseTrie.addWord (name);
66
+ }
67
+ }
68
+ return _operatorParseTrie;
69
+ }
70
+
8
71
/// A parser for comment references.
9
72
// TODO(jcollins-g): align with [CommentReference] from analyzer AST.
10
73
class CommentReferenceParser {
@@ -30,7 +93,7 @@ class CommentReferenceParser {
30
93
/// ```text
31
94
/// <rawCommentReference> ::= <prefix>?<commentReference><suffix>?
32
95
///
33
- /// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <identifier > ('.' <identifier>)*
96
+ /// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <dartdocIdentifier > ('.' <identifier>)*
34
97
/// ```
35
98
List <CommentReferenceNode > _parseRawCommentReference () {
36
99
var children = < CommentReferenceNode > [];
@@ -90,7 +153,7 @@ class CommentReferenceParser {
90
153
///
91
154
/// <constructorPrefixHint> ::= 'new '
92
155
///
93
- /// <leadingJunk> ::= ('const' | 'final' | 'var')(' '+)
156
+ /// <leadingJunk> ::= ('const' | 'final' | 'var' | 'operator' )(' '+)
94
157
/// ```
95
158
_PrefixParseResult _parsePrefix () {
96
159
if (_atEnd) {
@@ -108,25 +171,55 @@ class CommentReferenceParser {
108
171
return _PrefixParseResult .missing;
109
172
}
110
173
111
- static const _whitespace = [ $space, $tab, $lf, $cr] ;
174
+ static const _whitespace = { $space, $tab, $lf, $cr} ;
112
175
static const _nonIdentifierChars = {
113
176
$dot,
114
- $lt,
115
177
$gt,
116
178
$lparen,
179
+ $lt,
117
180
$rparen,
118
- $slash,
119
181
$backslash,
120
182
$question,
121
183
$exclamation,
122
184
..._whitespace,
123
185
};
124
186
187
+ /// Advances the index forward to the end of the operator if one is
188
+ /// present and returns the operator's name. Otherwise, leaves _index
189
+ /// unchanged and returns null.
190
+ String _tryParseOperator () {
191
+ var tryIndex = _index;
192
+ if (tryIndex + _operatorKeyword.length < codeRef.length &&
193
+ codeRef.substring (tryIndex, tryIndex + _operatorKeyword.length) ==
194
+ _operatorKeyword) {
195
+ tryIndex = tryIndex + _operatorKeyword.length;
196
+ while (_whitespace.contains (codeRef.codeUnitAt (tryIndex))) {
197
+ tryIndex++ ;
198
+ }
199
+ }
200
+
201
+ var result = operatorParseTrie.match (codeRef, tryIndex);
202
+ if (result == - 1 ) {
203
+ return null ;
204
+ }
205
+ _index = result;
206
+ return codeRef.substring (tryIndex, result);
207
+ }
208
+
209
+ /// Parse a dartdoc identifier.
210
+ ///
211
+ /// Dartdoc identifiers can include some operators.
125
212
_IdentifierParseResult _parseIdentifier () {
126
213
if (_atEnd) {
127
214
return _IdentifierParseResult .endOfFile;
128
215
}
129
216
var startIndex = _index;
217
+
218
+ var foundOperator = _tryParseOperator ();
219
+ if (foundOperator != null ) {
220
+ return _IdentifierParseResult .ok (IdentifierNode (foundOperator));
221
+ }
222
+
130
223
while (! _atEnd) {
131
224
if (_nonIdentifierChars.contains (_thisChar)) {
132
225
if (startIndex == _index) {
0 commit comments