MLIR  19.0.0git
QueryParser.cpp
Go to the documentation of this file.
1 //===---- QueryParser.cpp - mlir-query command parser ---------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "QueryParser.h"
10 #include "llvm/ADT/StringSwitch.h"
11 
12 namespace mlir::query {
13 
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
17 // lexed region.
18 llvm::StringRef QueryParser::lexWord() {
19  // Don't trim newlines.
20  line = line.ltrim(" \t\v\f\r");
21 
22  if (line.empty())
23  // Even though the line is empty, it contains a pointer and
24  // a (zero) length. The pointer is used in the LexOrCompleteWord
25  // code completion.
26  return line;
27 
28  llvm::StringRef word;
29  if (line.front() == '#') {
30  word = line.substr(0, 1);
31  } else {
32  word = line.take_until([](char c) {
33  // Don't trim newlines.
34  return llvm::StringRef(" \t\v\f\r").contains(c);
35  });
36  }
37 
38  line = line.drop_front(word.size());
39  return word;
40 }
41 
42 // This is the StringSwitch-alike used by LexOrCompleteWord below. See that
43 // function for details.
44 template <typename T>
46  llvm::StringRef word;
48 
50  // Set to the completion point offset in word, or StringRef::npos if
51  // completion point not in word.
53 
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) {
60  outWord = word;
61  if (queryParser->completionPos &&
62  queryParser->completionPos <= word.data() + word.size()) {
63  if (queryParser->completionPos < word.data())
65  else
66  wordCompletionPos = queryParser->completionPos - word.data();
67  }
68  }
69 
70  LexOrCompleteWord &Case(llvm::StringLiteral caseStr, const T &value,
71  bool isCompletion = true) {
72 
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)) {
79 
80  queryParser->completions.emplace_back(
81  (caseStr.substr(wordCompletionPos) + " ").str(),
82  std::string(caseStr));
83  }
84  return *this;
85  }
86 
87  T Default(T value) { return stringSwitch.Default(value); }
88 };
89 
90 QueryRef QueryParser::endQuery(QueryRef queryRef) {
91  llvm::StringRef extra = line;
92  llvm::StringRef extraTrimmed = extra.ltrim(" \t\v\f\r");
93 
94  if ((!extraTrimmed.empty() && extraTrimmed[0] == '\n') ||
95  (extraTrimmed.size() >= 2 && extraTrimmed[0] == '\r' &&
96  extraTrimmed[1] == '\n'))
97  queryRef->remainingContent = extra;
98  else {
99  llvm::StringRef trailingWord = lexWord();
100  if (!trailingWord.empty() && trailingWord.front() == '#') {
101  line = line.drop_until([](char c) { return c == '\n'; });
102  line = line.drop_while([](char c) { return c == '\n'; });
103  return endQuery(queryRef);
104  }
105  if (!trailingWord.empty()) {
106  return new InvalidQuery("unexpected extra input: '" + extra + "'");
107  }
108  }
109  return queryRef;
110 }
111 
112 namespace {
113 
114 enum class ParsedQueryKind {
115  Invalid,
116  Comment,
117  NoOp,
118  Help,
119  Match,
120  Quit,
121 };
122 
123 QueryRef
124 makeInvalidQueryFromDiagnostics(const matcher::internal::Diagnostics &diag) {
125  std::string errStr;
126  llvm::raw_string_ostream os(errStr);
127  diag.print(os);
128  return new InvalidQuery(os.str());
129 }
130 } // namespace
131 
132 QueryRef QueryParser::completeMatcherExpression() {
133  std::vector<matcher::MatcherCompletion> comps =
135  line, completionPos - line.begin(), qs.getRegistryData(),
136  &qs.namedValues);
137  for (const auto &comp : comps) {
138  completions.emplace_back(comp.typedText, comp.matcherDecl);
139  }
140  return QueryRef();
141 }
142 
143 QueryRef QueryParser::doParse() {
144 
145  llvm::StringRef commandStr;
146  ParsedQueryKind qKind =
147  LexOrCompleteWord<ParsedQueryKind>(this, commandStr)
148  .Case("", ParsedQueryKind::NoOp)
149  .Case("#", ParsedQueryKind::Comment, /*isCompletion=*/false)
150  .Case("help", ParsedQueryKind::Help)
151  .Case("m", ParsedQueryKind::Match, /*isCompletion=*/false)
152  .Case("match", ParsedQueryKind::Match)
153  .Case("q", ParsedQueryKind::Quit, /*IsCompletion=*/false)
154  .Case("quit", ParsedQueryKind::Quit)
155  .Default(ParsedQueryKind::Invalid);
156 
157  switch (qKind) {
158  case ParsedQueryKind::Comment:
159  case ParsedQueryKind::NoOp:
160  line = line.drop_until([](char c) { return c == '\n'; });
161  line = line.drop_while([](char c) { return c == '\n'; });
162  if (line.empty())
163  return new NoOpQuery;
164  return doParse();
165 
166  case ParsedQueryKind::Help:
167  return endQuery(new HelpQuery);
168 
169  case ParsedQueryKind::Quit:
170  return endQuery(new QuitQuery);
171 
172  case ParsedQueryKind::Match: {
173  if (completionPos) {
174  return completeMatcherExpression();
175  }
176 
177  matcher::internal::Diagnostics diag;
178  auto matcherSource = line.ltrim();
179  auto origMatcherSource = matcherSource;
180  std::optional<matcher::DynMatcher> matcher =
182  matcherSource, qs.getRegistryData(), &qs.namedValues, &diag);
183  if (!matcher) {
184  return makeInvalidQueryFromDiagnostics(diag);
185  }
186  auto actualSource = origMatcherSource.slice(0, origMatcherSource.size() -
187  matcherSource.size());
188  QueryRef query = new MatchQuery(actualSource, *matcher);
189  query->remainingContent = matcherSource;
190  return query;
191  }
192 
193  case ParsedQueryKind::Invalid:
194  return new InvalidQuery("unknown command: " + commandStr);
195  }
196 
197  llvm_unreachable("Invalid query kind");
198 }
199 
200 QueryRef QueryParser::parse(llvm::StringRef line, const QuerySession &qs) {
201  return QueryParser(line, qs).doParse();
202 }
203 
204 std::vector<llvm::LineEditor::Completion>
205 QueryParser::complete(llvm::StringRef line, size_t pos,
206  const QuerySession &qs) {
207  QueryParser queryParser(line, qs);
208  queryParser.completionPos = line.data() + pos;
209 
210  queryParser.doParse();
211  return queryParser.completions;
212 }
213 
214 } // namespace mlir::query
static std::string diag(const llvm::Value &value)
static QueryRef parse(llvm::StringRef line, const QuerySession &qs)
static std::vector< llvm::LineEditor::Completion > complete(llvm::StringRef line, size_t pos, const QuerySession &qs)
const matcher::Registry & getRegistryData() const
Definition: QuerySession.h:28
llvm::StringMap< matcher::VariantValue > namedValues
Definition: QuerySession.h:30
static std::vector< MatcherCompletion > completeExpression(llvm::StringRef &code, unsigned completionOffset, const Registry &matcherRegistry, const NamedValueMap *namedValues)
Definition: Parser.cpp:564
static std::optional< DynMatcher > parseMatcherExpression(llvm::StringRef &matcherCode, const Registry &matcherRegistry, const NamedValueMap *namedValues, Diagnostics *error)
Definition: Parser.cpp:576
Include the generated interface declarations.
Definition: CallGraph.h:229
llvm::IntrusiveRefCntPtr< Query > QueryRef
Definition: Query.h:37
LexOrCompleteWord(QueryParser *queryParser, llvm::StringRef &outWord)
Definition: QueryParser.cpp:57
LexOrCompleteWord & Case(llvm::StringLiteral caseStr, const T &value, bool isCompletion=true)
Definition: QueryParser.cpp:70