15
15
#include " llvm/ADT/StringRef.h"
16
16
#include " llvm/ADT/StringSwitch.h"
17
17
18
+ #include < set>
19
+
18
20
using namespace llvm ;
19
21
using namespace clang ::ast_matchers::dynamic;
20
22
@@ -25,10 +27,10 @@ namespace query {
25
27
// non-whitespace characters) from the start of region [Begin,End). If no word
26
28
// is found before End, return StringRef(). Begin is adjusted to exclude the
27
29
// lexed region.
28
- static StringRef LexWord ( const char *&Begin, const char *End ) {
30
+ StringRef QueryParser::lexWord ( ) {
29
31
while (true ) {
30
32
if (Begin == End)
31
- return StringRef ();
33
+ return StringRef (Begin, 0 );
32
34
33
35
if (!isWhitespace (*Begin))
34
36
break ;
@@ -46,8 +48,60 @@ static StringRef LexWord(const char *&Begin, const char *End) {
46
48
}
47
49
}
48
50
49
- static QueryRef ParseSetBool (bool QuerySession::*Var, StringRef ValStr) {
50
- unsigned Value = StringSwitch<unsigned >(ValStr)
51
+ // This is the StringSwitch-alike used by lexOrCompleteWord below. See that
52
+ // function for details.
53
+ template <typename T> struct QueryParser ::LexOrCompleteWord {
54
+ StringSwitch<T> Switch;
55
+
56
+ QueryParser *P;
57
+ StringRef Word;
58
+ // Set to the completion point offset in Word, or StringRef::npos if
59
+ // completion point not in Word.
60
+ size_t WordCompletionPos;
61
+
62
+ LexOrCompleteWord (QueryParser *P, StringRef Word, size_t WCP)
63
+ : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
64
+
65
+ template <unsigned N>
66
+ LexOrCompleteWord &Case (const char (&S)[N], const T &Value,
67
+ bool IsCompletion = true) {
68
+ StringRef CaseStr (S, N - 1 );
69
+
70
+ if (WordCompletionPos == StringRef::npos)
71
+ Switch.Case (S, Value);
72
+ else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size () &&
73
+ CaseStr.substr (0 , WordCompletionPos) ==
74
+ Word.substr (0 , WordCompletionPos))
75
+ P->Completions .push_back (LineEditor::Completion (
76
+ (CaseStr.substr (WordCompletionPos) + " " ).str (), CaseStr));
77
+ return *this ;
78
+ }
79
+
80
+ T Default (const T& Value) const {
81
+ return Switch.Default (Value);
82
+ }
83
+ };
84
+
85
+ // Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
86
+ // that can be used like a llvm::StringSwitch<T>, but adds cases as possible
87
+ // completions if the lexed word contains the completion point.
88
+ template <typename T>
89
+ QueryParser::LexOrCompleteWord<T>
90
+ QueryParser::lexOrCompleteWord (StringRef &Word) {
91
+ Word = lexWord ();
92
+ size_t WordCompletionPos = StringRef::npos;
93
+ if (CompletionPos && CompletionPos <= Word.data () + Word.size ()) {
94
+ if (CompletionPos < Word.data ())
95
+ WordCompletionPos = 0 ;
96
+ else
97
+ WordCompletionPos = CompletionPos - Word.data ();
98
+ }
99
+ return LexOrCompleteWord<T>(this , Word, WordCompletionPos);
100
+ }
101
+
102
+ QueryRef QueryParser::parseSetBool (bool QuerySession::*Var) {
103
+ StringRef ValStr;
104
+ unsigned Value = lexOrCompleteWord<unsigned >(ValStr)
51
105
.Case (" false" , 0 )
52
106
.Case (" true" , 1 )
53
107
.Default (~0u );
@@ -57,8 +111,9 @@ static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
57
111
return new SetQuery<bool >(Var, Value);
58
112
}
59
113
60
- static QueryRef ParseSetOutputKind (StringRef ValStr) {
61
- unsigned OutKind = StringSwitch<unsigned >(ValStr)
114
+ QueryRef QueryParser::parseSetOutputKind () {
115
+ StringRef ValStr;
116
+ unsigned OutKind = lexOrCompleteWord<unsigned >(ValStr)
62
117
.Case (" diag" , OK_Diag)
63
118
.Case (" print" , OK_Print)
64
119
.Case (" dump" , OK_Dump)
@@ -70,9 +125,9 @@ static QueryRef ParseSetOutputKind(StringRef ValStr) {
70
125
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind (OutKind));
71
126
}
72
127
73
- static QueryRef EndQuery ( const char *Begin, const char *End, QueryRef Q) {
128
+ QueryRef QueryParser::endQuery ( QueryRef Q) {
74
129
const char *Extra = Begin;
75
- if (!LexWord (Begin, End ).empty ())
130
+ if (!lexWord ( ).empty ())
76
131
return new InvalidQuery (" unexpected extra input: '" +
77
132
StringRef (Extra, End - Extra) + " '" );
78
133
return Q;
@@ -92,15 +147,12 @@ enum ParsedQueryVariable {
92
147
PQV_BindRoot
93
148
};
94
149
95
- QueryRef ParseQuery (StringRef Line) {
96
- const char *Begin = Line.data ();
97
- const char *End = Line.data () + Line.size ();
98
-
99
- StringRef CommandStr = LexWord (Begin, End);
100
- ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
150
+ QueryRef QueryParser::doParse () {
151
+ StringRef CommandStr;
152
+ ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
101
153
.Case (" " , PQK_NoOp)
102
154
.Case (" help" , PQK_Help)
103
- .Case (" m" , PQK_Match)
155
+ .Case (" m" , PQK_Match, /* IsCompletion= */ false )
104
156
.Case (" match" , PQK_Match)
105
157
.Case (" set" , PQK_Set)
106
158
.Default (PQK_Invalid);
@@ -110,50 +162,57 @@ QueryRef ParseQuery(StringRef Line) {
110
162
return new NoOpQuery;
111
163
112
164
case PQK_Help:
113
- return EndQuery (Begin, End, new HelpQuery);
165
+ return endQuery ( new HelpQuery);
114
166
115
167
case PQK_Match: {
116
- Diagnostics Diag;
117
- Optional<DynTypedMatcher> Matcher =
118
- Parser::parseMatcherExpression (StringRef (Begin, End - Begin), &Diag);
119
- if (!Matcher) {
120
- std::string ErrStr;
121
- llvm::raw_string_ostream OS (ErrStr);
122
- Diag.printToStreamFull (OS);
123
- return new InvalidQuery (OS.str ());
168
+ if (CompletionPos) {
169
+ std::vector<MatcherCompletion> Comps = Parser::completeExpression (
170
+ StringRef (Begin, End - Begin), CompletionPos - Begin);
171
+ for (std::vector<MatcherCompletion>::iterator I = Comps.begin (),
172
+ E = Comps.end ();
173
+ I != E; ++I) {
174
+ Completions.push_back (
175
+ LineEditor::Completion (I->TypedText , I->MatcherDecl ));
176
+ }
177
+ return QueryRef ();
178
+ } else {
179
+ Diagnostics Diag;
180
+ Optional<DynTypedMatcher> Matcher =
181
+ Parser::parseMatcherExpression (StringRef (Begin, End - Begin), &Diag);
182
+ if (!Matcher) {
183
+ std::string ErrStr;
184
+ llvm::raw_string_ostream OS (ErrStr);
185
+ Diag.printToStreamFull (OS);
186
+ return new InvalidQuery (OS.str ());
187
+ }
188
+ return new MatchQuery (*Matcher);
124
189
}
125
- return new MatchQuery (*Matcher);
126
190
}
127
191
128
192
case PQK_Set: {
129
- StringRef VarStr = LexWord (Begin, End);
193
+ StringRef VarStr;
194
+ ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
195
+ .Case (" output" , PQV_Output)
196
+ .Case (" bind-root" , PQV_BindRoot)
197
+ .Default (PQV_Invalid);
130
198
if (VarStr.empty ())
131
199
return new InvalidQuery (" expected variable name" );
132
-
133
- ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
134
- .Case (" output" , PQV_Output)
135
- .Case (" bind-root" , PQV_BindRoot)
136
- .Default (PQV_Invalid);
137
200
if (Var == PQV_Invalid)
138
201
return new InvalidQuery (" unknown variable: '" + VarStr + " '" );
139
202
140
- StringRef ValStr = LexWord (Begin, End);
141
- if (ValStr.empty ())
142
- return new InvalidQuery (" expected variable value" );
143
-
144
203
QueryRef Q;
145
204
switch (Var) {
146
205
case PQV_Output:
147
- Q = ParseSetOutputKind (ValStr );
206
+ Q = parseSetOutputKind ( );
148
207
break ;
149
208
case PQV_BindRoot:
150
- Q = ParseSetBool (&QuerySession::BindRoot, ValStr );
209
+ Q = parseSetBool (&QuerySession::BindRoot);
151
210
break ;
152
211
case PQV_Invalid:
153
212
llvm_unreachable (" Invalid query kind" );
154
213
}
155
214
156
- return EndQuery (Begin, End, Q);
215
+ return endQuery ( Q);
157
216
}
158
217
159
218
case PQK_Invalid:
@@ -163,5 +222,18 @@ QueryRef ParseQuery(StringRef Line) {
163
222
llvm_unreachable (" Invalid query kind" );
164
223
}
165
224
225
+ QueryRef QueryParser::parse (StringRef Line) {
226
+ return QueryParser (Line).doParse ();
227
+ }
228
+
229
+ std::vector<LineEditor::Completion> QueryParser::complete (StringRef Line,
230
+ size_t Pos) {
231
+ QueryParser P (Line);
232
+ P.CompletionPos = Line.data () + Pos;
233
+
234
+ P.doParse ();
235
+ return P.Completions ;
236
+ }
237
+
166
238
} // namespace query
167
239
} // namespace clang
0 commit comments