blob: 7f027cfbead4fb13d7381f641ed5e6a4fb873382 [file] [log] [blame]
Manuel Klimek9a05fa92011-04-27 16:39:14 +00001//===--- JsonCompileCommandLineDatabase.cpp - Simple JSON database --------===//
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// This file implements reading a compile command line database, as written
11// out for example by CMake.
12//
13//===----------------------------------------------------------------------===//
14
15#include "JsonCompileCommandLineDatabase.h"
16#include "llvm/ADT/Twine.h"
17
18namespace clang {
19namespace tooling {
20
21namespace {
22
23// A parser for JSON escaped strings of command line arguments with \-escaping
24// for quoted arguments (see the documentation of UnescapeJsonCommandLine(...)).
25class CommandLineArgumentParser {
26 public:
27 CommandLineArgumentParser(llvm::StringRef CommandLine)
28 : Input(CommandLine), Position(Input.begin()-1) {}
29
30 std::vector<std::string> Parse() {
31 bool HasMoreInput = true;
32 while (HasMoreInput && NextNonWhitespace()) {
33 std::string Argument;
34 HasMoreInput = ParseStringInto(Argument);
35 CommandLine.push_back(Argument);
36 }
37 return CommandLine;
38 }
39
40 private:
41 // All private methods return true if there is more input available.
42
43 bool ParseStringInto(std::string &String) {
44 do {
45 if (*Position == '"') {
46 if (!ParseQuotedStringInto(String)) return false;
47 } else {
48 if (!ParseFreeStringInto(String)) return false;
49 }
50 } while (*Position != ' ');
51 return true;
52 }
53
54 bool ParseQuotedStringInto(std::string &String) {
55 if (!Next()) return false;
56 while (*Position != '"') {
57 if (!SkipEscapeCharacter()) return false;
58 String.push_back(*Position);
59 if (!Next()) return false;
60 }
61 return Next();
62 }
63
64 bool ParseFreeStringInto(std::string &String) {
65 do {
66 if (!SkipEscapeCharacter()) return false;
67 String.push_back(*Position);
68 if (!Next()) return false;
69 } while (*Position != ' ' && *Position != '"');
70 return true;
71 }
72
73 bool SkipEscapeCharacter() {
74 if (*Position == '\\') {
75 return Next();
76 }
77 return true;
78 }
79
80 bool NextNonWhitespace() {
81 do {
82 if (!Next()) return false;
83 } while (*Position == ' ');
84 return true;
85 }
86
87 bool Next() {
88 ++Position;
89 if (Position == Input.end()) return false;
90 // Remove the JSON escaping first. This is done unconditionally.
91 if (*Position == '\\') ++Position;
92 return Position != Input.end();
93 }
94
95 const llvm::StringRef Input;
96 llvm::StringRef::iterator Position;
97 std::vector<std::string> CommandLine;
98};
99
100} // end namespace
101
102std::vector<std::string> UnescapeJsonCommandLine(
103 llvm::StringRef JsonEscapedCommandLine) {
104 CommandLineArgumentParser parser(JsonEscapedCommandLine);
105 return parser.Parse();
106}
107
108JsonCompileCommandLineParser::JsonCompileCommandLineParser(
109 const llvm::StringRef Input, CompileCommandHandler *CommandHandler)
110 : Input(Input), Position(Input.begin()-1), CommandHandler(CommandHandler) {}
111
112bool JsonCompileCommandLineParser::Parse() {
113 NextNonWhitespace();
114 return ParseTranslationUnits();
115}
116
117std::string JsonCompileCommandLineParser::GetErrorMessage() const {
118 return ErrorMessage;
119}
120
121bool JsonCompileCommandLineParser::ParseTranslationUnits() {
122 if (!ConsumeOrError('[', "at start of compile command file")) return false;
123 if (!ParseTranslationUnit(/*First=*/true)) return false;
124 while (Consume(',')) {
125 if (!ParseTranslationUnit(/*First=*/false)) return false;
126 }
127 if (!ConsumeOrError(']', "at end of array")) return false;
128 if (CommandHandler != NULL) {
129 CommandHandler->EndTranslationUnits();
130 }
131 return true;
132}
133
134bool JsonCompileCommandLineParser::ParseTranslationUnit(bool First) {
135 if (First) {
136 if (!Consume('{')) return true;
137 } else {
138 if (!ConsumeOrError('{', "at start of object")) return false;
139 }
140 if (!Consume('}')) {
141 if (!ParseObjectKeyValuePairs()) return false;
142 if (!ConsumeOrError('}', "at end of object")) return false;
143 }
144 if (CommandHandler != NULL) {
145 CommandHandler->EndTranslationUnit();
146 }
147 return true;
148}
149
150bool JsonCompileCommandLineParser::ParseObjectKeyValuePairs() {
151 do {
152 llvm::StringRef Key;
153 if (!ParseString(Key)) return false;
154 if (!ConsumeOrError(':', "between name and value")) return false;
155 llvm::StringRef Value;
156 if (!ParseString(Value)) return false;
157 if (CommandHandler != NULL) {
158 CommandHandler->HandleKeyValue(Key, Value);
159 }
160 } while (Consume(','));
161 return true;
162}
163
164bool JsonCompileCommandLineParser::ParseString(llvm::StringRef &String) {
165 if (!ConsumeOrError('"', "at start of string")) return false;
166 llvm::StringRef::iterator First = Position;
167 llvm::StringRef::iterator Last = Position;
168 while (!Consume('"')) {
169 Consume('\\');
170 ++Position;
171 // We need to store Position, as Consume will change Last before leaving
172 // the loop.
173 Last = Position;
174 }
175 String = llvm::StringRef(First, Last - First);
176 return true;
177}
178
179bool JsonCompileCommandLineParser::Consume(char C) {
180 if (Position == Input.end()) return false;
181 if (*Position != C) return false;
182 NextNonWhitespace();
183 return true;
184}
185
186bool JsonCompileCommandLineParser::ConsumeOrError(
187 char C, llvm::StringRef Message) {
188 if (!Consume(C)) {
189 SetExpectError(C, Message);
190 return false;
191 }
192 return true;
193}
194
195void JsonCompileCommandLineParser::SetExpectError(
196 char C, llvm::StringRef Message) {
197 ErrorMessage = (llvm::Twine("'") + llvm::StringRef(&C, 1) +
198 "' expected " + Message + ".").str();
199}
200
201void JsonCompileCommandLineParser::NextNonWhitespace() {
202 do {
203 ++Position;
204 } while (IsWhitespace());
205}
206
207bool JsonCompileCommandLineParser::IsWhitespace() {
208 if (Position == Input.end()) return false;
209 return (*Position == ' ' || *Position == '\t' ||
210 *Position == '\n' || *Position == '\r');
211}
212
213} // end namespace tooling
214} // end namespace clang