1 //===---- QueryParser.cpp - clang-query command parser --------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "QueryParser.h" 11 #include "Query.h" 12 #include "QuerySession.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "llvm/ADT/StringSwitch.h" 15 #include "clang/ASTMatchers/Dynamic/Parser.h" 16 #include "clang/Basic/CharInfo.h" 17 18 using namespace llvm; 19 using namespace clang::ast_matchers::dynamic; 20 21 namespace clang { 22 namespace query { 23 24 // Lex any amount of whitespace followed by a "word" (any sequence of 25 // non-whitespace characters) from the start of region [Begin,End). If no word 26 // is found before End, return StringRef(). Begin is adjusted to exclude the 27 // lexed region. 28 static StringRef LexWord(const char *&Begin, const char *End) { 29 while (true) { 30 if (Begin == End) 31 return StringRef(); 32 33 if (!isWhitespace(*Begin)) 34 break; 35 36 ++Begin; 37 } 38 39 const char *WordBegin = Begin; 40 41 while (true) { 42 ++Begin; 43 44 if (Begin == End || isWhitespace(*Begin)) 45 return StringRef(WordBegin, Begin - WordBegin); 46 } 47 } 48 49 static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) { 50 unsigned Value = StringSwitch<unsigned>(ValStr) 51 .Case("false", 0) 52 .Case("true", 1) 53 .Default(~0u); 54 if (Value == ~0u) { 55 return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'"); 56 } 57 return new SetQuery<bool>(Var, Value); 58 } 59 60 static QueryRef ParseSetOutputKind(StringRef ValStr) { 61 unsigned OutKind = StringSwitch<unsigned>(ValStr) 62 .Case("diag", OK_Diag) 63 .Case("print", OK_Print) 64 .Case("dump", OK_Dump) 65 .Default(~0u); 66 if (OutKind == ~0u) { 67 return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" + 68 ValStr + "'"); 69 } 70 return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind)); 71 } 72 73 static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) { 74 const char *Extra = Begin; 75 if (!LexWord(Begin, End).empty()) 76 return new InvalidQuery("unexpected extra input: '" + 77 StringRef(Extra, End - Extra) + "'"); 78 return Q; 79 } 80 81 enum ParsedQueryKind { 82 PQK_Invalid, 83 PQK_NoOp, 84 PQK_Help, 85 PQK_Match, 86 PQK_Set 87 }; 88 89 enum ParsedQueryVariable { 90 PQV_Invalid, 91 PQV_Output, 92 PQV_BindRoot 93 }; 94 95 QueryRef ParseQuery(StringRef Line) { 96 const char *Begin = Line.data(); 97 const char *End = Line.data() + Line.size(); 98 99 StringRef CommandStr = LexWord(Begin, End); 100 ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr) 101 .Case("", PQK_NoOp) 102 .Case("help", PQK_Help) 103 .Case("m", PQK_Match) 104 .Case("match", PQK_Match) 105 .Case("set", PQK_Set) 106 .Default(PQK_Invalid); 107 108 switch (QKind) { 109 case PQK_NoOp: 110 return new NoOpQuery; 111 112 case PQK_Help: 113 return EndQuery(Begin, End, new HelpQuery); 114 115 case PQK_Match: { 116 Diagnostics Diag; 117 Optional<DynTypedMatcher> Matcher = 118 Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag); 119 if (!Matcher) { 120 std::string ErrStr; 121 llvm::raw_string_ostream OS(ErrStr); 122 Diag.printToStreamFull(OS); 123 return new InvalidQuery(OS.str()); 124 } 125 return new MatchQuery(*Matcher); 126 } 127 128 case PQK_Set: { 129 StringRef VarStr = LexWord(Begin, End); 130 if (VarStr.empty()) 131 return new InvalidQuery("expected variable name"); 132 133 ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr) 134 .Case("output", PQV_Output) 135 .Case("bind-root", PQV_BindRoot) 136 .Default(PQV_Invalid); 137 if (Var == PQV_Invalid) 138 return new InvalidQuery("unknown variable: '" + VarStr + "'"); 139 140 StringRef ValStr = LexWord(Begin, End); 141 if (ValStr.empty()) 142 return new InvalidQuery("expected variable value"); 143 144 QueryRef Q; 145 switch (Var) { 146 case PQV_Output: 147 Q = ParseSetOutputKind(ValStr); 148 break; 149 case PQV_BindRoot: 150 Q = ParseSetBool(&QuerySession::BindRoot, ValStr); 151 break; 152 case PQV_Invalid: 153 llvm_unreachable("Invalid query kind"); 154 } 155 156 return EndQuery(Begin, End, Q); 157 } 158 159 case PQK_Invalid: 160 return new InvalidQuery("unknown command: " + CommandStr); 161 } 162 163 llvm_unreachable("Invalid query kind"); 164 } 165 166 } // namespace query 167 } // namespace clang 168