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
12namespace 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.
18llvm::StringRef QueryParser::lexWord() {
19 // Don't trim newlines.
20 line = line.ltrim(Chars: " \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(Start: 0, N: 1);
31 } else {
32 word = line.take_until(F: [](char c) {
33 // Don't trim newlines.
34 return llvm::StringRef(" \t\v\f\r").contains(C: c);
35 });
36 }
37
38 line = line.drop_front(N: word.size());
39 return word;
40}
41
42// This is the StringSwitch-alike used by LexOrCompleteWord below. See that
43// function for details.
44template <typename T>
45struct QueryParser::LexOrCompleteWord {
46 llvm::StringRef word;
47 llvm::StringSwitch<T> stringSwitch;
48
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;
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())
64 wordCompletionPos = 0;
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(Start: 0, N: wordCompletionPos) ==
78 word.substr(Start: 0, N: wordCompletionPos)) {
79
80 queryParser->completions.emplace_back(
81 args: (caseStr.substr(Start: wordCompletionPos) + " ").str(),
82 args: std::string(caseStr));
83 }
84 return *this;
85 }
86
87 T Default(T value) { return stringSwitch.Default(value); }
88};
89
90QueryRef QueryParser::endQuery(QueryRef queryRef) {
91 llvm::StringRef extra = line;
92 llvm::StringRef extraTrimmed = extra.ltrim(Chars: " \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(F: [](char c) { return c == '\n'; });
102 line = line.drop_while(F: [](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
112namespace {
113
114enum class ParsedQueryKind {
115 Invalid,
116 Comment,
117 NoOp,
118 Help,
119 Match,
120 Quit,
121};
122
123QueryRef
124makeInvalidQueryFromDiagnostics(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
132QueryRef QueryParser::completeMatcherExpression() {
133 std::vector<matcher::MatcherCompletion> comps =
134 matcher::internal::Parser::completeExpression(
135 code&: line, completionOffset: completionPos - line.begin(), matcherRegistry: qs.getRegistryData(),
136 namedValues: &qs.namedValues);
137 for (const auto &comp : comps) {
138 completions.emplace_back(args: comp.typedText, args: comp.matcherDecl);
139 }
140 return QueryRef();
141}
142
143QueryRef QueryParser::doParse() {
144
145 llvm::StringRef commandStr;
146 ParsedQueryKind qKind =
147 LexOrCompleteWord<ParsedQueryKind>(this, commandStr)
148 .Case(caseStr: "", value: ParsedQueryKind::NoOp)
149 .Case(caseStr: "#", value: ParsedQueryKind::Comment, /*isCompletion=*/false)
150 .Case(caseStr: "help", value: ParsedQueryKind::Help)
151 .Case(caseStr: "m", value: ParsedQueryKind::Match, /*isCompletion=*/false)
152 .Case(caseStr: "match", value: ParsedQueryKind::Match)
153 .Case(caseStr: "q", value: ParsedQueryKind::Quit, /*IsCompletion=*/isCompletion: false)
154 .Case(caseStr: "quit", value: ParsedQueryKind::Quit)
155 .Default(value: ParsedQueryKind::Invalid);
156
157 switch (qKind) {
158 case ParsedQueryKind::Comment:
159 case ParsedQueryKind::NoOp:
160 line = line.drop_until(F: [](char c) { return c == '\n'; });
161 line = line.drop_while(F: [](char c) { return c == '\n'; });
162 if (line.empty())
163 return new NoOpQuery;
164 return doParse();
165
166 case ParsedQueryKind::Help:
167 return endQuery(queryRef: new HelpQuery);
168
169 case ParsedQueryKind::Quit:
170 return endQuery(queryRef: 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 =
181 matcher::internal::Parser::parseMatcherExpression(
182 matcherCode&: matcherSource, matcherRegistry: qs.getRegistryData(), namedValues: &qs.namedValues, error: &diag);
183 if (!matcher) {
184 return makeInvalidQueryFromDiagnostics(diag);
185 }
186 auto actualSource = origMatcherSource.slice(Start: 0, End: 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
200QueryRef QueryParser::parse(llvm::StringRef line, const QuerySession &qs) {
201 return QueryParser(line, qs).doParse();
202}
203
204std::vector<llvm::LineEditor::Completion>
205QueryParser::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
215

source code of mlir/lib/Query/QueryParser.cpp