xref: /llvm-project/mlir/lib/Query/QueryParser.cpp (revision 5287a9b3456fe7aefa24c8da95ef265b8dba875b)
102d9f4d1SDevajith //===---- QueryParser.cpp - mlir-query command parser ---------------------===//
202d9f4d1SDevajith //
302d9f4d1SDevajith // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
402d9f4d1SDevajith // See https://llvm.org/LICENSE.txt for license information.
502d9f4d1SDevajith // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
602d9f4d1SDevajith //
702d9f4d1SDevajith //===----------------------------------------------------------------------===//
802d9f4d1SDevajith 
902d9f4d1SDevajith #include "QueryParser.h"
1002d9f4d1SDevajith #include "llvm/ADT/StringSwitch.h"
1102d9f4d1SDevajith 
1202d9f4d1SDevajith namespace mlir::query {
1302d9f4d1SDevajith 
1402d9f4d1SDevajith // Lex any amount of whitespace followed by a "word" (any sequence of
1502d9f4d1SDevajith // non-whitespace characters) from the start of region [begin,end).  If no word
1602d9f4d1SDevajith // is found before end, return StringRef(). begin is adjusted to exclude the
1702d9f4d1SDevajith // lexed region.
1802d9f4d1SDevajith llvm::StringRef QueryParser::lexWord() {
1902d9f4d1SDevajith   // Don't trim newlines.
20abaa79b2SKazu Hirata   line = line.ltrim(" \t\v\f\r");
2102d9f4d1SDevajith 
2202d9f4d1SDevajith   if (line.empty())
2302d9f4d1SDevajith     // Even though the line is empty, it contains a pointer and
2402d9f4d1SDevajith     // a (zero) length. The pointer is used in the LexOrCompleteWord
2502d9f4d1SDevajith     // code completion.
2602d9f4d1SDevajith     return line;
2702d9f4d1SDevajith 
2802d9f4d1SDevajith   llvm::StringRef word;
2902d9f4d1SDevajith   if (line.front() == '#') {
3002d9f4d1SDevajith     word = line.substr(0, 1);
3102d9f4d1SDevajith   } else {
3202d9f4d1SDevajith     word = line.take_until([](char c) {
3302d9f4d1SDevajith       // Don't trim newlines.
3402d9f4d1SDevajith       return llvm::StringRef(" \t\v\f\r").contains(c);
3502d9f4d1SDevajith     });
3602d9f4d1SDevajith   }
3702d9f4d1SDevajith 
3802d9f4d1SDevajith   line = line.drop_front(word.size());
3902d9f4d1SDevajith   return word;
4002d9f4d1SDevajith }
4102d9f4d1SDevajith 
4202d9f4d1SDevajith // This is the StringSwitch-alike used by LexOrCompleteWord below. See that
4302d9f4d1SDevajith // function for details.
4402d9f4d1SDevajith template <typename T>
4502d9f4d1SDevajith struct QueryParser::LexOrCompleteWord {
4602d9f4d1SDevajith   llvm::StringRef word;
4702d9f4d1SDevajith   llvm::StringSwitch<T> stringSwitch;
4802d9f4d1SDevajith 
4902d9f4d1SDevajith   QueryParser *queryParser;
5002d9f4d1SDevajith   // Set to the completion point offset in word, or StringRef::npos if
5102d9f4d1SDevajith   // completion point not in word.
5202d9f4d1SDevajith   size_t wordCompletionPos;
5302d9f4d1SDevajith 
5402d9f4d1SDevajith   // Lexes a word and stores it in word. Returns a LexOrCompleteword<T> object
5502d9f4d1SDevajith   // that can be used like a llvm::StringSwitch<T>, but adds cases as possible
5602d9f4d1SDevajith   // completions if the lexed word contains the completion point.
5702d9f4d1SDevajith   LexOrCompleteWord(QueryParser *queryParser, llvm::StringRef &outWord)
5802d9f4d1SDevajith       : word(queryParser->lexWord()), stringSwitch(word),
5902d9f4d1SDevajith         queryParser(queryParser), wordCompletionPos(llvm::StringRef::npos) {
6002d9f4d1SDevajith     outWord = word;
6102d9f4d1SDevajith     if (queryParser->completionPos &&
6202d9f4d1SDevajith         queryParser->completionPos <= word.data() + word.size()) {
6302d9f4d1SDevajith       if (queryParser->completionPos < word.data())
6402d9f4d1SDevajith         wordCompletionPos = 0;
6502d9f4d1SDevajith       else
6602d9f4d1SDevajith         wordCompletionPos = queryParser->completionPos - word.data();
6702d9f4d1SDevajith     }
6802d9f4d1SDevajith   }
6902d9f4d1SDevajith 
7002d9f4d1SDevajith   LexOrCompleteWord &Case(llvm::StringLiteral caseStr, const T &value,
7102d9f4d1SDevajith                           bool isCompletion = true) {
7202d9f4d1SDevajith 
7302d9f4d1SDevajith     if (wordCompletionPos == llvm::StringRef::npos)
7402d9f4d1SDevajith       stringSwitch.Case(caseStr, value);
7502d9f4d1SDevajith     else if (!caseStr.empty() && isCompletion &&
7602d9f4d1SDevajith              wordCompletionPos <= caseStr.size() &&
7702d9f4d1SDevajith              caseStr.substr(0, wordCompletionPos) ==
7802d9f4d1SDevajith                  word.substr(0, wordCompletionPos)) {
7902d9f4d1SDevajith 
8002d9f4d1SDevajith       queryParser->completions.emplace_back(
8102d9f4d1SDevajith           (caseStr.substr(wordCompletionPos) + " ").str(),
8202d9f4d1SDevajith           std::string(caseStr));
8302d9f4d1SDevajith     }
8402d9f4d1SDevajith     return *this;
8502d9f4d1SDevajith   }
8602d9f4d1SDevajith 
8702d9f4d1SDevajith   T Default(T value) { return stringSwitch.Default(value); }
8802d9f4d1SDevajith };
8902d9f4d1SDevajith 
9002d9f4d1SDevajith QueryRef QueryParser::endQuery(QueryRef queryRef) {
9102d9f4d1SDevajith   llvm::StringRef extra = line;
92abaa79b2SKazu Hirata   llvm::StringRef extraTrimmed = extra.ltrim(" \t\v\f\r");
9302d9f4d1SDevajith 
948901f718SKazu Hirata   if (extraTrimmed.starts_with('\n') || extraTrimmed.starts_with("\r\n"))
9502d9f4d1SDevajith     queryRef->remainingContent = extra;
9602d9f4d1SDevajith   else {
9702d9f4d1SDevajith     llvm::StringRef trailingWord = lexWord();
988901f718SKazu Hirata     if (trailingWord.starts_with('#')) {
9902d9f4d1SDevajith       line = line.drop_until([](char c) { return c == '\n'; });
10002d9f4d1SDevajith       line = line.drop_while([](char c) { return c == '\n'; });
10102d9f4d1SDevajith       return endQuery(queryRef);
10202d9f4d1SDevajith     }
10302d9f4d1SDevajith     if (!trailingWord.empty()) {
10402d9f4d1SDevajith       return new InvalidQuery("unexpected extra input: '" + extra + "'");
10502d9f4d1SDevajith     }
10602d9f4d1SDevajith   }
10702d9f4d1SDevajith   return queryRef;
10802d9f4d1SDevajith }
10902d9f4d1SDevajith 
11002d9f4d1SDevajith namespace {
11102d9f4d1SDevajith 
11202d9f4d1SDevajith enum class ParsedQueryKind {
11302d9f4d1SDevajith   Invalid,
11402d9f4d1SDevajith   Comment,
11502d9f4d1SDevajith   NoOp,
11602d9f4d1SDevajith   Help,
11702d9f4d1SDevajith   Match,
11802d9f4d1SDevajith   Quit,
11902d9f4d1SDevajith };
12002d9f4d1SDevajith 
12102d9f4d1SDevajith QueryRef
12202d9f4d1SDevajith makeInvalidQueryFromDiagnostics(const matcher::internal::Diagnostics &diag) {
12302d9f4d1SDevajith   std::string errStr;
12402d9f4d1SDevajith   llvm::raw_string_ostream os(errStr);
12502d9f4d1SDevajith   diag.print(os);
126884221edSJOE1994   return new InvalidQuery(errStr);
12702d9f4d1SDevajith }
12802d9f4d1SDevajith } // namespace
12902d9f4d1SDevajith 
13002d9f4d1SDevajith QueryRef QueryParser::completeMatcherExpression() {
13102d9f4d1SDevajith   std::vector<matcher::MatcherCompletion> comps =
13202d9f4d1SDevajith       matcher::internal::Parser::completeExpression(
13302d9f4d1SDevajith           line, completionPos - line.begin(), qs.getRegistryData(),
13402d9f4d1SDevajith           &qs.namedValues);
13502d9f4d1SDevajith   for (const auto &comp : comps) {
13602d9f4d1SDevajith     completions.emplace_back(comp.typedText, comp.matcherDecl);
13702d9f4d1SDevajith   }
13802d9f4d1SDevajith   return QueryRef();
13902d9f4d1SDevajith }
14002d9f4d1SDevajith 
14102d9f4d1SDevajith QueryRef QueryParser::doParse() {
14202d9f4d1SDevajith 
14302d9f4d1SDevajith   llvm::StringRef commandStr;
14402d9f4d1SDevajith   ParsedQueryKind qKind =
14502d9f4d1SDevajith       LexOrCompleteWord<ParsedQueryKind>(this, commandStr)
14602d9f4d1SDevajith           .Case("", ParsedQueryKind::NoOp)
14702d9f4d1SDevajith           .Case("#", ParsedQueryKind::Comment, /*isCompletion=*/false)
14802d9f4d1SDevajith           .Case("help", ParsedQueryKind::Help)
14902d9f4d1SDevajith           .Case("m", ParsedQueryKind::Match, /*isCompletion=*/false)
15002d9f4d1SDevajith           .Case("match", ParsedQueryKind::Match)
15102d9f4d1SDevajith           .Case("q", ParsedQueryKind::Quit, /*IsCompletion=*/false)
15202d9f4d1SDevajith           .Case("quit", ParsedQueryKind::Quit)
15302d9f4d1SDevajith           .Default(ParsedQueryKind::Invalid);
15402d9f4d1SDevajith 
15502d9f4d1SDevajith   switch (qKind) {
15602d9f4d1SDevajith   case ParsedQueryKind::Comment:
15702d9f4d1SDevajith   case ParsedQueryKind::NoOp:
15802d9f4d1SDevajith     line = line.drop_until([](char c) { return c == '\n'; });
15902d9f4d1SDevajith     line = line.drop_while([](char c) { return c == '\n'; });
16002d9f4d1SDevajith     if (line.empty())
16102d9f4d1SDevajith       return new NoOpQuery;
16202d9f4d1SDevajith     return doParse();
16302d9f4d1SDevajith 
16402d9f4d1SDevajith   case ParsedQueryKind::Help:
16502d9f4d1SDevajith     return endQuery(new HelpQuery);
16602d9f4d1SDevajith 
16702d9f4d1SDevajith   case ParsedQueryKind::Quit:
16802d9f4d1SDevajith     return endQuery(new QuitQuery);
16902d9f4d1SDevajith 
17002d9f4d1SDevajith   case ParsedQueryKind::Match: {
17102d9f4d1SDevajith     if (completionPos) {
17202d9f4d1SDevajith       return completeMatcherExpression();
17302d9f4d1SDevajith     }
17402d9f4d1SDevajith 
17502d9f4d1SDevajith     matcher::internal::Diagnostics diag;
17602d9f4d1SDevajith     auto matcherSource = line.ltrim();
17702d9f4d1SDevajith     auto origMatcherSource = matcherSource;
17802d9f4d1SDevajith     std::optional<matcher::DynMatcher> matcher =
17902d9f4d1SDevajith         matcher::internal::Parser::parseMatcherExpression(
18002d9f4d1SDevajith             matcherSource, qs.getRegistryData(), &qs.namedValues, &diag);
18102d9f4d1SDevajith     if (!matcher) {
18202d9f4d1SDevajith       return makeInvalidQueryFromDiagnostics(diag);
18302d9f4d1SDevajith     }
184*5287a9b3SKazu Hirata     auto actualSource = origMatcherSource.substr(0, origMatcherSource.size() -
18502d9f4d1SDevajith                                                         matcherSource.size());
18602d9f4d1SDevajith     QueryRef query = new MatchQuery(actualSource, *matcher);
18702d9f4d1SDevajith     query->remainingContent = matcherSource;
18802d9f4d1SDevajith     return query;
18902d9f4d1SDevajith   }
19002d9f4d1SDevajith 
19102d9f4d1SDevajith   case ParsedQueryKind::Invalid:
19202d9f4d1SDevajith     return new InvalidQuery("unknown command: " + commandStr);
19302d9f4d1SDevajith   }
19402d9f4d1SDevajith 
19502d9f4d1SDevajith   llvm_unreachable("Invalid query kind");
19602d9f4d1SDevajith }
19702d9f4d1SDevajith 
19802d9f4d1SDevajith QueryRef QueryParser::parse(llvm::StringRef line, const QuerySession &qs) {
19902d9f4d1SDevajith   return QueryParser(line, qs).doParse();
20002d9f4d1SDevajith }
20102d9f4d1SDevajith 
20202d9f4d1SDevajith std::vector<llvm::LineEditor::Completion>
20302d9f4d1SDevajith QueryParser::complete(llvm::StringRef line, size_t pos,
20402d9f4d1SDevajith                       const QuerySession &qs) {
20502d9f4d1SDevajith   QueryParser queryParser(line, qs);
20602d9f4d1SDevajith   queryParser.completionPos = line.data() + pos;
20702d9f4d1SDevajith 
20802d9f4d1SDevajith   queryParser.doParse();
20902d9f4d1SDevajith   return queryParser.completions;
21002d9f4d1SDevajith }
21102d9f4d1SDevajith 
21202d9f4d1SDevajith } // namespace mlir::query
213