diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2014-02-01 01:42:46 +0000 |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2014-02-01 01:42:46 +0000 |
commit | e4cc48cfdf7c1848a91003f8049e1e1116bd3031 (patch) | |
tree | af870192ba5fe8ece6dc9ed8cda54e92d8120fb3 /clang-query | |
parent | f88a2f75ab1bedc9df5ed3db41e41eca493efaba (diff) |
Add completion to the query parser, and hook it up to clang-query.
Differential Revision: http://llvm-reviews.chandlerc.com/D2263
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@200604 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-query')
-rw-r--r-- | clang-query/QueryParser.cpp | 148 | ||||
-rw-r--r-- | clang-query/QueryParser.h | 51 | ||||
-rw-r--r-- | clang-query/tool/ClangQuery.cpp | 7 |
3 files changed, 160 insertions, 46 deletions
diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp index 9ef6c233..38903e3e 100644 --- a/clang-query/QueryParser.cpp +++ b/clang-query/QueryParser.cpp @@ -15,6 +15,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include <set> + using namespace llvm; using namespace clang::ast_matchers::dynamic; @@ -25,10 +27,10 @@ namespace query { // non-whitespace characters) from the start of region [Begin,End). If no word // is found before End, return StringRef(). Begin is adjusted to exclude the // lexed region. -static StringRef LexWord(const char *&Begin, const char *End) { +StringRef QueryParser::lexWord() { while (true) { if (Begin == End) - return StringRef(); + return StringRef(Begin, 0); if (!isWhitespace(*Begin)) break; @@ -46,8 +48,60 @@ static StringRef LexWord(const char *&Begin, const char *End) { } } -static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) { - unsigned Value = StringSwitch<unsigned>(ValStr) +// This is the StringSwitch-alike used by lexOrCompleteWord below. See that +// function for details. +template <typename T> struct QueryParser::LexOrCompleteWord { + StringSwitch<T> Switch; + + QueryParser *P; + StringRef Word; + // Set to the completion point offset in Word, or StringRef::npos if + // completion point not in Word. + size_t WordCompletionPos; + + LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP) + : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {} + + template <unsigned N> + LexOrCompleteWord &Case(const char (&S)[N], const T &Value, + bool IsCompletion = true) { + StringRef CaseStr(S, N - 1); + + if (WordCompletionPos == StringRef::npos) + Switch.Case(S, Value); + else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() && + CaseStr.substr(0, WordCompletionPos) == + Word.substr(0, WordCompletionPos)) + P->Completions.push_back(LineEditor::Completion( + (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr)); + return *this; + } + + T Default(const T& Value) const { + return Switch.Default(Value); + } +}; + +// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object +// that can be used like a llvm::StringSwitch<T>, but adds cases as possible +// completions if the lexed word contains the completion point. +template <typename T> +QueryParser::LexOrCompleteWord<T> +QueryParser::lexOrCompleteWord(StringRef &Word) { + Word = lexWord(); + size_t WordCompletionPos = StringRef::npos; + if (CompletionPos && CompletionPos <= Word.data() + Word.size()) { + if (CompletionPos < Word.data()) + WordCompletionPos = 0; + else + WordCompletionPos = CompletionPos - Word.data(); + } + return LexOrCompleteWord<T>(this, Word, WordCompletionPos); +} + +QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) { + StringRef ValStr; + unsigned Value = lexOrCompleteWord<unsigned>(ValStr) .Case("false", 0) .Case("true", 1) .Default(~0u); @@ -57,8 +111,9 @@ static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) { return new SetQuery<bool>(Var, Value); } -static QueryRef ParseSetOutputKind(StringRef ValStr) { - unsigned OutKind = StringSwitch<unsigned>(ValStr) +QueryRef QueryParser::parseSetOutputKind() { + StringRef ValStr; + unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr) .Case("diag", OK_Diag) .Case("print", OK_Print) .Case("dump", OK_Dump) @@ -70,9 +125,9 @@ static QueryRef ParseSetOutputKind(StringRef ValStr) { return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind)); } -static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) { +QueryRef QueryParser::endQuery(QueryRef Q) { const char *Extra = Begin; - if (!LexWord(Begin, End).empty()) + if (!lexWord().empty()) return new InvalidQuery("unexpected extra input: '" + StringRef(Extra, End - Extra) + "'"); return Q; @@ -92,15 +147,12 @@ enum ParsedQueryVariable { PQV_BindRoot }; -QueryRef ParseQuery(StringRef Line) { - const char *Begin = Line.data(); - const char *End = Line.data() + Line.size(); - - StringRef CommandStr = LexWord(Begin, End); - ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr) +QueryRef QueryParser::doParse() { + StringRef CommandStr; + ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr) .Case("", PQK_NoOp) .Case("help", PQK_Help) - .Case("m", PQK_Match) + .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) .Case("set", PQK_Set) .Default(PQK_Invalid); @@ -110,50 +162,57 @@ QueryRef ParseQuery(StringRef Line) { return new NoOpQuery; case PQK_Help: - return EndQuery(Begin, End, new HelpQuery); + return endQuery(new HelpQuery); case PQK_Match: { - Diagnostics Diag; - Optional<DynTypedMatcher> Matcher = - Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag); - if (!Matcher) { - std::string ErrStr; - llvm::raw_string_ostream OS(ErrStr); - Diag.printToStreamFull(OS); - return new InvalidQuery(OS.str()); + if (CompletionPos) { + std::vector<MatcherCompletion> Comps = Parser::completeExpression( + StringRef(Begin, End - Begin), CompletionPos - Begin); + for (std::vector<MatcherCompletion>::iterator I = Comps.begin(), + E = Comps.end(); + I != E; ++I) { + Completions.push_back( + LineEditor::Completion(I->TypedText, I->MatcherDecl)); + } + return QueryRef(); + } else { + Diagnostics Diag; + Optional<DynTypedMatcher> Matcher = + Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag); + if (!Matcher) { + std::string ErrStr; + llvm::raw_string_ostream OS(ErrStr); + Diag.printToStreamFull(OS); + return new InvalidQuery(OS.str()); + } + return new MatchQuery(*Matcher); } - return new MatchQuery(*Matcher); } case PQK_Set: { - StringRef VarStr = LexWord(Begin, End); + StringRef VarStr; + ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr) + .Case("output", PQV_Output) + .Case("bind-root", PQV_BindRoot) + .Default(PQV_Invalid); if (VarStr.empty()) return new InvalidQuery("expected variable name"); - - ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr) - .Case("output", PQV_Output) - .Case("bind-root", PQV_BindRoot) - .Default(PQV_Invalid); if (Var == PQV_Invalid) return new InvalidQuery("unknown variable: '" + VarStr + "'"); - StringRef ValStr = LexWord(Begin, End); - if (ValStr.empty()) - return new InvalidQuery("expected variable value"); - QueryRef Q; switch (Var) { case PQV_Output: - Q = ParseSetOutputKind(ValStr); + Q = parseSetOutputKind(); break; case PQV_BindRoot: - Q = ParseSetBool(&QuerySession::BindRoot, ValStr); + Q = parseSetBool(&QuerySession::BindRoot); break; case PQV_Invalid: llvm_unreachable("Invalid query kind"); } - return EndQuery(Begin, End, Q); + return endQuery(Q); } case PQK_Invalid: @@ -163,5 +222,18 @@ QueryRef ParseQuery(StringRef Line) { llvm_unreachable("Invalid query kind"); } +QueryRef QueryParser::parse(StringRef Line) { + return QueryParser(Line).doParse(); +} + +std::vector<LineEditor::Completion> QueryParser::complete(StringRef Line, + size_t Pos) { + QueryParser P(Line); + P.CompletionPos = Line.data() + Pos; + + P.doParse(); + return P.Completions; +} + } // namespace query } // namespace clang diff --git a/clang-query/QueryParser.h b/clang-query/QueryParser.h index 00c95fcf..cfba0266 100644 --- a/clang-query/QueryParser.h +++ b/clang-query/QueryParser.h @@ -12,14 +12,55 @@ #include "Query.h" +#include <stddef.h> +#include "llvm/LineEditor/LineEditor.h" + namespace clang { namespace query { -/// \brief Parse \p Line. -/// -/// \return A reference to the parsed query object, which may be an -/// \c InvalidQuery if a parse error occurs. -QueryRef ParseQuery(StringRef Line); +class QuerySession; + +class QueryParser { +public: + /// Parse \param Line as a query. + /// + /// \return A QueryRef representing the query, which may be an InvalidQuery. + static QueryRef parse(StringRef Line); + + /// Compute a list of completions for \param Line assuming a cursor at + /// \param Pos characters past the start of \param Line, ordered from most + /// likely to least likely. + /// + /// \return A vector of completions for \param Line. + static std::vector<llvm::LineEditor::Completion> complete(StringRef Line, + size_t Pos); + +private: + QueryParser(StringRef Line) + : Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0) {} + + StringRef lexWord(); + + template <typename T> struct LexOrCompleteWord; + template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str); + + QueryRef parseSetBool(bool QuerySession::*Var); + QueryRef parseSetOutputKind(); + + QueryRef endQuery(QueryRef Q); + + /// \brief Parse [\p Begin,\p End). + /// + /// \return A reference to the parsed query object, which may be an + /// \c InvalidQuery if a parse error occurs. + QueryRef doParse(); + + const char *Begin; + const char *End; + + const char *CompletionPos; + std::vector<llvm::LineEditor::Completion> Completions; +}; } // namespace query } // namespace clang diff --git a/clang-query/tool/ClangQuery.cpp b/clang-query/tool/ClangQuery.cpp index 0150d416..f9b62e08 100644 --- a/clang-query/tool/ClangQuery.cpp +++ b/clang-query/tool/ClangQuery.cpp @@ -96,7 +96,7 @@ int main(int argc, const char **argv) { for (cl::list<std::string>::iterator I = Commands.begin(), E = Commands.end(); I != E; ++I) { - QueryRef Q = ParseQuery(I->c_str()); + QueryRef Q = QueryParser::parse(I->c_str()); if (!Q->run(llvm::outs(), QS)) return 1; } @@ -113,15 +113,16 @@ int main(int argc, const char **argv) { std::string Line; std::getline(Input, Line); - QueryRef Q = ParseQuery(Line.c_str()); + QueryRef Q = QueryParser::parse(Line.c_str()); if (!Q->run(llvm::outs(), QS)) return 1; } } } else { LineEditor LE("clang-query"); + LE.setListCompleter(QueryParser::complete); while (llvm::Optional<std::string> Line = LE.readLine()) { - QueryRef Q = ParseQuery(*Line); + QueryRef Q = QueryParser::parse(*Line); Q->run(llvm::outs(), QS); } } |