213 lines · cpp
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-exception6//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 of15// non-whitespace characters) from the start of region [begin,end). If no word16// is found before end, return StringRef(). begin is adjusted to exclude the17// lexed region.18llvm::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 and24 // a (zero) length. The pointer is used in the LexOrCompleteWord25 // 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 that43// 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 if51 // completion point not in word.52 size_t wordCompletionPos;53 54 // Lexes a word and stores it in word. Returns a LexOrCompleteword<T> object55 // that can be used like a llvm::StringSwitch<T>, but adds cases as possible56 // 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 else66 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 90QueryRef QueryParser::endQuery(QueryRef queryRef) {91 llvm::StringRef extra = line;92 llvm::StringRef extraTrimmed = extra.ltrim(" \t\v\f\r");93 94 if (extraTrimmed.starts_with('\n') || extraTrimmed.starts_with("\r\n"))95 queryRef->remainingContent = extra;96 else {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);102 }103 if (!trailingWord.empty()) {104 return new InvalidQuery("unexpected extra input: '" + extra + "'");105 }106 }107 return queryRef;108}109 110namespace {111 112enum class ParsedQueryKind {113 Invalid,114 Comment,115 NoOp,116 Help,117 Match,118 Quit,119};120 121QueryRef122makeInvalidQueryFromDiagnostics(const matcher::internal::Diagnostics &diag) {123 std::string errStr;124 llvm::raw_string_ostream os(errStr);125 diag.print(os);126 return new InvalidQuery(errStr);127}128} // namespace129 130QueryRef QueryParser::completeMatcherExpression() {131 std::vector<matcher::MatcherCompletion> comps =132 matcher::internal::Parser::completeExpression(133 line, completionPos - line.begin(), qs.getRegistryData(),134 &qs.namedValues);135 for (const auto &comp : comps) {136 completions.emplace_back(comp.typedText, comp.matcherDecl);137 }138 return QueryRef();139}140 141QueryRef QueryParser::doParse() {142 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);154 155 switch (qKind) {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'; });160 if (line.empty())161 return new NoOpQuery;162 return doParse();163 164 case ParsedQueryKind::Help:165 return endQuery(new HelpQuery);166 167 case ParsedQueryKind::Quit:168 return endQuery(new QuitQuery);169 170 case ParsedQueryKind::Match: {171 if (completionPos) {172 return completeMatcherExpression();173 }174 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);181 if (!matcher) {182 return makeInvalidQueryFromDiagnostics(diag);183 }184 auto actualSource = origMatcherSource.substr(0, origMatcherSource.size() -185 matcherSource.size());186 QueryRef query = new MatchQuery(actualSource, *matcher);187 query->remainingContent = matcherSource;188 return query;189 }190 191 case ParsedQueryKind::Invalid:192 return new InvalidQuery("unknown command: " + commandStr);193 }194 195 llvm_unreachable("Invalid query kind");196}197 198QueryRef QueryParser::parse(llvm::StringRef line, const QuerySession &qs) {199 return QueryParser(line, qs).doParse();200}201 202std::vector<llvm::LineEditor::Completion>203QueryParser::complete(llvm::StringRef line, size_t pos,204 const QuerySession &qs) {205 QueryParser queryParser(line, qs);206 queryParser.completionPos = line.data() + pos;207 208 queryParser.doParse();209 return queryParser.completions;210}211 212} // namespace mlir::query213