blob: 7f027cfbead4fb13d7381f641ed5e6a4fb873382 [file] [log] [blame]
//===--- JsonCompileCommandLineDatabase.cpp - Simple JSON database --------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements reading a compile command line database, as written
// out for example by CMake.
//
//===----------------------------------------------------------------------===//
#include "JsonCompileCommandLineDatabase.h"
#include "llvm/ADT/Twine.h"
namespace clang {
namespace tooling {
namespace {
// A parser for JSON escaped strings of command line arguments with \-escaping
// for quoted arguments (see the documentation of UnescapeJsonCommandLine(...)).
class CommandLineArgumentParser {
public:
CommandLineArgumentParser(llvm::StringRef CommandLine)
: Input(CommandLine), Position(Input.begin()-1) {}
std::vector<std::string> Parse() {
bool HasMoreInput = true;
while (HasMoreInput && NextNonWhitespace()) {
std::string Argument;
HasMoreInput = ParseStringInto(Argument);
CommandLine.push_back(Argument);
}
return CommandLine;
}
private:
// All private methods return true if there is more input available.
bool ParseStringInto(std::string &String) {
do {
if (*Position == '"') {
if (!ParseQuotedStringInto(String)) return false;
} else {
if (!ParseFreeStringInto(String)) return false;
}
} while (*Position != ' ');
return true;
}
bool ParseQuotedStringInto(std::string &String) {
if (!Next()) return false;
while (*Position != '"') {
if (!SkipEscapeCharacter()) return false;
String.push_back(*Position);
if (!Next()) return false;
}
return Next();
}
bool ParseFreeStringInto(std::string &String) {
do {
if (!SkipEscapeCharacter()) return false;
String.push_back(*Position);
if (!Next()) return false;
} while (*Position != ' ' && *Position != '"');
return true;
}
bool SkipEscapeCharacter() {
if (*Position == '\\') {
return Next();
}
return true;
}
bool NextNonWhitespace() {
do {
if (!Next()) return false;
} while (*Position == ' ');
return true;
}
bool Next() {
++Position;
if (Position == Input.end()) return false;
// Remove the JSON escaping first. This is done unconditionally.
if (*Position == '\\') ++Position;
return Position != Input.end();
}
const llvm::StringRef Input;
llvm::StringRef::iterator Position;
std::vector<std::string> CommandLine;
};
} // end namespace
std::vector<std::string> UnescapeJsonCommandLine(
llvm::StringRef JsonEscapedCommandLine) {
CommandLineArgumentParser parser(JsonEscapedCommandLine);
return parser.Parse();
}
JsonCompileCommandLineParser::JsonCompileCommandLineParser(
const llvm::StringRef Input, CompileCommandHandler *CommandHandler)
: Input(Input), Position(Input.begin()-1), CommandHandler(CommandHandler) {}
bool JsonCompileCommandLineParser::Parse() {
NextNonWhitespace();
return ParseTranslationUnits();
}
std::string JsonCompileCommandLineParser::GetErrorMessage() const {
return ErrorMessage;
}
bool JsonCompileCommandLineParser::ParseTranslationUnits() {
if (!ConsumeOrError('[', "at start of compile command file")) return false;
if (!ParseTranslationUnit(/*First=*/true)) return false;
while (Consume(',')) {
if (!ParseTranslationUnit(/*First=*/false)) return false;
}
if (!ConsumeOrError(']', "at end of array")) return false;
if (CommandHandler != NULL) {
CommandHandler->EndTranslationUnits();
}
return true;
}
bool JsonCompileCommandLineParser::ParseTranslationUnit(bool First) {
if (First) {
if (!Consume('{')) return true;
} else {
if (!ConsumeOrError('{', "at start of object")) return false;
}
if (!Consume('}')) {
if (!ParseObjectKeyValuePairs()) return false;
if (!ConsumeOrError('}', "at end of object")) return false;
}
if (CommandHandler != NULL) {
CommandHandler->EndTranslationUnit();
}
return true;
}
bool JsonCompileCommandLineParser::ParseObjectKeyValuePairs() {
do {
llvm::StringRef Key;
if (!ParseString(Key)) return false;
if (!ConsumeOrError(':', "between name and value")) return false;
llvm::StringRef Value;
if (!ParseString(Value)) return false;
if (CommandHandler != NULL) {
CommandHandler->HandleKeyValue(Key, Value);
}
} while (Consume(','));
return true;
}
bool JsonCompileCommandLineParser::ParseString(llvm::StringRef &String) {
if (!ConsumeOrError('"', "at start of string")) return false;
llvm::StringRef::iterator First = Position;
llvm::StringRef::iterator Last = Position;
while (!Consume('"')) {
Consume('\\');
++Position;
// We need to store Position, as Consume will change Last before leaving
// the loop.
Last = Position;
}
String = llvm::StringRef(First, Last - First);
return true;
}
bool JsonCompileCommandLineParser::Consume(char C) {
if (Position == Input.end()) return false;
if (*Position != C) return false;
NextNonWhitespace();
return true;
}
bool JsonCompileCommandLineParser::ConsumeOrError(
char C, llvm::StringRef Message) {
if (!Consume(C)) {
SetExpectError(C, Message);
return false;
}
return true;
}
void JsonCompileCommandLineParser::SetExpectError(
char C, llvm::StringRef Message) {
ErrorMessage = (llvm::Twine("'") + llvm::StringRef(&C, 1) +
"' expected " + Message + ".").str();
}
void JsonCompileCommandLineParser::NextNonWhitespace() {
do {
++Position;
} while (IsWhitespace());
}
bool JsonCompileCommandLineParser::IsWhitespace() {
if (Position == Input.end()) return false;
return (*Position == ' ' || *Position == '\t' ||
*Position == '\n' || *Position == '\r');
}
} // end namespace tooling
} // end namespace clang