Mercurial > hg > CbC > CbC_llvm
diff clang-tools-extra/clang-query/QueryParser.cpp @ 150:1d019706d866
LLVM10
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 15:10:13 +0900 |
parents | |
children | 0572611fdcc8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clang-tools-extra/clang-query/QueryParser.cpp Thu Feb 13 15:10:13 2020 +0900 @@ -0,0 +1,351 @@ +//===---- QueryParser.cpp - clang-query command parser --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "QueryParser.h" +#include "Query.h" +#include "QuerySession.h" +#include "clang/ASTMatchers/Dynamic/Parser.h" +#include "clang/Basic/CharInfo.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include <set> + +using namespace llvm; +using namespace clang::ast_matchers::dynamic; + +namespace clang { +namespace query { + +// Lex any amount of whitespace followed by a "word" (any sequence of +// 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. +StringRef QueryParser::lexWord() { + Line = Line.drop_while([](char c) { + // Don't trim newlines. + return StringRef(" \t\v\f\r").contains(c); + }); + + if (Line.empty()) + // Even though the Line is empty, it contains a pointer and + // a (zero) length. The pointer is used in the LexOrCompleteWord + // code completion. + return Line; + + StringRef Word; + if (Line.front() == '#') + Word = Line.substr(0, 1); + else + Word = Line.take_until(isWhitespace); + + Line = Line.drop_front(Word.size()); + return Word; +} + +// This is the StringSwitch-alike used by lexOrCompleteWord below. See that +// function for details. +template <typename T> struct QueryParser::LexOrCompleteWord { + StringRef Word; + StringSwitch<T> Switch; + + QueryParser *P; + // Set to the completion point offset in Word, or StringRef::npos if + // completion point not in Word. + size_t WordCompletionPos; + + // 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. + LexOrCompleteWord(QueryParser *P, StringRef &OutWord) + : Word(P->lexWord()), Switch(Word), P(P), + WordCompletionPos(StringRef::npos) { + OutWord = Word; + if (P->CompletionPos && P->CompletionPos <= Word.data() + Word.size()) { + if (P->CompletionPos < Word.data()) + WordCompletionPos = 0; + else + WordCompletionPos = P->CompletionPos - Word.data(); + } + } + + LexOrCompleteWord &Case(llvm::StringLiteral CaseStr, const T &Value, + bool IsCompletion = true) { + + if (WordCompletionPos == StringRef::npos) + Switch.Case(CaseStr, Value); + else if (CaseStr.size() != 0 && IsCompletion && WordCompletionPos <= CaseStr.size() && + CaseStr.substr(0, WordCompletionPos) == + Word.substr(0, WordCompletionPos)) + P->Completions.push_back(LineEditor::Completion( + (CaseStr.substr(WordCompletionPos) + " ").str(), + std::string(CaseStr))); + return *this; + } + + T Default(T Value) { return Switch.Default(Value); } +}; + +QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) { + StringRef ValStr; + unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr) + .Case("false", 0) + .Case("true", 1) + .Default(~0u); + if (Value == ~0u) { + return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'"); + } + return new SetQuery<bool>(Var, Value); +} + +template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() { + StringRef ValStr; + unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr) + .Case("diag", OK_Diag) + .Case("print", OK_Print) + .Case("detailed-ast", OK_DetailedAST) + .Case("dump", OK_DetailedAST) + .Default(~0u); + if (OutKind == ~0u) { + return new InvalidQuery( + "expected 'diag', 'print', 'detailed-ast' or 'dump', got '" + ValStr + + "'"); + } + + switch (OutKind) { + case OK_DetailedAST: + return new QueryType(&QuerySession::DetailedASTOutput); + case OK_Diag: + return new QueryType(&QuerySession::DiagOutput); + case OK_Print: + return new QueryType(&QuerySession::PrintOutput); + } + + llvm_unreachable("Invalid output kind"); +} + +QueryRef QueryParser::endQuery(QueryRef Q) { + StringRef Extra = Line; + StringRef ExtraTrimmed = Extra.drop_while( + [](char c) { return StringRef(" \t\v\f\r").contains(c); }); + + if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') || + (ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' && + ExtraTrimmed[1] == '\n')) + Q->RemainingContent = Extra; + else { + StringRef TrailingWord = lexWord(); + if (!TrailingWord.empty() && TrailingWord.front() == '#') { + Line = Line.drop_until([](char c) { return c == '\n'; }); + Line = Line.drop_while([](char c) { return c == '\n'; }); + return endQuery(Q); + } + if (!TrailingWord.empty()) { + return new InvalidQuery("unexpected extra input: '" + Extra + "'"); + } + } + return Q; +} + +namespace { + +enum ParsedQueryKind { + PQK_Invalid, + PQK_Comment, + PQK_NoOp, + PQK_Help, + PQK_Let, + PQK_Match, + PQK_Set, + PQK_Unlet, + PQK_Quit, + PQK_Enable, + PQK_Disable +}; + +enum ParsedQueryVariable { + PQV_Invalid, + PQV_Output, + PQV_BindRoot, + PQV_PrintMatcher +}; + +QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) { + std::string ErrStr; + llvm::raw_string_ostream OS(ErrStr); + Diag.printToStreamFull(OS); + return new InvalidQuery(OS.str()); +} + +} // namespace + +QueryRef QueryParser::completeMatcherExpression() { + std::vector<MatcherCompletion> Comps = Parser::completeExpression( + Line, CompletionPos - Line.begin(), nullptr, &QS.NamedValues); + for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) { + Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl)); + } + return QueryRef(); +} + +QueryRef QueryParser::doParse() { + StringRef CommandStr; + ParsedQueryKind QKind = LexOrCompleteWord<ParsedQueryKind>(this, CommandStr) + .Case("", PQK_NoOp) + .Case("#", PQK_Comment, /*IsCompletion=*/false) + .Case("help", PQK_Help) + .Case("l", PQK_Let, /*IsCompletion=*/false) + .Case("let", PQK_Let) + .Case("m", PQK_Match, /*IsCompletion=*/false) + .Case("match", PQK_Match) + .Case("q", PQK_Quit, /*IsCompletion=*/false) + .Case("quit", PQK_Quit) + .Case("set", PQK_Set) + .Case("enable", PQK_Enable) + .Case("disable", PQK_Disable) + .Case("unlet", PQK_Unlet) + .Default(PQK_Invalid); + + switch (QKind) { + case PQK_Comment: + case PQK_NoOp: + Line = Line.drop_until([](char c) { return c == '\n'; }); + Line = Line.drop_while([](char c) { return c == '\n'; }); + if (Line.empty()) + return new NoOpQuery; + return doParse(); + + case PQK_Help: + return endQuery(new HelpQuery); + + case PQK_Quit: + return endQuery(new QuitQuery); + + case PQK_Let: { + StringRef Name = lexWord(); + + if (Name.empty()) + return new InvalidQuery("expected variable name"); + + if (CompletionPos) + return completeMatcherExpression(); + + Diagnostics Diag; + ast_matchers::dynamic::VariantValue Value; + if (!Parser::parseExpression(Line, nullptr, &QS.NamedValues, &Value, + &Diag)) { + return makeInvalidQueryFromDiagnostics(Diag); + } + + auto *Q = new LetQuery(Name, Value); + Q->RemainingContent = Line; + return Q; + } + + case PQK_Match: { + if (CompletionPos) + return completeMatcherExpression(); + + Diagnostics Diag; + auto MatcherSource = Line.ltrim(); + auto OrigMatcherSource = MatcherSource; + Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression( + MatcherSource, nullptr, &QS.NamedValues, &Diag); + if (!Matcher) { + return makeInvalidQueryFromDiagnostics(Diag); + } + auto ActualSource = OrigMatcherSource.slice(0, OrigMatcherSource.size() - + MatcherSource.size()); + auto *Q = new MatchQuery(ActualSource, *Matcher); + Q->RemainingContent = MatcherSource; + return Q; + } + + case PQK_Set: { + StringRef VarStr; + ParsedQueryVariable Var = + LexOrCompleteWord<ParsedQueryVariable>(this, VarStr) + .Case("output", PQV_Output) + .Case("bind-root", PQV_BindRoot) + .Case("print-matcher", PQV_PrintMatcher) + .Default(PQV_Invalid); + if (VarStr.empty()) + return new InvalidQuery("expected variable name"); + if (Var == PQV_Invalid) + return new InvalidQuery("unknown variable: '" + VarStr + "'"); + + QueryRef Q; + switch (Var) { + case PQV_Output: + Q = parseSetOutputKind<SetExclusiveOutputQuery>(); + break; + case PQV_BindRoot: + Q = parseSetBool(&QuerySession::BindRoot); + break; + case PQV_PrintMatcher: + Q = parseSetBool(&QuerySession::PrintMatcher); + break; + case PQV_Invalid: + llvm_unreachable("Invalid query kind"); + } + + return endQuery(Q); + } + case PQK_Enable: + case PQK_Disable: { + StringRef VarStr; + ParsedQueryVariable Var = + LexOrCompleteWord<ParsedQueryVariable>(this, VarStr) + .Case("output", PQV_Output) + .Default(PQV_Invalid); + if (VarStr.empty()) + return new InvalidQuery("expected variable name"); + if (Var == PQV_Invalid) + return new InvalidQuery("unknown variable: '" + VarStr + "'"); + + QueryRef Q; + + if (QKind == PQK_Enable) + Q = parseSetOutputKind<EnableOutputQuery>(); + else if (QKind == PQK_Disable) + Q = parseSetOutputKind<DisableOutputQuery>(); + else + llvm_unreachable("Invalid query kind"); + return endQuery(Q); + } + + case PQK_Unlet: { + StringRef Name = lexWord(); + + if (Name.empty()) + return new InvalidQuery("expected variable name"); + + return endQuery(new LetQuery(Name, VariantValue())); + } + + case PQK_Invalid: + return new InvalidQuery("unknown command: " + CommandStr); + } + + llvm_unreachable("Invalid query kind"); +} + +QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) { + return QueryParser(Line, QS).doParse(); +} + +std::vector<LineEditor::Completion> +QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) { + QueryParser P(Line, QS); + P.CompletionPos = Line.data() + Pos; + + P.doParse(); + return P.Completions; +} + +} // namespace query +} // namespace clang