55import 'package:charcode/charcode.dart' ;
66import 'package:meta/meta.dart' ;
77
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+
871/// A parser for comment references.
972// TODO(jcollins-g): align with [CommentReference] from analyzer AST.
1073class CommentReferenceParser {
@@ -30,7 +93,7 @@ class CommentReferenceParser {
3093 /// ```text
3194 /// <rawCommentReference> ::= <prefix>?<commentReference><suffix>?
3295 ///
33- /// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <identifier > ('.' <identifier>)*
96+ /// <commentReference> ::= (<packageName> '.')? (<libraryName> '.')? <dartdocIdentifier > ('.' <identifier>)*
3497 /// ```
3598 List <CommentReferenceNode > _parseRawCommentReference () {
3699 var children = < CommentReferenceNode > [];
@@ -90,7 +153,7 @@ class CommentReferenceParser {
90153 ///
91154 /// <constructorPrefixHint> ::= 'new '
92155 ///
93- /// <leadingJunk> ::= ('const' | 'final' | 'var')(' '+)
156+ /// <leadingJunk> ::= ('const' | 'final' | 'var' | 'operator' )(' '+)
94157 /// ```
95158 _PrefixParseResult _parsePrefix () {
96159 if (_atEnd) {
@@ -108,25 +171,55 @@ class CommentReferenceParser {
108171 return _PrefixParseResult .missing;
109172 }
110173
111- static const _whitespace = [ $space, $tab, $lf, $cr] ;
174+ static const _whitespace = { $space, $tab, $lf, $cr} ;
112175 static const _nonIdentifierChars = {
113176 $dot,
114- $lt,
115177 $gt,
116178 $lparen,
179+ $lt,
117180 $rparen,
118- $slash,
119181 $backslash,
120182 $question,
121183 $exclamation,
122184 ..._whitespace,
123185 };
124186
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.
125212 _IdentifierParseResult _parseIdentifier () {
126213 if (_atEnd) {
127214 return _IdentifierParseResult .endOfFile;
128215 }
129216 var startIndex = _index;
217+
218+ var foundOperator = _tryParseOperator ();
219+ if (foundOperator != null ) {
220+ return _IdentifierParseResult .ok (IdentifierNode (foundOperator));
221+ }
222+
130223 while (! _atEnd) {
131224 if (_nonIdentifierChars.contains (_thisChar)) {
132225 if (startIndex == _index) {
0 commit comments