1 //===---- QueryParser.cpp - mlir-query command parser ---------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "QueryParser.h"
10 #include "llvm/ADT/StringSwitch.h"
12 namespace mlir::query
{
14 // Lex any amount of whitespace followed by a "word" (any sequence of
15 // non-whitespace characters) from the start of region [begin,end). If no word
16 // is found before end, return StringRef(). begin is adjusted to exclude the
18 llvm::StringRef
QueryParser::lexWord() {
19 // Don't trim newlines.
20 line
= line
.ltrim(" \t\v\f\r");
23 // Even though the line is empty, it contains a pointer and
24 // a (zero) length. The pointer is used in the LexOrCompleteWord
29 if (line
.front() == '#') {
30 word
= line
.substr(0, 1);
32 word
= line
.take_until([](char c
) {
33 // Don't trim newlines.
34 return llvm::StringRef(" \t\v\f\r").contains(c
);
38 line
= line
.drop_front(word
.size());
42 // This is the StringSwitch-alike used by LexOrCompleteWord below. See that
43 // function for details.
45 struct QueryParser::LexOrCompleteWord
{
47 llvm::StringSwitch
<T
> stringSwitch
;
49 QueryParser
*queryParser
;
50 // Set to the completion point offset in word, or StringRef::npos if
51 // completion point not in word.
52 size_t wordCompletionPos
;
54 // Lexes a word and stores it in word. Returns a LexOrCompleteword<T> object
55 // that can be used like a llvm::StringSwitch<T>, but adds cases as possible
56 // completions if the lexed word contains the completion point.
57 LexOrCompleteWord(QueryParser
*queryParser
, llvm::StringRef
&outWord
)
58 : word(queryParser
->lexWord()), stringSwitch(word
),
59 queryParser(queryParser
), wordCompletionPos(llvm::StringRef::npos
) {
61 if (queryParser
->completionPos
&&
62 queryParser
->completionPos
<= word
.data() + word
.size()) {
63 if (queryParser
->completionPos
< word
.data())
64 wordCompletionPos
= 0;
66 wordCompletionPos
= queryParser
->completionPos
- word
.data();
70 LexOrCompleteWord
&Case(llvm::StringLiteral caseStr
, const T
&value
,
71 bool isCompletion
= true) {
73 if (wordCompletionPos
== llvm::StringRef::npos
)
74 stringSwitch
.Case(caseStr
, value
);
75 else if (!caseStr
.empty() && isCompletion
&&
76 wordCompletionPos
<= caseStr
.size() &&
77 caseStr
.substr(0, wordCompletionPos
) ==
78 word
.substr(0, wordCompletionPos
)) {
80 queryParser
->completions
.emplace_back(
81 (caseStr
.substr(wordCompletionPos
) + " ").str(),
82 std::string(caseStr
));
87 T
Default(T value
) { return stringSwitch
.Default(value
); }
90 QueryRef
QueryParser::endQuery(QueryRef queryRef
) {
91 llvm::StringRef extra
= line
;
92 llvm::StringRef extraTrimmed
= extra
.ltrim(" \t\v\f\r");
94 if (extraTrimmed
.starts_with('\n') || extraTrimmed
.starts_with("\r\n"))
95 queryRef
->remainingContent
= extra
;
97 llvm::StringRef trailingWord
= lexWord();
98 if (trailingWord
.starts_with('#')) {
99 line
= line
.drop_until([](char c
) { return c
== '\n'; });
100 line
= line
.drop_while([](char c
) { return c
== '\n'; });
101 return endQuery(queryRef
);
103 if (!trailingWord
.empty()) {
104 return new InvalidQuery("unexpected extra input: '" + extra
+ "'");
112 enum class ParsedQueryKind
{
122 makeInvalidQueryFromDiagnostics(const matcher::internal::Diagnostics
&diag
) {
124 llvm::raw_string_ostream
os(errStr
);
126 return new InvalidQuery(errStr
);
130 QueryRef
QueryParser::completeMatcherExpression() {
131 std::vector
<matcher::MatcherCompletion
> comps
=
132 matcher::internal::Parser::completeExpression(
133 line
, completionPos
- line
.begin(), qs
.getRegistryData(),
135 for (const auto &comp
: comps
) {
136 completions
.emplace_back(comp
.typedText
, comp
.matcherDecl
);
141 QueryRef
QueryParser::doParse() {
143 llvm::StringRef commandStr
;
144 ParsedQueryKind qKind
=
145 LexOrCompleteWord
<ParsedQueryKind
>(this, commandStr
)
146 .Case("", ParsedQueryKind::NoOp
)
147 .Case("#", ParsedQueryKind::Comment
, /*isCompletion=*/false)
148 .Case("help", ParsedQueryKind::Help
)
149 .Case("m", ParsedQueryKind::Match
, /*isCompletion=*/false)
150 .Case("match", ParsedQueryKind::Match
)
151 .Case("q", ParsedQueryKind::Quit
, /*IsCompletion=*/false)
152 .Case("quit", ParsedQueryKind::Quit
)
153 .Default(ParsedQueryKind::Invalid
);
156 case ParsedQueryKind::Comment
:
157 case ParsedQueryKind::NoOp
:
158 line
= line
.drop_until([](char c
) { return c
== '\n'; });
159 line
= line
.drop_while([](char c
) { return c
== '\n'; });
161 return new NoOpQuery
;
164 case ParsedQueryKind::Help
:
165 return endQuery(new HelpQuery
);
167 case ParsedQueryKind::Quit
:
168 return endQuery(new QuitQuery
);
170 case ParsedQueryKind::Match
: {
172 return completeMatcherExpression();
175 matcher::internal::Diagnostics diag
;
176 auto matcherSource
= line
.ltrim();
177 auto origMatcherSource
= matcherSource
;
178 std::optional
<matcher::DynMatcher
> matcher
=
179 matcher::internal::Parser::parseMatcherExpression(
180 matcherSource
, qs
.getRegistryData(), &qs
.namedValues
, &diag
);
182 return makeInvalidQueryFromDiagnostics(diag
);
184 auto actualSource
= origMatcherSource
.substr(0, origMatcherSource
.size() -
185 matcherSource
.size());
186 QueryRef query
= new MatchQuery(actualSource
, *matcher
);
187 query
->remainingContent
= matcherSource
;
191 case ParsedQueryKind::Invalid
:
192 return new InvalidQuery("unknown command: " + commandStr
);
195 llvm_unreachable("Invalid query kind");
198 QueryRef
QueryParser::parse(llvm::StringRef line
, const QuerySession
&qs
) {
199 return QueryParser(line
, qs
).doParse();
202 std::vector
<llvm::LineEditor::Completion
>
203 QueryParser::complete(llvm::StringRef line
, size_t pos
,
204 const QuerySession
&qs
) {
205 QueryParser
queryParser(line
, qs
);
206 queryParser
.completionPos
= line
.data() + pos
;
208 queryParser
.doParse();
209 return queryParser
.completions
;
212 } // namespace mlir::query