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