blob: 7a93e3bfa8c5b27f3f45c8894316b560b39f7b14 [file] [log] [blame]
//===---- QueryParser.cpp - clang-query command parser --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "clang/ASTMatchers/Dynamic/Parser.h"
#include "clang/Basic/CharInfo.h"
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.
static StringRef LexWord(const char *&Begin, const char *End) {
while (true) {
if (Begin == End)
return StringRef();
if (!isWhitespace(*Begin))
break;
++Begin;
}
const char *WordBegin = Begin;
while (true) {
++Begin;
if (Begin == End || isWhitespace(*Begin))
return StringRef(WordBegin, Begin - WordBegin);
}
}
static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
unsigned Value = StringSwitch<unsigned>(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);
}
static QueryRef ParseSetOutputKind(StringRef ValStr) {
unsigned OutKind = StringSwitch<unsigned>(ValStr)
.Case("diag", OK_Diag)
.Case("print", OK_Print)
.Case("dump", OK_Dump)
.Default(~0u);
if (OutKind == ~0u) {
return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
ValStr + "'");
}
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
}
static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
const char *Extra = Begin;
if (!LexWord(Begin, End).empty())
return new InvalidQuery("unexpected extra input: '" +
StringRef(Extra, End - Extra) + "'");
return Q;
}
enum ParsedQueryKind {
PQK_Invalid,
PQK_NoOp,
PQK_Help,
PQK_Match,
PQK_Set
};
enum ParsedQueryVariable {
PQV_Invalid,
PQV_Output,
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)
.Case("", PQK_NoOp)
.Case("help", PQK_Help)
.Case("m", PQK_Match)
.Case("match", PQK_Match)
.Case("set", PQK_Set)
.Default(PQK_Invalid);
switch (QKind) {
case PQK_NoOp:
return new NoOpQuery;
case PQK_Help:
return EndQuery(Begin, End, 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());
}
return new MatchQuery(*Matcher);
}
case PQK_Set: {
StringRef VarStr = LexWord(Begin, End);
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);
break;
case PQV_BindRoot:
Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
break;
case PQV_Invalid:
llvm_unreachable("Invalid query kind");
}
return EndQuery(Begin, End, Q);
}
case PQK_Invalid:
return new InvalidQuery("unknown command: " + CommandStr);
}
llvm_unreachable("Invalid query kind");
}
} // namespace query
} // namespace clang