Changeset - 172b64c2042b
CMakeLists.txt
Show inline comments
 
@@ -47,8 +47,12 @@ endif()
 
find_package(Boost COMPONENTS program_options date_time system filesystem regex  signals REQUIRED)
 
message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}")
 

	
 
if (NOT WIN32)
 
set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Protobuf REQUIRED)
 
else()
 
set(PROTOBUF_FOUND TRUE)
 
endif()
 

	
 
set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
 
find_package(Communi)
backends/libpurple/CMakeLists.txt
Show inline comments
 
@@ -6,7 +6,8 @@ ADD_EXECUTABLE(spectrum2_libpurple_backend ${SRC})
 
if(NOT WIN32)
 
target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin pthread)
 
else()
 
target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin)
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/protobuf/libprotobuf")
 
target_link_libraries(spectrum2_libpurple_backend ${PURPLE_LIBRARY} ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport-plugin libprotobuf)
 
endif()
 
 
INSTALL(TARGETS spectrum2_libpurple_backend RUNTIME DESTINATION bin)
backends/libpurple/main.cpp
Show inline comments
 
@@ -1658,7 +1658,21 @@ int main(int argc, char **argv) {
 
		}
 
		else {
 
			log4cxx::helpers::Properties p;
 
			log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(KEYFILE_STRING("logging", "backend_config"));
 
			log4cxx::helpers::FileInputStream *istream = NULL;
 
			try {
 
				istream = new log4cxx::helpers::FileInputStream(KEYFILE_STRING("logging", "backend_config"));
 
			}
 
			catch(log4cxx::helpers::IOException &ex) {
 
				std::cerr << "Can't create FileInputStream logger instance: " << ex.what() << "\n";
 
			}
 
			catch (...) {
 
				std::cerr << "Can't create FileInputStream logger instance\n";
 
			}
 

	
 
			if (!istream) {
 
				return 1;
 
			}
 

	
 
			p.load(istream);
 
			LogString pid, jid;
 
			log4cxx::helpers::Transcoder::decode(stringOf(getpid()), pid);
backends/skype/main.cpp
Show inline comments
 
@@ -38,7 +38,13 @@ class SpectrumNetworkPlugin;
 

	
 
#define GET_RESPONSE_DATA(RESP, DATA) ((RESP.find(std::string(DATA) + " ") != std::string::npos) ? RESP.substr(RESP.find(DATA) + strlen(DATA) + 1) : "");
 
#define GET_PROPERTY(VAR, OBJ, WHICH, PROP) std::string VAR = sk->send_command(std::string("GET ") + OBJ + " " + WHICH + " " + PROP); \
 
					VAR = GET_RESPONSE_DATA(VAR, PROP);
 
					try {\
 
						VAR = GET_RESPONSE_DATA(VAR, PROP);\
 
					}\
 
					catch (std::out_of_range& oor) {\
 
						VAR="";\
 
					}
 
					
 

	
 

	
 
SpectrumNetworkPlugin *np;
 
@@ -547,7 +553,12 @@ bool Skype::loadSkypeBuddies() {
 
			name = GET_RESPONSE_DATA(name, "DISPLAYNAME");
 

	
 
			std::string users = send_command("GET GROUP " + data[1] + " USERS");
 
			users = GET_RESPONSE_DATA(users, "USERS");
 
			try {
 
				users = GET_RESPONSE_DATA(users, "USERS");
 
			}
 
			catch (std::out_of_range& oor) {
 
				continue;
 
			}
 
			boost::split(data, users, boost::is_any_of(","));
 
			BOOST_FOREACH(std::string u, data) {
 
				group_map[u] = grp;
backends/template/CMakeLists.txt
Show inline comments
 
@@ -7,7 +7,8 @@ ADD_EXECUTABLE(spectrum2_template_backend ${SRC})
 
if (NOT WIN32)
 
target_link_libraries(spectrum2_template_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
else()
 
target_link_libraries(spectrum2_template_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/protobuf/libprotobuf")
 
target_link_libraries(spectrum2_template_backend transport libprotobuf ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
 
endif()
 
 
#INSTALL(TARGETS spectrum2_template_backend RUNTIME DESTINATION bin)
include/transport/CMakeLists.txt
Show inline comments
 
if (PROTOBUF_FOUND)
 
    if (WIN32)
 
	set (PROTOBUF_PROTOC_EXECUTABLE protoc)
 
    endif()
 
    ADD_CUSTOM_COMMAND(
 
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protocol.pb.cc ${CMAKE_CURRENT_BINARY_DIR}/protocol.pb.h
 
        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --cpp_out  ${CMAKE_CURRENT_BINARY_DIR} --proto_path ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/protocol.proto
msvc-deps/CMakeLists.txt
Show inline comments
 
ADD_SUBDIRECTORY(sqlite3)
 
\ No newline at end of file
 
ADD_SUBDIRECTORY(sqlite3)
 
ADD_SUBDIRECTORY(protobuf)
 
\ No newline at end of file
msvc-deps/protobuf/CMakeLists.txt
Show inline comments
 
new file 100644
 
add_subdirectory(libprotobuf)
 
add_subdirectory(libprotoc)
 
add_subdirectory(protoc)
 
\ No newline at end of file
msvc-deps/protobuf/libprotobuf/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC google/protobuf/*.cc google/protobuf/*.h google/protobuf/io/*.cc google/protobuf/io/*.h google/protobuf/stubs/*.cc google/protobuf/stubs/*.h google/protobuf/compiler/*.cc google/protobuf/compiler/*.h)
 

	
 
include_directories(.)
 

	
 
ADD_LIBRARY(libprotobuf STATIC ${HEADERS} ${SRC})
 

	
 
INSTALL(TARGETS libprotobuf LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries)
 
\ No newline at end of file
msvc-deps/protobuf/libprotobuf/config.h
Show inline comments
 
new file 100644
 
/* protobuf config.h for MSVC.  On other platforms, this is generated
 
 * automatically by autoheader / autoconf / configure. */
 

	
 
/* the location of <hash_map> */
 
#define HASH_MAP_H <hash_map>
 

	
 
/* the namespace of hash_map/hash_set */
 
// Apparently Microsoft decided to move hash_map *back* to the std namespace
 
// in MSVC 2010:
 
//   http://blogs.msdn.com/vcblog/archive/2009/05/25/stl-breaking-changes-in-visual-studio-2010-beta-1.aspx
 
// TODO(kenton):  Use unordered_map instead, which is available in MSVC 2010.
 
#if _MSC_VER < 1310 || _MSC_VER >= 1600
 
#define HASH_NAMESPACE std
 
#else
 
#define HASH_NAMESPACE stdext
 
#endif
 

	
 
/* the location of <hash_set> */
 
#define HASH_SET_H <hash_set>
 

	
 
/* define if the compiler has hash_map */
 
#define HAVE_HASH_MAP 1
 

	
 
/* define if the compiler has hash_set */
 
#define HAVE_HASH_SET 1
 

	
 
/* define if you want to use zlib.  See readme.txt for additional
 
 * requirements. */
 
// #define HAVE_ZLIB 1
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/importer.cc
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 

	
 
#ifdef _MSC_VER
 
#include <io.h>
 
#else
 
#include <unistd.h>
 
#endif
 
#include <sys/types.h>
 
#include <sys/stat.h>
 
#include <fcntl.h>
 
#include <errno.h>
 

	
 
#include <algorithm>
 

	
 
#include <google/protobuf/compiler/importer.h>
 

	
 
#include <google/protobuf/compiler/parser.h>
 
#include <google/protobuf/io/tokenizer.h>
 
#include <google/protobuf/io/zero_copy_stream_impl.h>
 
#include <google/protobuf/stubs/strutil.h>
 

	
 
namespace google {
 
namespace protobuf {
 
namespace compiler {
 

	
 
#ifdef _WIN32
 
#ifndef F_OK
 
#define F_OK 00  // not defined by MSVC for whatever reason
 
#endif
 
#include <ctype.h>
 
#endif
 

	
 
// Returns true if the text looks like a Windows-style absolute path, starting
 
// with a drive letter.  Example:  "C:\foo".  TODO(kenton):  Share this with
 
// copy in command_line_interface.cc?
 
static bool IsWindowsAbsolutePath(const string& text) {
 
#if defined(_WIN32) || defined(__CYGWIN__)
 
  return text.size() >= 3 && text[1] == ':' &&
 
         isalpha(text[0]) &&
 
         (text[2] == '/' || text[2] == '\\') &&
 
         text.find_last_of(':') == 1;
 
#else
 
  return false;
 
#endif
 
}
 

	
 
MultiFileErrorCollector::~MultiFileErrorCollector() {}
 

	
 
// This class serves two purposes:
 
// - It implements the ErrorCollector interface (used by Tokenizer and Parser)
 
//   in terms of MultiFileErrorCollector, using a particular filename.
 
// - It lets us check if any errors have occurred.
 
class SourceTreeDescriptorDatabase::SingleFileErrorCollector
 
    : public io::ErrorCollector {
 
 public:
 
  SingleFileErrorCollector(const string& filename,
 
                           MultiFileErrorCollector* multi_file_error_collector)
 
    : filename_(filename),
 
      multi_file_error_collector_(multi_file_error_collector),
 
      had_errors_(false) {}
 
  ~SingleFileErrorCollector() {}
 

	
 
  bool had_errors() { return had_errors_; }
 

	
 
  // implements ErrorCollector ---------------------------------------
 
  void AddError(int line, int column, const string& message) {
 
    if (multi_file_error_collector_ != NULL) {
 
      multi_file_error_collector_->AddError(filename_, line, column, message);
 
    }
 
    had_errors_ = true;
 
  }
 

	
 
 private:
 
  string filename_;
 
  MultiFileErrorCollector* multi_file_error_collector_;
 
  bool had_errors_;
 
};
 

	
 
// ===================================================================
 

	
 
SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase(
 
    SourceTree* source_tree)
 
  : source_tree_(source_tree),
 
    error_collector_(NULL),
 
    using_validation_error_collector_(false),
 
    validation_error_collector_(this) {}
 

	
 
SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileByName(
 
    const string& filename, FileDescriptorProto* output) {
 
  scoped_ptr<io::ZeroCopyInputStream> input(source_tree_->Open(filename));
 
  if (input == NULL) {
 
    if (error_collector_ != NULL) {
 
      error_collector_->AddError(filename, -1, 0, "File not found.");
 
    }
 
    return false;
 
  }
 

	
 
  // Set up the tokenizer and parser.
 
  SingleFileErrorCollector file_error_collector(filename, error_collector_);
 
  io::Tokenizer tokenizer(input.get(), &file_error_collector);
 

	
 
  Parser parser;
 
  if (error_collector_ != NULL) {
 
    parser.RecordErrorsTo(&file_error_collector);
 
  }
 
  if (using_validation_error_collector_) {
 
    parser.RecordSourceLocationsTo(&source_locations_);
 
  }
 

	
 
  // Parse it.
 
  output->set_name(filename);
 
  return parser.Parse(&tokenizer, output) &&
 
         !file_error_collector.had_errors();
 
}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileContainingSymbol(
 
    const string& symbol_name, FileDescriptorProto* output) {
 
  return false;
 
}
 

	
 
bool SourceTreeDescriptorDatabase::FindFileContainingExtension(
 
    const string& containing_type, int field_number,
 
    FileDescriptorProto* output) {
 
  return false;
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
SourceTreeDescriptorDatabase::ValidationErrorCollector::
 
ValidationErrorCollector(SourceTreeDescriptorDatabase* owner)
 
  : owner_(owner) {}
 

	
 
SourceTreeDescriptorDatabase::ValidationErrorCollector::
 
~ValidationErrorCollector() {}
 

	
 
void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError(
 
    const string& filename,
 
    const string& element_name,
 
    const Message* descriptor,
 
    ErrorLocation location,
 
    const string& message) {
 
  if (owner_->error_collector_ == NULL) return;
 

	
 
  int line, column;
 
  owner_->source_locations_.Find(descriptor, location, &line, &column);
 
  owner_->error_collector_->AddError(filename, line, column, message);
 
}
 

	
 
// ===================================================================
 

	
 
Importer::Importer(SourceTree* source_tree,
 
                   MultiFileErrorCollector* error_collector)
 
  : database_(source_tree),
 
    pool_(&database_, database_.GetValidationErrorCollector()) {
 
  database_.RecordErrorsTo(error_collector);
 
}
 

	
 
Importer::~Importer() {}
 

	
 
const FileDescriptor* Importer::Import(const string& filename) {
 
  return pool_.FindFileByName(filename);
 
}
 

	
 
// ===================================================================
 

	
 
SourceTree::~SourceTree() {}
 

	
 
DiskSourceTree::DiskSourceTree() {}
 

	
 
DiskSourceTree::~DiskSourceTree() {}
 

	
 
static inline char LastChar(const string& str) {
 
  return str[str.size() - 1];
 
}
 

	
 
// Given a path, returns an equivalent path with these changes:
 
// - On Windows, any backslashes are replaced with forward slashes.
 
// - Any instances of the directory "." are removed.
 
// - Any consecutive '/'s are collapsed into a single slash.
 
// Note that the resulting string may be empty.
 
//
 
// TODO(kenton):  It would be nice to handle "..", e.g. so that we can figure
 
//   out that "foo/bar.proto" is inside "baz/../foo".  However, if baz is a
 
//   symlink or doesn't exist, then things get complicated, and we can't
 
//   actually determine this without investigating the filesystem, probably
 
//   in non-portable ways.  So, we punt.
 
//
 
// TODO(kenton):  It would be nice to use realpath() here except that it
 
//   resolves symbolic links.  This could cause problems if people place
 
//   symbolic links in their source tree.  For example, if you executed:
 
//     protoc --proto_path=foo foo/bar/baz.proto
 
//   then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize
 
//   to a path which does not appear to be under foo, and thus the compiler
 
//   will complain that baz.proto is not inside the --proto_path.
 
static string CanonicalizePath(string path) {
 
#ifdef _WIN32
 
  // The Win32 API accepts forward slashes as a path delimiter even though
 
  // backslashes are standard.  Let's avoid confusion and use only forward
 
  // slashes.
 
  if (HasPrefixString(path, "\\\\")) {
 
    // Avoid converting two leading backslashes.
 
    path = "\\\\" + StringReplace(path.substr(2), "\\", "/", true);
 
  } else {
 
    path = StringReplace(path, "\\", "/", true);
 
  }
 
#endif
 

	
 
  vector<string> parts;
 
  vector<string> canonical_parts;
 
  SplitStringUsing(path, "/", &parts);  // Note:  Removes empty parts.
 
  for (int i = 0; i < parts.size(); i++) {
 
    if (parts[i] == ".") {
 
      // Ignore.
 
    } else {
 
      canonical_parts.push_back(parts[i]);
 
    }
 
  }
 
  string result = JoinStrings(canonical_parts, "/");
 
  if (!path.empty() && path[0] == '/') {
 
    // Restore leading slash.
 
    result = '/' + result;
 
  }
 
  if (!path.empty() && LastChar(path) == '/' &&
 
      !result.empty() && LastChar(result) != '/') {
 
    // Restore trailing slash.
 
    result += '/';
 
  }
 
  return result;
 
}
 

	
 
static inline bool ContainsParentReference(const string& path) {
 
  return path == ".." ||
 
         HasPrefixString(path, "../") ||
 
         HasSuffixString(path, "/..") ||
 
         path.find("/../") != string::npos;
 
}
 

	
 
// Maps a file from an old location to a new one.  Typically, old_prefix is
 
// a virtual path and new_prefix is its corresponding disk path.  Returns
 
// false if the filename did not start with old_prefix, otherwise replaces
 
// old_prefix with new_prefix and stores the result in *result.  Examples:
 
//   string result;
 
//   assert(ApplyMapping("foo/bar", "", "baz", &result));
 
//   assert(result == "baz/foo/bar");
 
//
 
//   assert(ApplyMapping("foo/bar", "foo", "baz", &result));
 
//   assert(result == "baz/bar");
 
//
 
//   assert(ApplyMapping("foo", "foo", "bar", &result));
 
//   assert(result == "bar");
 
//
 
//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
 
//   assert(!ApplyMapping("foo/bar", "baz", "qux", &result));
 
//   assert(!ApplyMapping("foobar", "foo", "baz", &result));
 
static bool ApplyMapping(const string& filename,
 
                         const string& old_prefix,
 
                         const string& new_prefix,
 
                         string* result) {
 
  if (old_prefix.empty()) {
 
    // old_prefix matches any relative path.
 
    if (ContainsParentReference(filename)) {
 
      // We do not allow the file name to use "..".
 
      return false;
 
    }
 
    if (HasPrefixString(filename, "/") ||
 
        IsWindowsAbsolutePath(filename)) {
 
      // This is an absolute path, so it isn't matched by the empty string.
 
      return false;
 
    }
 
    result->assign(new_prefix);
 
    if (!result->empty()) result->push_back('/');
 
    result->append(filename);
 
    return true;
 
  } else if (HasPrefixString(filename, old_prefix)) {
 
    // old_prefix is a prefix of the filename.  Is it the whole filename?
 
    if (filename.size() == old_prefix.size()) {
 
      // Yep, it's an exact match.
 
      *result = new_prefix;
 
      return true;
 
    } else {
 
      // Not an exact match.  Is the next character a '/'?  Otherwise,
 
      // this isn't actually a match at all.  E.g. the prefix "foo/bar"
 
      // does not match the filename "foo/barbaz".
 
      int after_prefix_start = -1;
 
      if (filename[old_prefix.size()] == '/') {
 
        after_prefix_start = old_prefix.size() + 1;
 
      } else if (filename[old_prefix.size() - 1] == '/') {
 
        // old_prefix is never empty, and canonicalized paths never have
 
        // consecutive '/' characters.
 
        after_prefix_start = old_prefix.size();
 
      }
 
      if (after_prefix_start != -1) {
 
        // Yep.  So the prefixes are directories and the filename is a file
 
        // inside them.
 
        string after_prefix = filename.substr(after_prefix_start);
 
        if (ContainsParentReference(after_prefix)) {
 
          // We do not allow the file name to use "..".
 
          return false;
 
        }
 
        result->assign(new_prefix);
 
        if (!result->empty()) result->push_back('/');
 
        result->append(after_prefix);
 
        return true;
 
      }
 
    }
 
  }
 

	
 
  return false;
 
}
 

	
 
void DiskSourceTree::MapPath(const string& virtual_path,
 
                             const string& disk_path) {
 
  mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path)));
 
}
 

	
 
DiskSourceTree::DiskFileToVirtualFileResult
 
DiskSourceTree::DiskFileToVirtualFile(
 
    const string& disk_file,
 
    string* virtual_file,
 
    string* shadowing_disk_file) {
 
  int mapping_index = -1;
 
  string canonical_disk_file = CanonicalizePath(disk_file);
 

	
 
  for (int i = 0; i < mappings_.size(); i++) {
 
    // Apply the mapping in reverse.
 
    if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path,
 
                     mappings_[i].virtual_path, virtual_file)) {
 
      // Success.
 
      mapping_index = i;
 
      break;
 
    }
 
  }
 

	
 
  if (mapping_index == -1) {
 
    return NO_MAPPING;
 
  }
 

	
 
  // Iterate through all mappings with higher precedence and verify that none
 
  // of them map this file to some other existing file.
 
  for (int i = 0; i < mapping_index; i++) {
 
    if (ApplyMapping(*virtual_file, mappings_[i].virtual_path,
 
                     mappings_[i].disk_path, shadowing_disk_file)) {
 
      if (access(shadowing_disk_file->c_str(), F_OK) >= 0) {
 
        // File exists.
 
        return SHADOWED;
 
      }
 
    }
 
  }
 
  shadowing_disk_file->clear();
 

	
 
  // Verify that we can open the file.  Note that this also has the side-effect
 
  // of verifying that we are not canonicalizing away any non-existent
 
  // directories.
 
  scoped_ptr<io::ZeroCopyInputStream> stream(OpenDiskFile(disk_file));
 
  if (stream == NULL) {
 
    return CANNOT_OPEN;
 
  }
 

	
 
  return SUCCESS;
 
}
 

	
 
bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file,
 
                                           string* disk_file) {
 
  scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file,
 
                                                             disk_file));
 
  return stream != NULL;
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) {
 
  return OpenVirtualFile(filename, NULL);
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile(
 
    const string& virtual_file,
 
    string* disk_file) {
 
  if (virtual_file != CanonicalizePath(virtual_file) ||
 
      ContainsParentReference(virtual_file)) {
 
    // We do not allow importing of paths containing things like ".." or
 
    // consecutive slashes since the compiler expects files to be uniquely
 
    // identified by file name.
 
    return NULL;
 
  }
 

	
 
  for (int i = 0; i < mappings_.size(); i++) {
 
    string temp_disk_file;
 
    if (ApplyMapping(virtual_file, mappings_[i].virtual_path,
 
                     mappings_[i].disk_path, &temp_disk_file)) {
 
      io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file);
 
      if (stream != NULL) {
 
        if (disk_file != NULL) {
 
          *disk_file = temp_disk_file;
 
        }
 
        return stream;
 
      }
 

	
 
      if (errno == EACCES) {
 
        // The file exists but is not readable.
 
        // TODO(kenton):  Find a way to report this more nicely.
 
        GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file;
 
        return NULL;
 
      }
 
    }
 
  }
 

	
 
  return NULL;
 
}
 

	
 
io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile(
 
    const string& filename) {
 
  int file_descriptor;
 
  do {
 
    file_descriptor = open(filename.c_str(), O_RDONLY);
 
  } while (file_descriptor < 0 && errno == EINTR);
 
  if (file_descriptor >= 0) {
 
    io::FileInputStream* result = new io::FileInputStream(file_descriptor);
 
    result->SetCloseOnDelete(true);
 
    return result;
 
  } else {
 
    return NULL;
 
  }
 
}
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 
}  // namespace google
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/importer.h
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// This file is the public interface to the .proto file parser.
 

	
 
#ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
 
#define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
 

	
 
#include <string>
 
#include <vector>
 
#include <set>
 
#include <utility>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor_database.h>
 
#include <google/protobuf/compiler/parser.h>
 

	
 
namespace google {
 
namespace protobuf {
 

	
 
namespace io { class ZeroCopyInputStream; }
 

	
 
namespace compiler {
 

	
 
// Defined in this file.
 
class Importer;
 
class MultiFileErrorCollector;
 
class SourceTree;
 
class DiskSourceTree;
 

	
 
// TODO(kenton):  Move all SourceTree stuff to a separate file?
 

	
 
// An implementation of DescriptorDatabase which loads files from a SourceTree
 
// and parses them.
 
//
 
// Note:  This class is not thread-safe since it maintains a table of source
 
//   code locations for error reporting.  However, when a DescriptorPool wraps
 
//   a DescriptorDatabase, it uses mutex locking to make sure only one method
 
//   of the database is called at a time, even if the DescriptorPool is used
 
//   from multiple threads.  Therefore, there is only a problem if you create
 
//   multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase
 
//   and use them from multiple threads.
 
//
 
// Note:  This class does not implement FindFileContainingSymbol() or
 
//   FindFileContainingExtension(); these will always return false.
 
class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase {
 
 public:
 
  SourceTreeDescriptorDatabase(SourceTree* source_tree);
 
  ~SourceTreeDescriptorDatabase();
 

	
 
  // Instructs the SourceTreeDescriptorDatabase to report any parse errors
 
  // to the given MultiFileErrorCollector.  This should be called before
 
  // parsing.  error_collector must remain valid until either this method
 
  // is called again or the SourceTreeDescriptorDatabase is destroyed.
 
  void RecordErrorsTo(MultiFileErrorCollector* error_collector) {
 
    error_collector_ = error_collector;
 
  }
 

	
 
  // Gets a DescriptorPool::ErrorCollector which records errors to the
 
  // MultiFileErrorCollector specified with RecordErrorsTo().  This collector
 
  // has the ability to determine exact line and column numbers of errors
 
  // from the information given to it by the DescriptorPool.
 
  DescriptorPool::ErrorCollector* GetValidationErrorCollector() {
 
    using_validation_error_collector_ = true;
 
    return &validation_error_collector_;
 
  }
 

	
 
  // implements DescriptorDatabase -----------------------------------
 
  bool FindFileByName(const string& filename, FileDescriptorProto* output);
 
  bool FindFileContainingSymbol(const string& symbol_name,
 
                                FileDescriptorProto* output);
 
  bool FindFileContainingExtension(const string& containing_type,
 
                                   int field_number,
 
                                   FileDescriptorProto* output);
 

	
 
 private:
 
  class SingleFileErrorCollector;
 

	
 
  SourceTree* source_tree_;
 
  MultiFileErrorCollector* error_collector_;
 

	
 
  class LIBPROTOBUF_EXPORT ValidationErrorCollector : public DescriptorPool::ErrorCollector {
 
   public:
 
    ValidationErrorCollector(SourceTreeDescriptorDatabase* owner);
 
    ~ValidationErrorCollector();
 

	
 
    // implements ErrorCollector ---------------------------------------
 
    void AddError(const string& filename,
 
                  const string& element_name,
 
                  const Message* descriptor,
 
                  ErrorLocation location,
 
                  const string& message);
 

	
 
   private:
 
    SourceTreeDescriptorDatabase* owner_;
 
  };
 
  friend class ValidationErrorCollector;
 

	
 
  bool using_validation_error_collector_;
 
  SourceLocationTable source_locations_;
 
  ValidationErrorCollector validation_error_collector_;
 
};
 

	
 
// Simple interface for parsing .proto files.  This wraps the process
 
// of opening the file, parsing it with a Parser, recursively parsing all its
 
// imports, and then cross-linking the results to produce a FileDescriptor.
 
//
 
// This is really just a thin wrapper around SourceTreeDescriptorDatabase.
 
// You may find that SourceTreeDescriptorDatabase is more flexible.
 
//
 
// TODO(kenton):  I feel like this class is not well-named.
 
class LIBPROTOBUF_EXPORT Importer {
 
 public:
 
  Importer(SourceTree* source_tree,
 
           MultiFileErrorCollector* error_collector);
 
  ~Importer();
 

	
 
  // Import the given file and build a FileDescriptor representing it.  If
 
  // the file is already in the DescriptorPool, the existing FileDescriptor
 
  // will be returned.  The FileDescriptor is property of the DescriptorPool,
 
  // and will remain valid until it is destroyed.  If any errors occur, they
 
  // will be reported using the error collector and Import() will return NULL.
 
  //
 
  // A particular Importer object will only report errors for a particular
 
  // file once.  All future attempts to import the same file will return NULL
 
  // without reporting any errors.  The idea is that you might want to import
 
  // a lot of files without seeing the same errors over and over again.  If
 
  // you want to see errors for the same files repeatedly, you can use a
 
  // separate Importer object to import each one (but use the same
 
  // DescriptorPool so that they can be cross-linked).
 
  const FileDescriptor* Import(const string& filename);
 

	
 
  // The DescriptorPool in which all imported FileDescriptors and their
 
  // contents are stored.
 
  inline const DescriptorPool* pool() const {
 
    return &pool_;
 
  }
 

	
 
 private:
 
  SourceTreeDescriptorDatabase database_;
 
  DescriptorPool pool_;
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
 
};
 

	
 
// If the importer encounters problems while trying to import the proto files,
 
// it reports them to a MultiFileErrorCollector.
 
class LIBPROTOBUF_EXPORT MultiFileErrorCollector {
 
 public:
 
  inline MultiFileErrorCollector() {}
 
  virtual ~MultiFileErrorCollector();
 

	
 
  // Line and column numbers are zero-based.  A line number of -1 indicates
 
  // an error with the entire file (e.g. "not found").
 
  virtual void AddError(const string& filename, int line, int column,
 
                        const string& message) = 0;
 

	
 
 private:
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector);
 
};
 

	
 
// Abstract interface which represents a directory tree containing proto files.
 
// Used by the default implementation of Importer to resolve import statements
 
// Most users will probably want to use the DiskSourceTree implementation,
 
// below.
 
class LIBPROTOBUF_EXPORT SourceTree {
 
 public:
 
  inline SourceTree() {}
 
  virtual ~SourceTree();
 

	
 
  // Open the given file and return a stream that reads it, or NULL if not
 
  // found.  The caller takes ownership of the returned object.  The filename
 
  // must be a path relative to the root of the source tree and must not
 
  // contain "." or ".." components.
 
  virtual io::ZeroCopyInputStream* Open(const string& filename) = 0;
 

	
 
 private:
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree);
 
};
 

	
 
// An implementation of SourceTree which loads files from locations on disk.
 
// Multiple mappings can be set up to map locations in the DiskSourceTree to
 
// locations in the physical filesystem.
 
class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree {
 
 public:
 
  DiskSourceTree();
 
  ~DiskSourceTree();
 

	
 
  // Map a path on disk to a location in the SourceTree.  The path may be
 
  // either a file or a directory.  If it is a directory, the entire tree
 
  // under it will be mapped to the given virtual location.  To map a directory
 
  // to the root of the source tree, pass an empty string for virtual_path.
 
  //
 
  // If multiple mapped paths apply when opening a file, they will be searched
 
  // in order.  For example, if you do:
 
  //   MapPath("bar", "foo/bar");
 
  //   MapPath("", "baz");
 
  // and then you do:
 
  //   Open("bar/qux");
 
  // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux,
 
  // returning the first one that opens successfuly.
 
  //
 
  // disk_path may be an absolute path or relative to the current directory,
 
  // just like a path you'd pass to open().
 
  void MapPath(const string& virtual_path, const string& disk_path);
 

	
 
  // Return type for DiskFileToVirtualFile().
 
  enum DiskFileToVirtualFileResult {
 
    SUCCESS,
 
    SHADOWED,
 
    CANNOT_OPEN,
 
    NO_MAPPING
 
  };
 

	
 
  // Given a path to a file on disk, find a virtual path mapping to that
 
  // file.  The first mapping created with MapPath() whose disk_path contains
 
  // the filename is used.  However, that virtual path may not actually be
 
  // usable to open the given file.  Possible return values are:
 
  // * SUCCESS: The mapping was found.  *virtual_file is filled in so that
 
  //   calling Open(*virtual_file) will open the file named by disk_file.
 
  // * SHADOWED: A mapping was found, but using Open() to open this virtual
 
  //   path will end up returning some different file.  This is because some
 
  //   other mapping with a higher precedence also matches this virtual path
 
  //   and maps it to a different file that exists on disk.  *virtual_file
 
  //   is filled in as it would be in the SUCCESS case.  *shadowing_disk_file
 
  //   is filled in with the disk path of the file which would be opened if
 
  //   you were to call Open(*virtual_file).
 
  // * CANNOT_OPEN: The mapping was found and was not shadowed, but the
 
  //   file specified cannot be opened.  When this value is returned,
 
  //   errno will indicate the reason the file cannot be opened.  *virtual_file
 
  //   will be set to the virtual path as in the SUCCESS case, even though
 
  //   it is not useful.
 
  // * NO_MAPPING: Indicates that no mapping was found which contains this
 
  //   file.
 
  DiskFileToVirtualFileResult
 
    DiskFileToVirtualFile(const string& disk_file,
 
                          string* virtual_file,
 
                          string* shadowing_disk_file);
 

	
 
  // Given a virtual path, find the path to the file on disk.
 
  // Return true and update disk_file with the on-disk path if the file exists.
 
  // Return false and leave disk_file untouched if the file doesn't exist.
 
  bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file);
 

	
 
  // implements SourceTree -------------------------------------------
 
  io::ZeroCopyInputStream* Open(const string& filename);
 

	
 
 private:
 
  struct Mapping {
 
    string virtual_path;
 
    string disk_path;
 

	
 
    inline Mapping(const string& virtual_path, const string& disk_path)
 
      : virtual_path(virtual_path), disk_path(disk_path) {}
 
  };
 
  vector<Mapping> mappings_;
 

	
 
  // Like Open(), but returns the on-disk path in disk_file if disk_file is
 
  // non-NULL and the file could be successfully opened.
 
  io::ZeroCopyInputStream* OpenVirtualFile(const string& virtual_file,
 
                                           string* disk_file);
 

	
 
  // Like Open() but given the actual on-disk path.
 
  io::ZeroCopyInputStream* OpenDiskFile(const string& filename);
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree);
 
};
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 

	
 
}  // namespace google
 
#endif  // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/parser.cc
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// Recursive descent FTW.
 

	
 
#include <float.h>
 
#include <google/protobuf/stubs/hash.h>
 
#include <limits>
 

	
 

	
 
#include <google/protobuf/compiler/parser.h>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor.pb.h>
 
#include <google/protobuf/wire_format.h>
 
#include <google/protobuf/io/tokenizer.h>
 
#include <google/protobuf/stubs/common.h>
 
#include <google/protobuf/stubs/strutil.h>
 
#include <google/protobuf/stubs/map-util.h>
 

	
 
namespace google {
 
namespace protobuf {
 
namespace compiler {
 

	
 
using internal::WireFormat;
 

	
 
namespace {
 

	
 
typedef hash_map<string, FieldDescriptorProto::Type> TypeNameMap;
 

	
 
TypeNameMap MakeTypeNameTable() {
 
  TypeNameMap result;
 

	
 
  result["double"  ] = FieldDescriptorProto::TYPE_DOUBLE;
 
  result["float"   ] = FieldDescriptorProto::TYPE_FLOAT;
 
  result["uint64"  ] = FieldDescriptorProto::TYPE_UINT64;
 
  result["fixed64" ] = FieldDescriptorProto::TYPE_FIXED64;
 
  result["fixed32" ] = FieldDescriptorProto::TYPE_FIXED32;
 
  result["bool"    ] = FieldDescriptorProto::TYPE_BOOL;
 
  result["string"  ] = FieldDescriptorProto::TYPE_STRING;
 
  result["group"   ] = FieldDescriptorProto::TYPE_GROUP;
 

	
 
  result["bytes"   ] = FieldDescriptorProto::TYPE_BYTES;
 
  result["uint32"  ] = FieldDescriptorProto::TYPE_UINT32;
 
  result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32;
 
  result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64;
 
  result["int32"   ] = FieldDescriptorProto::TYPE_INT32;
 
  result["int64"   ] = FieldDescriptorProto::TYPE_INT64;
 
  result["sint32"  ] = FieldDescriptorProto::TYPE_SINT32;
 
  result["sint64"  ] = FieldDescriptorProto::TYPE_SINT64;
 

	
 
  return result;
 
}
 

	
 
const TypeNameMap kTypeNames = MakeTypeNameTable();
 

	
 
}  // anonymous namespace
 

	
 
// Makes code slightly more readable.  The meaning of "DO(foo)" is
 
// "Execute foo and fail if it fails.", where failure is indicated by
 
// returning false.
 
#define DO(STATEMENT) if (STATEMENT) {} else return false
 

	
 
// ===================================================================
 

	
 
Parser::Parser()
 
  : input_(NULL),
 
    error_collector_(NULL),
 
    source_location_table_(NULL),
 
    had_errors_(false),
 
    require_syntax_identifier_(false),
 
    stop_after_syntax_identifier_(false) {
 
}
 

	
 
Parser::~Parser() {
 
}
 

	
 
// ===================================================================
 

	
 
inline bool Parser::LookingAt(const char* text) {
 
  return input_->current().text == text;
 
}
 

	
 
inline bool Parser::LookingAtType(io::Tokenizer::TokenType token_type) {
 
  return input_->current().type == token_type;
 
}
 

	
 
inline bool Parser::AtEnd() {
 
  return LookingAtType(io::Tokenizer::TYPE_END);
 
}
 

	
 
bool Parser::TryConsume(const char* text) {
 
  if (LookingAt(text)) {
 
    input_->Next();
 
    return true;
 
  } else {
 
    return false;
 
  }
 
}
 

	
 
bool Parser::Consume(const char* text, const char* error) {
 
  if (TryConsume(text)) {
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::Consume(const char* text) {
 
  if (TryConsume(text)) {
 
    return true;
 
  } else {
 
    AddError("Expected \"" + string(text) + "\".");
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeIdentifier(string* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
 
    *output = input_->current().text;
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeInteger(int* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    uint64 value = 0;
 
    if (!io::Tokenizer::ParseInteger(input_->current().text,
 
                                     kint32max, &value)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse an integer.
 
    }
 
    *output = value;
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeInteger64(uint64 max_value, uint64* output,
 
                              const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    if (!io::Tokenizer::ParseInteger(input_->current().text, max_value,
 
                                     output)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse an integer.
 
      *output = 0;
 
    }
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeNumber(double* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) {
 
    *output = io::Tokenizer::ParseFloat(input_->current().text);
 
    input_->Next();
 
    return true;
 
  } else if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) {
 
    // Also accept integers.
 
    uint64 value = 0;
 
    if (!io::Tokenizer::ParseInteger(input_->current().text,
 
                                     kuint64max, &value)) {
 
      AddError("Integer out of range.");
 
      // We still return true because we did, in fact, parse a number.
 
    }
 
    *output = value;
 
    input_->Next();
 
    return true;
 
  } else if (LookingAt("inf")) {
 
    *output = numeric_limits<double>::infinity();
 
    input_->Next();
 
    return true;
 
  } else if (LookingAt("nan")) {
 
    *output = numeric_limits<double>::quiet_NaN();
 
    input_->Next();
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
bool Parser::ConsumeString(string* output, const char* error) {
 
  if (LookingAtType(io::Tokenizer::TYPE_STRING)) {
 
    io::Tokenizer::ParseString(input_->current().text, output);
 
    input_->Next();
 
    // Allow C++ like concatenation of adjacent string tokens.
 
    while (LookingAtType(io::Tokenizer::TYPE_STRING)) {
 
      io::Tokenizer::ParseStringAppend(input_->current().text, output);
 
      input_->Next();
 
    }
 
    return true;
 
  } else {
 
    AddError(error);
 
    return false;
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
void Parser::AddError(int line, int column, const string& error) {
 
  if (error_collector_ != NULL) {
 
    error_collector_->AddError(line, column, error);
 
  }
 
  had_errors_ = true;
 
}
 

	
 
void Parser::AddError(const string& error) {
 
  AddError(input_->current().line, input_->current().column, error);
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
Parser::LocationRecorder::LocationRecorder(Parser* parser)
 
  : parser_(parser),
 
    location_(parser_->source_code_info_->add_location()) {
 
  location_->add_span(parser_->input_->current().line);
 
  location_->add_span(parser_->input_->current().column);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent) {
 
  Init(parent);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
 
                                           int path1) {
 
  Init(parent);
 
  AddPath(path1);
 
}
 

	
 
Parser::LocationRecorder::LocationRecorder(const LocationRecorder& parent,
 
                                           int path1, int path2) {
 
  Init(parent);
 
  AddPath(path1);
 
  AddPath(path2);
 
}
 

	
 
void Parser::LocationRecorder::Init(const LocationRecorder& parent) {
 
  parser_ = parent.parser_;
 
  location_ = parser_->source_code_info_->add_location();
 
  location_->mutable_path()->CopyFrom(parent.location_->path());
 

	
 
  location_->add_span(parser_->input_->current().line);
 
  location_->add_span(parser_->input_->current().column);
 
}
 

	
 
Parser::LocationRecorder::~LocationRecorder() {
 
  if (location_->span_size() <= 2) {
 
    EndAt(parser_->input_->previous());
 
  }
 
}
 

	
 
void Parser::LocationRecorder::AddPath(int path_component) {
 
  location_->add_path(path_component);
 
}
 

	
 
void Parser::LocationRecorder::StartAt(const io::Tokenizer::Token& token) {
 
  location_->set_span(0, token.line);
 
  location_->set_span(1, token.column);
 
}
 

	
 
void Parser::LocationRecorder::EndAt(const io::Tokenizer::Token& token) {
 
  if (token.line != location_->span(0)) {
 
    location_->add_span(token.line);
 
  }
 
  location_->add_span(token.end_column);
 
}
 

	
 
void Parser::LocationRecorder::RecordLegacyLocation(const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location) {
 
  if (parser_->source_location_table_ != NULL) {
 
    parser_->source_location_table_->Add(
 
        descriptor, location, location_->span(0), location_->span(1));
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
void Parser::SkipStatement() {
 
  while (true) {
 
    if (AtEnd()) {
 
      return;
 
    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
 
      if (TryConsume(";")) {
 
        return;
 
      } else if (TryConsume("{")) {
 
        SkipRestOfBlock();
 
        return;
 
      } else if (LookingAt("}")) {
 
        return;
 
      }
 
    }
 
    input_->Next();
 
  }
 
}
 

	
 
void Parser::SkipRestOfBlock() {
 
  while (true) {
 
    if (AtEnd()) {
 
      return;
 
    } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) {
 
      if (TryConsume("}")) {
 
        return;
 
      } else if (TryConsume("{")) {
 
        SkipRestOfBlock();
 
      }
 
    }
 
    input_->Next();
 
  }
 
}
 

	
 
// ===================================================================
 

	
 
bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) {
 
  input_ = input;
 
  had_errors_ = false;
 
  syntax_identifier_.clear();
 

	
 
  // Note that |file| could be NULL at this point if
 
  // stop_after_syntax_identifier_ is true.  So, we conservatively allocate
 
  // SourceCodeInfo on the stack, then swap it into the FileDescriptorProto
 
  // later on.
 
  SourceCodeInfo source_code_info;
 
  source_code_info_ = &source_code_info;
 

	
 
  if (LookingAtType(io::Tokenizer::TYPE_START)) {
 
    // Advance to first token.
 
    input_->Next();
 
  }
 

	
 
  {
 
    LocationRecorder root_location(this);
 

	
 
    if (require_syntax_identifier_ || LookingAt("syntax")) {
 
      if (!ParseSyntaxIdentifier()) {
 
        // Don't attempt to parse the file if we didn't recognize the syntax
 
        // identifier.
 
        return false;
 
      }
 
    } else if (!stop_after_syntax_identifier_) {
 
      syntax_identifier_ = "proto2";
 
    }
 

	
 
    if (stop_after_syntax_identifier_) return !had_errors_;
 

	
 
    // Repeatedly parse statements until we reach the end of the file.
 
    while (!AtEnd()) {
 
      if (!ParseTopLevelStatement(file, root_location)) {
 
        // This statement failed to parse.  Skip it, but keep looping to parse
 
        // other statements.
 
        SkipStatement();
 

	
 
        if (LookingAt("}")) {
 
          AddError("Unmatched \"}\".");
 
          input_->Next();
 
        }
 
      }
 
    }
 
  }
 

	
 
  input_ = NULL;
 
  source_code_info_ = NULL;
 
  source_code_info.Swap(file->mutable_source_code_info());
 
  return !had_errors_;
 
}
 

	
 
bool Parser::ParseSyntaxIdentifier() {
 
  DO(Consume("syntax", "File must begin with 'syntax = \"proto2\";'."));
 
  DO(Consume("="));
 
  io::Tokenizer::Token syntax_token = input_->current();
 
  string syntax;
 
  DO(ConsumeString(&syntax, "Expected syntax identifier."));
 
  DO(Consume(";"));
 

	
 
  syntax_identifier_ = syntax;
 

	
 
  if (syntax != "proto2" && !stop_after_syntax_identifier_) {
 
    AddError(syntax_token.line, syntax_token.column,
 
      "Unrecognized syntax identifier \"" + syntax + "\".  This parser "
 
      "only recognizes \"proto2\".");
 
    return false;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseTopLevelStatement(FileDescriptorProto* file,
 
                                    const LocationRecorder& root_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("message")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kMessageTypeFieldNumber, file->message_type_size());
 
    return ParseMessageDefinition(file->add_message_type(), location);
 
  } else if (LookingAt("enum")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kEnumTypeFieldNumber, file->enum_type_size());
 
    return ParseEnumDefinition(file->add_enum_type(), location);
 
  } else if (LookingAt("service")) {
 
    LocationRecorder location(root_location,
 
      FileDescriptorProto::kServiceFieldNumber, file->service_size());
 
    return ParseServiceDefinition(file->add_service(), location);
 
  } else if (LookingAt("extend")) {
 
    LocationRecorder location(root_location,
 
        FileDescriptorProto::kExtensionFieldNumber);
 
    return ParseExtend(file->mutable_extension(),
 
                       file->mutable_message_type(),
 
                       root_location,
 
                       FileDescriptorProto::kMessageTypeFieldNumber,
 
                       location);
 
  } else if (LookingAt("import")) {
 
    int index = file->dependency_size();
 
    return ParseImport(file->add_dependency(), root_location, index);
 
  } else if (LookingAt("package")) {
 
    return ParsePackage(file, root_location);
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(root_location,
 
        FileDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(file->mutable_options(), location);
 
  } else {
 
    AddError("Expected top-level statement (e.g. \"message\").");
 
    return false;
 
  }
 
}
 

	
 
// -------------------------------------------------------------------
 
// Messages
 

	
 
bool Parser::ParseMessageDefinition(DescriptorProto* message,
 
                                    const LocationRecorder& message_location) {
 
  DO(Consume("message"));
 
  {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        message, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(message->mutable_name(), "Expected message name."));
 
  }
 
  DO(ParseMessageBlock(message, message_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseMessageBlock(DescriptorProto* message,
 
                               const LocationRecorder& message_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in message definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseMessageStatement(message, message_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseMessageStatement(DescriptorProto* message,
 
                                   const LocationRecorder& message_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("message")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kNestedTypeFieldNumber,
 
                              message->nested_type_size());
 
    return ParseMessageDefinition(message->add_nested_type(), location);
 
  } else if (LookingAt("enum")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kEnumTypeFieldNumber,
 
                              message->enum_type_size());
 
    return ParseEnumDefinition(message->add_enum_type(), location);
 
  } else if (LookingAt("extensions")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kExtensionRangeFieldNumber);
 
    return ParseExtensions(message, location);
 
  } else if (LookingAt("extend")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kExtensionFieldNumber);
 
    return ParseExtend(message->mutable_extension(),
 
                       message->mutable_nested_type(),
 
                       message_location,
 
                       DescriptorProto::kNestedTypeFieldNumber,
 
                       location);
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(message->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(message_location,
 
                              DescriptorProto::kFieldFieldNumber,
 
                              message->field_size());
 
    return ParseMessageField(message->add_field(),
 
                             message->mutable_nested_type(),
 
                             message_location,
 
                             DescriptorProto::kNestedTypeFieldNumber,
 
                             location);
 
  }
 
}
 

	
 
bool Parser::ParseMessageField(FieldDescriptorProto* field,
 
                               RepeatedPtrField<DescriptorProto>* messages,
 
                               const LocationRecorder& parent_location,
 
                               int location_field_number_for_nested_type,
 
                               const LocationRecorder& field_location) {
 
  // Parse label and type.
 
  io::Tokenizer::Token label_token = input_->current();
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kLabelFieldNumber);
 
    FieldDescriptorProto::Label label;
 
    DO(ParseLabel(&label));
 
    field->set_label(label);
 
  }
 

	
 
  {
 
    LocationRecorder location(field_location);  // add path later
 
    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::TYPE);
 

	
 
    FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32;
 
    string type_name;
 
    DO(ParseType(&type, &type_name));
 
    if (type_name.empty()) {
 
      location.AddPath(FieldDescriptorProto::kTypeFieldNumber);
 
      field->set_type(type);
 
    } else {
 
      location.AddPath(FieldDescriptorProto::kTypeNameFieldNumber);
 
      field->set_type_name(type_name);
 
    }
 
  }
 

	
 
  // Parse name and '='.
 
  io::Tokenizer::Token name_token = input_->current();
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(field, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(field->mutable_name(), "Expected field name."));
 
  }
 
  DO(Consume("=", "Missing field number."));
 

	
 
  // Parse field number.
 
  {
 
    LocationRecorder location(field_location,
 
                              FieldDescriptorProto::kNumberFieldNumber);
 
    location.RecordLegacyLocation(
 
        field, DescriptorPool::ErrorCollector::NUMBER);
 
    int number;
 
    DO(ConsumeInteger(&number, "Expected field number."));
 
    field->set_number(number);
 
  }
 

	
 
  // Parse options.
 
  DO(ParseFieldOptions(field, field_location));
 

	
 
  // Deal with groups.
 
  if (field->has_type() && field->type() == FieldDescriptorProto::TYPE_GROUP) {
 
    // Awkward:  Since a group declares both a message type and a field, we
 
    //   have to create overlapping locations.
 
    LocationRecorder group_location(parent_location);
 
    group_location.StartAt(label_token);
 
    group_location.AddPath(location_field_number_for_nested_type);
 
    group_location.AddPath(messages->size());
 

	
 
    DescriptorProto* group = messages->Add();
 
    group->set_name(field->name());
 

	
 
    // Record name location to match the field name's location.
 
    {
 
      LocationRecorder location(group_location,
 
                                DescriptorProto::kNameFieldNumber);
 
      location.StartAt(name_token);
 
      location.EndAt(name_token);
 
      location.RecordLegacyLocation(
 
          group, DescriptorPool::ErrorCollector::NAME);
 
    }
 

	
 
    // The field's type_name also comes from the name.  Confusing!
 
    {
 
      LocationRecorder location(field_location,
 
                                FieldDescriptorProto::kTypeNameFieldNumber);
 
      location.StartAt(name_token);
 
      location.EndAt(name_token);
 
    }
 

	
 
    // As a hack for backwards-compatibility, we force the group name to start
 
    // with a capital letter and lower-case the field name.  New code should
 
    // not use groups; it should use nested messages.
 
    if (group->name()[0] < 'A' || 'Z' < group->name()[0]) {
 
      AddError(name_token.line, name_token.column,
 
        "Group names must start with a capital letter.");
 
    }
 
    LowerString(field->mutable_name());
 

	
 
    field->set_type_name(group->name());
 
    if (LookingAt("{")) {
 
      DO(ParseMessageBlock(group, group_location));
 
    } else {
 
      AddError("Missing group body.");
 
      return false;
 
    }
 
  } else {
 
    DO(Consume(";"));
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseFieldOptions(FieldDescriptorProto* field,
 
                               const LocationRecorder& field_location) {
 
  if (!LookingAt("[")) return true;
 

	
 
  LocationRecorder location(field_location,
 
                            FieldDescriptorProto::kOptionsFieldNumber);
 

	
 
  DO(Consume("["));
 

	
 
  // Parse field options.
 
  do {
 
    if (LookingAt("default")) {
 
      // We intentionally pass field_location rather than location here, since
 
      // the default value is not actually an option.
 
      DO(ParseDefaultAssignment(field, field_location));
 
    } else {
 
      DO(ParseOptionAssignment(field->mutable_options(), location));
 
    }
 
  } while (TryConsume(","));
 

	
 
  DO(Consume("]"));
 
  return true;
 
}
 

	
 
bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field,
 
                                    const LocationRecorder& field_location) {
 
  if (field->has_default_value()) {
 
    AddError("Already set option \"default\".");
 
    field->clear_default_value();
 
  }
 

	
 
  DO(Consume("default"));
 
  DO(Consume("="));
 

	
 
  LocationRecorder location(field_location,
 
                            FieldDescriptorProto::kDefaultValueFieldNumber);
 
  location.RecordLegacyLocation(
 
      field, DescriptorPool::ErrorCollector::DEFAULT_VALUE);
 
  string* default_value = field->mutable_default_value();
 

	
 
  if (!field->has_type()) {
 
    // The field has a type name, but we don't know if it is a message or an
 
    // enum yet.  Assume an enum for now.
 
    DO(ConsumeIdentifier(default_value, "Expected identifier."));
 
    return true;
 
  }
 

	
 
  switch (field->type()) {
 
    case FieldDescriptorProto::TYPE_INT32:
 
    case FieldDescriptorProto::TYPE_INT64:
 
    case FieldDescriptorProto::TYPE_SINT32:
 
    case FieldDescriptorProto::TYPE_SINT64:
 
    case FieldDescriptorProto::TYPE_SFIXED32:
 
    case FieldDescriptorProto::TYPE_SFIXED64: {
 
      uint64 max_value = kint64max;
 
      if (field->type() == FieldDescriptorProto::TYPE_INT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_SINT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_SFIXED32) {
 
        max_value = kint32max;
 
      }
 

	
 
      // These types can be negative.
 
      if (TryConsume("-")) {
 
        default_value->append("-");
 
        // Two's complement always has one more negative value than positive.
 
        ++max_value;
 
      }
 
      // Parse the integer to verify that it is not out-of-range.
 
      uint64 value;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      // And stringify it again.
 
      default_value->append(SimpleItoa(value));
 
      break;
 
    }
 

	
 
    case FieldDescriptorProto::TYPE_UINT32:
 
    case FieldDescriptorProto::TYPE_UINT64:
 
    case FieldDescriptorProto::TYPE_FIXED32:
 
    case FieldDescriptorProto::TYPE_FIXED64: {
 
      uint64 max_value = kuint64max;
 
      if (field->type() == FieldDescriptorProto::TYPE_UINT32 ||
 
          field->type() == FieldDescriptorProto::TYPE_FIXED32) {
 
        max_value = kuint32max;
 
      }
 

	
 
      // Numeric, not negative.
 
      if (TryConsume("-")) {
 
        AddError("Unsigned field can't have negative default value.");
 
      }
 
      // Parse the integer to verify that it is not out-of-range.
 
      uint64 value;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      // And stringify it again.
 
      default_value->append(SimpleItoa(value));
 
      break;
 
    }
 

	
 
    case FieldDescriptorProto::TYPE_FLOAT:
 
    case FieldDescriptorProto::TYPE_DOUBLE:
 
      // These types can be negative.
 
      if (TryConsume("-")) {
 
        default_value->append("-");
 
      }
 
      // Parse the integer because we have to convert hex integers to decimal
 
      // floats.
 
      double value;
 
      DO(ConsumeNumber(&value, "Expected number."));
 
      // And stringify it again.
 
      default_value->append(SimpleDtoa(value));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_BOOL:
 
      if (TryConsume("true")) {
 
        default_value->assign("true");
 
      } else if (TryConsume("false")) {
 
        default_value->assign("false");
 
      } else {
 
        AddError("Expected \"true\" or \"false\".");
 
        return false;
 
      }
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_STRING:
 
      DO(ConsumeString(default_value, "Expected string."));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_BYTES:
 
      DO(ConsumeString(default_value, "Expected string."));
 
      *default_value = CEscape(*default_value);
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_ENUM:
 
      DO(ConsumeIdentifier(default_value, "Expected identifier."));
 
      break;
 

	
 
    case FieldDescriptorProto::TYPE_MESSAGE:
 
    case FieldDescriptorProto::TYPE_GROUP:
 
      AddError("Messages can't have default values.");
 
      return false;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
 
                                 const LocationRecorder& part_location) {
 
  UninterpretedOption::NamePart* name = uninterpreted_option->add_name();
 
  string identifier;  // We parse identifiers into this string.
 
  if (LookingAt("(")) {  // This is an extension.
 
    DO(Consume("("));
 

	
 
    {
 
      LocationRecorder location(
 
          part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
 
      // An extension name consists of dot-separated identifiers, and may begin
 
      // with a dot.
 
      if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) {
 
        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
        name->mutable_name_part()->append(identifier);
 
      }
 
      while (LookingAt(".")) {
 
        DO(Consume("."));
 
        name->mutable_name_part()->append(".");
 
        DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
        name->mutable_name_part()->append(identifier);
 
      }
 
    }
 

	
 
    DO(Consume(")"));
 
    name->set_is_extension(true);
 
  } else {  // This is a regular field.
 
    LocationRecorder location(
 
        part_location, UninterpretedOption::NamePart::kNamePartFieldNumber);
 
    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
    name->mutable_name_part()->append(identifier);
 
    name->set_is_extension(false);
 
  }
 
  return true;
 
}
 

	
 
bool Parser::ParseUninterpretedBlock(string* value) {
 
  // Note that enclosing braces are not added to *value.
 
  DO(Consume("{"));
 
  int brace_depth = 1;
 
  while (!AtEnd()) {
 
    if (LookingAt("{")) {
 
      brace_depth++;
 
    } else if (LookingAt("}")) {
 
      brace_depth--;
 
      if (brace_depth == 0) {
 
        input_->Next();
 
        return true;
 
      }
 
    }
 
    // TODO(sanjay): Interpret line/column numbers to preserve formatting
 
    if (!value->empty()) value->push_back(' ');
 
    value->append(input_->current().text);
 
    input_->Next();
 
  }
 
  AddError("Unexpected end of stream while parsing aggregate value.");
 
  return false;
 
}
 

	
 
// We don't interpret the option here. Instead we store it in an
 
// UninterpretedOption, to be interpreted later.
 
bool Parser::ParseOptionAssignment(Message* options,
 
                                   const LocationRecorder& options_location) {
 
  // Create an entry in the uninterpreted_option field.
 
  const FieldDescriptor* uninterpreted_option_field = options->GetDescriptor()->
 
      FindFieldByName("uninterpreted_option");
 
  GOOGLE_CHECK(uninterpreted_option_field != NULL)
 
      << "No field named \"uninterpreted_option\" in the Options proto.";
 

	
 
  const Reflection* reflection = options->GetReflection();
 

	
 
  LocationRecorder location(
 
      options_location, uninterpreted_option_field->number(),
 
      reflection->FieldSize(*options, uninterpreted_option_field));
 

	
 
  UninterpretedOption* uninterpreted_option = down_cast<UninterpretedOption*>(
 
      options->GetReflection()->AddMessage(options,
 
                                           uninterpreted_option_field));
 

	
 
  // Parse dot-separated name.
 
  {
 
    LocationRecorder name_location(location,
 
                                   UninterpretedOption::kNameFieldNumber);
 
    name_location.RecordLegacyLocation(
 
        uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_NAME);
 

	
 
    {
 
      LocationRecorder part_location(name_location,
 
                                     uninterpreted_option->name_size());
 
      DO(ParseOptionNamePart(uninterpreted_option, part_location));
 
    }
 

	
 
    while (LookingAt(".")) {
 
      DO(Consume("."));
 
      LocationRecorder part_location(name_location,
 
                                     uninterpreted_option->name_size());
 
      DO(ParseOptionNamePart(uninterpreted_option, part_location));
 
    }
 
  }
 

	
 
  DO(Consume("="));
 

	
 
  LocationRecorder value_location(location);
 
  value_location.RecordLegacyLocation(
 
      uninterpreted_option, DescriptorPool::ErrorCollector::OPTION_VALUE);
 

	
 
  // All values are a single token, except for negative numbers, which consist
 
  // of a single '-' symbol, followed by a positive number.
 
  bool is_negative = TryConsume("-");
 

	
 
  switch (input_->current().type) {
 
    case io::Tokenizer::TYPE_START:
 
      GOOGLE_LOG(FATAL) << "Trying to read value before any tokens have been read.";
 
      return false;
 

	
 
    case io::Tokenizer::TYPE_END:
 
      AddError("Unexpected end of stream while parsing option value.");
 
      return false;
 

	
 
    case io::Tokenizer::TYPE_IDENTIFIER: {
 
      value_location.AddPath(UninterpretedOption::kIdentifierValueFieldNumber);
 
      if (is_negative) {
 
        AddError("Invalid '-' symbol before identifier.");
 
        return false;
 
      }
 
      string value;
 
      DO(ConsumeIdentifier(&value, "Expected identifier."));
 
      uninterpreted_option->set_identifier_value(value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_INTEGER: {
 
      uint64 value;
 
      uint64 max_value =
 
          is_negative ? static_cast<uint64>(kint64max) + 1 : kuint64max;
 
      DO(ConsumeInteger64(max_value, &value, "Expected integer."));
 
      if (is_negative) {
 
        value_location.AddPath(
 
            UninterpretedOption::kNegativeIntValueFieldNumber);
 
        uninterpreted_option->set_negative_int_value(-static_cast<int64>(value));
 
      } else {
 
        value_location.AddPath(
 
            UninterpretedOption::kPositiveIntValueFieldNumber);
 
        uninterpreted_option->set_positive_int_value(value);
 
      }
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_FLOAT: {
 
      value_location.AddPath(UninterpretedOption::kDoubleValueFieldNumber);
 
      double value;
 
      DO(ConsumeNumber(&value, "Expected number."));
 
      uninterpreted_option->set_double_value(is_negative ? -value : value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_STRING: {
 
      value_location.AddPath(UninterpretedOption::kStringValueFieldNumber);
 
      if (is_negative) {
 
        AddError("Invalid '-' symbol before string.");
 
        return false;
 
      }
 
      string value;
 
      DO(ConsumeString(&value, "Expected string."));
 
      uninterpreted_option->set_string_value(value);
 
      break;
 
    }
 

	
 
    case io::Tokenizer::TYPE_SYMBOL:
 
      if (LookingAt("{")) {
 
        value_location.AddPath(UninterpretedOption::kAggregateValueFieldNumber);
 
        DO(ParseUninterpretedBlock(
 
            uninterpreted_option->mutable_aggregate_value()));
 
      } else {
 
        AddError("Expected option value.");
 
        return false;
 
      }
 
      break;
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseExtensions(DescriptorProto* message,
 
                             const LocationRecorder& extensions_location) {
 
  // Parse the declaration.
 
  DO(Consume("extensions"));
 

	
 
  do {
 
    // Note that kExtensionRangeFieldNumber was already pushed by the parent.
 
    LocationRecorder location(extensions_location,
 
                              message->extension_range_size());
 

	
 
    DescriptorProto::ExtensionRange* range = message->add_extension_range();
 
    location.RecordLegacyLocation(
 
        range, DescriptorPool::ErrorCollector::NUMBER);
 

	
 
    int start, end;
 
    io::Tokenizer::Token start_token;
 

	
 
    {
 
      LocationRecorder start_location(
 
          location, DescriptorProto::ExtensionRange::kStartFieldNumber);
 
      start_token = input_->current();
 
      DO(ConsumeInteger(&start, "Expected field number range."));
 
    }
 

	
 
    if (TryConsume("to")) {
 
      LocationRecorder end_location(
 
          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
 
      if (TryConsume("max")) {
 
        end = FieldDescriptor::kMaxNumber;
 
      } else {
 
        DO(ConsumeInteger(&end, "Expected integer."));
 
      }
 
    } else {
 
      LocationRecorder end_location(
 
          location, DescriptorProto::ExtensionRange::kEndFieldNumber);
 
      end_location.StartAt(start_token);
 
      end_location.EndAt(start_token);
 
      end = start;
 
    }
 

	
 
    // Users like to specify inclusive ranges, but in code we like the end
 
    // number to be exclusive.
 
    ++end;
 

	
 
    range->set_start(start);
 
    range->set_end(end);
 
  } while (TryConsume(","));
 

	
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
 
                         RepeatedPtrField<DescriptorProto>* messages,
 
                         const LocationRecorder& parent_location,
 
                         int location_field_number_for_nested_type,
 
                         const LocationRecorder& extend_location) {
 
  DO(Consume("extend"));
 

	
 
  // Parse the extendee type.
 
  io::Tokenizer::Token extendee_start = input_->current();
 
  string extendee;
 
  DO(ParseUserDefinedType(&extendee));
 
  io::Tokenizer::Token extendee_end = input_->previous();
 

	
 
  // Parse the block.
 
  DO(Consume("{"));
 

	
 
  bool is_first = true;
 

	
 
  do {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in extend definition (missing '}').");
 
      return false;
 
    }
 

	
 
    // Note that kExtensionFieldNumber was already pushed by the parent.
 
    LocationRecorder location(extend_location, extensions->size());
 

	
 
    FieldDescriptorProto* field = extensions->Add();
 

	
 
    {
 
      LocationRecorder extendee_location(
 
          location, FieldDescriptorProto::kExtendeeFieldNumber);
 
      extendee_location.StartAt(extendee_start);
 
      extendee_location.EndAt(extendee_end);
 

	
 
      if (is_first) {
 
        extendee_location.RecordLegacyLocation(
 
            field, DescriptorPool::ErrorCollector::EXTENDEE);
 
        is_first = false;
 
      }
 
    }
 

	
 
    field->set_extendee(extendee);
 

	
 
    if (!ParseMessageField(field, messages, parent_location,
 
                           location_field_number_for_nested_type,
 
                           location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  } while(!TryConsume("}"));
 

	
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 
// Enums
 

	
 
bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type,
 
                                 const LocationRecorder& enum_location) {
 
  DO(Consume("enum"));
 

	
 
  {
 
    LocationRecorder location(enum_location,
 
                              EnumDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_type, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name."));
 
  }
 

	
 
  DO(ParseEnumBlock(enum_type, enum_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type,
 
                            const LocationRecorder& enum_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in enum definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseEnumStatement(enum_type, enum_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type,
 
                                const LocationRecorder& enum_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(enum_location,
 
                              EnumDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(enum_type->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(enum_location,
 
        EnumDescriptorProto::kValueFieldNumber, enum_type->value_size());
 
    return ParseEnumConstant(enum_type->add_value(), location);
 
  }
 
}
 

	
 
bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value,
 
                               const LocationRecorder& enum_value_location) {
 
  // Parse name.
 
  {
 
    LocationRecorder location(enum_value_location,
 
                              EnumValueDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_value, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(enum_value->mutable_name(),
 
                         "Expected enum constant name."));
 
  }
 

	
 
  DO(Consume("=", "Missing numeric value for enum constant."));
 

	
 
  // Parse value.
 
  {
 
    LocationRecorder location(
 
        enum_value_location, EnumValueDescriptorProto::kNumberFieldNumber);
 
    location.RecordLegacyLocation(
 
        enum_value, DescriptorPool::ErrorCollector::NUMBER);
 

	
 
    bool is_negative = TryConsume("-");
 
    int number;
 
    DO(ConsumeInteger(&number, "Expected integer."));
 
    if (is_negative) number *= -1;
 
    enum_value->set_number(number);
 
  }
 

	
 
  DO(ParseEnumConstantOptions(enum_value, enum_value_location));
 

	
 
  DO(Consume(";"));
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseEnumConstantOptions(
 
    EnumValueDescriptorProto* value,
 
    const LocationRecorder& enum_value_location) {
 
  if (!LookingAt("[")) return true;
 

	
 
  LocationRecorder location(
 
      enum_value_location, EnumValueDescriptorProto::kOptionsFieldNumber);
 

	
 
  DO(Consume("["));
 

	
 
  do {
 
    DO(ParseOptionAssignment(value->mutable_options(), location));
 
  } while (TryConsume(","));
 

	
 
  DO(Consume("]"));
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 
// Services
 

	
 
bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service,
 
                                    const LocationRecorder& service_location) {
 
  DO(Consume("service"));
 

	
 
  {
 
    LocationRecorder location(service_location,
 
                              ServiceDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        service, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(service->mutable_name(), "Expected service name."));
 
  }
 

	
 
  DO(ParseServiceBlock(service, service_location));
 
  return true;
 
}
 

	
 
bool Parser::ParseServiceBlock(ServiceDescriptorProto* service,
 
                               const LocationRecorder& service_location) {
 
  DO(Consume("{"));
 

	
 
  while (!TryConsume("}")) {
 
    if (AtEnd()) {
 
      AddError("Reached end of input in service definition (missing '}').");
 
      return false;
 
    }
 

	
 
    if (!ParseServiceStatement(service, service_location)) {
 
      // This statement failed to parse.  Skip it, but keep looping to parse
 
      // other statements.
 
      SkipStatement();
 
    }
 
  }
 

	
 
  return true;
 
}
 

	
 
bool Parser::ParseServiceStatement(ServiceDescriptorProto* service,
 
                                   const LocationRecorder& service_location) {
 
  if (TryConsume(";")) {
 
    // empty statement; ignore
 
    return true;
 
  } else if (LookingAt("option")) {
 
    LocationRecorder location(
 
        service_location, ServiceDescriptorProto::kOptionsFieldNumber);
 
    return ParseOption(service->mutable_options(), location);
 
  } else {
 
    LocationRecorder location(service_location,
 
        ServiceDescriptorProto::kMethodFieldNumber, service->method_size());
 
    return ParseServiceMethod(service->add_method(), location);
 
  }
 
}
 

	
 
bool Parser::ParseServiceMethod(MethodDescriptorProto* method,
 
                                const LocationRecorder& method_location) {
 
  DO(Consume("rpc"));
 

	
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kNameFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::NAME);
 
    DO(ConsumeIdentifier(method->mutable_name(), "Expected method name."));
 
  }
 

	
 
  // Parse input type.
 
  DO(Consume("("));
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kInputTypeFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::INPUT_TYPE);
 
    DO(ParseUserDefinedType(method->mutable_input_type()));
 
  }
 
  DO(Consume(")"));
 

	
 
  // Parse output type.
 
  DO(Consume("returns"));
 
  DO(Consume("("));
 
  {
 
    LocationRecorder location(method_location,
 
                              MethodDescriptorProto::kOutputTypeFieldNumber);
 
    location.RecordLegacyLocation(
 
        method, DescriptorPool::ErrorCollector::OUTPUT_TYPE);
 
    DO(ParseUserDefinedType(method->mutable_output_type()));
 
  }
 
  DO(Consume(")"));
 

	
 
  if (TryConsume("{")) {
 
    // Options!
 
    while (!TryConsume("}")) {
 
      if (AtEnd()) {
 
        AddError("Reached end of input in method options (missing '}').");
 
        return false;
 
      }
 

	
 
      if (TryConsume(";")) {
 
        // empty statement; ignore
 
      } else {
 
        LocationRecorder location(method_location,
 
                                  MethodDescriptorProto::kOptionsFieldNumber);
 
        if (!ParseOption(method->mutable_options(), location)) {
 
          // This statement failed to parse.  Skip it, but keep looping to
 
          // parse other statements.
 
          SkipStatement();
 
        }
 
      }
 
    }
 
  } else {
 
    DO(Consume(";"));
 
  }
 

	
 
  return true;
 
}
 

	
 
// -------------------------------------------------------------------
 

	
 
bool Parser::ParseLabel(FieldDescriptorProto::Label* label) {
 
  if (TryConsume("optional")) {
 
    *label = FieldDescriptorProto::LABEL_OPTIONAL;
 
    return true;
 
  } else if (TryConsume("repeated")) {
 
    *label = FieldDescriptorProto::LABEL_REPEATED;
 
    return true;
 
  } else if (TryConsume("required")) {
 
    *label = FieldDescriptorProto::LABEL_REQUIRED;
 
    return true;
 
  } else {
 
    AddError("Expected \"required\", \"optional\", or \"repeated\".");
 
    // We can actually reasonably recover here by just assuming the user
 
    // forgot the label altogether.
 
    *label = FieldDescriptorProto::LABEL_OPTIONAL;
 
    return true;
 
  }
 
}
 

	
 
bool Parser::ParseType(FieldDescriptorProto::Type* type,
 
                       string* type_name) {
 
  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
 
  if (iter != kTypeNames.end()) {
 
    *type = iter->second;
 
    input_->Next();
 
  } else {
 
    DO(ParseUserDefinedType(type_name));
 
  }
 
  return true;
 
}
 

	
 
bool Parser::ParseUserDefinedType(string* type_name) {
 
  type_name->clear();
 

	
 
  TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text);
 
  if (iter != kTypeNames.end()) {
 
    // Note:  The only place enum types are allowed is for field types, but
 
    //   if we are parsing a field type then we would not get here because
 
    //   primitives are allowed there as well.  So this error message doesn't
 
    //   need to account for enums.
 
    AddError("Expected message type.");
 

	
 
    // Pretend to accept this type so that we can go on parsing.
 
    *type_name = input_->current().text;
 
    input_->Next();
 
    return true;
 
  }
 

	
 
  // A leading "." means the name is fully-qualified.
 
  if (TryConsume(".")) type_name->append(".");
 

	
 
  // Consume the first part of the name.
 
  string identifier;
 
  DO(ConsumeIdentifier(&identifier, "Expected type name."));
 
  type_name->append(identifier);
 

	
 
  // Consume more parts.
 
  while (TryConsume(".")) {
 
    type_name->append(".");
 
    DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
    type_name->append(identifier);
 
  }
 

	
 
  return true;
 
}
 

	
 
// ===================================================================
 

	
 
bool Parser::ParsePackage(FileDescriptorProto* file,
 
                          const LocationRecorder& root_location) {
 
  if (file->has_package()) {
 
    AddError("Multiple package definitions.");
 
    // Don't append the new package to the old one.  Just replace it.  Not
 
    // that it really matters since this is an error anyway.
 
    file->clear_package();
 
  }
 

	
 
  DO(Consume("package"));
 

	
 
  {
 
    LocationRecorder location(root_location,
 
                              FileDescriptorProto::kPackageFieldNumber);
 
    location.RecordLegacyLocation(file, DescriptorPool::ErrorCollector::NAME);
 

	
 
    while (true) {
 
      string identifier;
 
      DO(ConsumeIdentifier(&identifier, "Expected identifier."));
 
      file->mutable_package()->append(identifier);
 
      if (!TryConsume(".")) break;
 
      file->mutable_package()->append(".");
 
    }
 
  }
 

	
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseImport(string* import_filename,
 
                         const LocationRecorder& root_location,
 
                         int index) {
 
  DO(Consume("import"));
 
  {
 
    LocationRecorder location(root_location,
 
                              FileDescriptorProto::kDependencyFieldNumber,
 
                              index);
 
    DO(ConsumeString(import_filename,
 
      "Expected a string naming the file to import."));
 
  }
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
bool Parser::ParseOption(Message* options,
 
                         const LocationRecorder& options_location) {
 
  DO(Consume("option"));
 
  DO(ParseOptionAssignment(options, options_location));
 
  DO(Consume(";"));
 
  return true;
 
}
 

	
 
// ===================================================================
 

	
 
SourceLocationTable::SourceLocationTable() {}
 
SourceLocationTable::~SourceLocationTable() {}
 

	
 
bool SourceLocationTable::Find(
 
    const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location,
 
    int* line, int* column) const {
 
  const pair<int, int>* result =
 
    FindOrNull(location_map_, make_pair(descriptor, location));
 
  if (result == NULL) {
 
    *line   = -1;
 
    *column = 0;
 
    return false;
 
  } else {
 
    *line   = result->first;
 
    *column = result->second;
 
    return true;
 
  }
 
}
 

	
 
void SourceLocationTable::Add(
 
    const Message* descriptor,
 
    DescriptorPool::ErrorCollector::ErrorLocation location,
 
    int line, int column) {
 
  location_map_[make_pair(descriptor, location)] = make_pair(line, column);
 
}
 

	
 
void SourceLocationTable::Clear() {
 
  location_map_.clear();
 
}
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 
}  // namespace google
msvc-deps/protobuf/libprotobuf/google/protobuf/compiler/parser.h
Show inline comments
 
new file 100644
 
// Protocol Buffers - Google's data interchange format
 
// Copyright 2008 Google Inc.  All rights reserved.
 
// http://code.google.com/p/protobuf/
 
//
 
// Redistribution and use in source and binary forms, with or without
 
// modification, are permitted provided that the following conditions are
 
// met:
 
//
 
//     * Redistributions of source code must retain the above copyright
 
// notice, this list of conditions and the following disclaimer.
 
//     * Redistributions in binary form must reproduce the above
 
// copyright notice, this list of conditions and the following disclaimer
 
// in the documentation and/or other materials provided with the
 
// distribution.
 
//     * Neither the name of Google Inc. nor the names of its
 
// contributors may be used to endorse or promote products derived from
 
// this software without specific prior written permission.
 
//
 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

	
 
// Author: kenton@google.com (Kenton Varda)
 
//  Based on original Protocol Buffers design by
 
//  Sanjay Ghemawat, Jeff Dean, and others.
 
//
 
// Implements parsing of .proto files to FileDescriptorProtos.
 

	
 
#ifndef GOOGLE_PROTOBUF_COMPILER_PARSER_H__
 
#define GOOGLE_PROTOBUF_COMPILER_PARSER_H__
 

	
 
#include <map>
 
#include <string>
 
#include <utility>
 
#include <google/protobuf/stubs/common.h>
 
#include <google/protobuf/descriptor.h>
 
#include <google/protobuf/descriptor.pb.h>
 
#include <google/protobuf/repeated_field.h>
 
#include <google/protobuf/io/tokenizer.h>
 

	
 
namespace google {
 
namespace protobuf { class Message; }
 

	
 
namespace protobuf {
 
namespace compiler {
 

	
 
// Defined in this file.
 
class Parser;
 
class SourceLocationTable;
 

	
 
// Implements parsing of protocol definitions (such as .proto files).
 
//
 
// Note that most users will be more interested in the Importer class.
 
// Parser is a lower-level class which simply converts a single .proto file
 
// to a FileDescriptorProto.  It does not resolve import directives or perform
 
// many other kinds of validation needed to construct a complete
 
// FileDescriptor.
 
class LIBPROTOBUF_EXPORT Parser {
 
 public:
 
  Parser();
 
  ~Parser();
 

	
 
  // Parse the entire input and construct a FileDescriptorProto representing
 
  // it.  Returns true if no errors occurred, false otherwise.
 
  bool Parse(io::Tokenizer* input, FileDescriptorProto* file);
 

	
 
  // Optional fetaures:
 

	
 
  // DEPRECATED:  New code should use the SourceCodeInfo embedded in the
 
  //   FileDescriptorProto.
 
  //
 
  // Requests that locations of certain definitions be recorded to the given
 
  // SourceLocationTable while parsing.  This can be used to look up exact line
 
  // and column numbers for errors reported by DescriptorPool during validation.
 
  // Set to NULL (the default) to discard source location information.
 
  void RecordSourceLocationsTo(SourceLocationTable* location_table) {
 
    source_location_table_ = location_table;
 
  }
 

	
 
  // Requests that errors be recorded to the given ErrorCollector while
 
  // parsing.  Set to NULL (the default) to discard error messages.
 
  void RecordErrorsTo(io::ErrorCollector* error_collector) {
 
    error_collector_ = error_collector;
 
  }
 

	
 
  // Returns the identifier used in the "syntax = " declaration, if one was
 
  // seen during the last call to Parse(), or the empty string otherwise.
 
  const string& GetSyntaxIdentifier() { return syntax_identifier_; }
 

	
 
  // If set true, input files will be required to begin with a syntax
 
  // identifier.  Otherwise, files may omit this.  If a syntax identifier
 
  // is provided, it must be 'syntax = "proto2";' and must appear at the
 
  // top of this file regardless of whether or not it was required.
 
  void SetRequireSyntaxIdentifier(bool value) {
 
    require_syntax_identifier_ = value;
 
  }
 

	
 
  // Call SetStopAfterSyntaxIdentifier(true) to tell the parser to stop
 
  // parsing as soon as it has seen the syntax identifier, or lack thereof.
 
  // This is useful for quickly identifying the syntax of the file without
 
  // parsing the whole thing.  If this is enabled, no error will be recorded
 
  // if the syntax identifier is something other than "proto2" (since
 
  // presumably the caller intends to deal with that), but other kinds of
 
  // errors (e.g. parse errors) will still be reported.  When this is enabled,
 
  // you may pass a NULL FileDescriptorProto to Parse().
 
  void SetStopAfterSyntaxIdentifier(bool value) {
 
    stop_after_syntax_identifier_ = value;
 
  }
 

	
 
 private:
 
  // =================================================================
 
  // Error recovery helpers
 

	
 
  // Consume the rest of the current statement.  This consumes tokens
 
  // until it sees one of:
 
  //   ';'  Consumes the token and returns.
 
  //   '{'  Consumes the brace then calls SkipRestOfBlock().
 
  //   '}'  Returns without consuming.
 
  //   EOF  Returns (can't consume).
 
  // The Parser often calls SkipStatement() after encountering a syntax
 
  // error.  This allows it to go on parsing the following lines, allowing
 
  // it to report more than just one error in the file.
 
  void SkipStatement();
 

	
 
  // Consume the rest of the current block, including nested blocks,
 
  // ending after the closing '}' is encountered and consumed, or at EOF.
 
  void SkipRestOfBlock();
 

	
 
  // -----------------------------------------------------------------
 
  // Single-token consuming helpers
 
  //
 
  // These make parsing code more readable.
 

	
 
  // True if the current token is TYPE_END.
 
  inline bool AtEnd();
 

	
 
  // True if the next token matches the given text.
 
  inline bool LookingAt(const char* text);
 
  // True if the next token is of the given type.
 
  inline bool LookingAtType(io::Tokenizer::TokenType token_type);
 

	
 
  // If the next token exactly matches the text given, consume it and return
 
  // true.  Otherwise, return false without logging an error.
 
  bool TryConsume(const char* text);
 

	
 
  // These attempt to read some kind of token from the input.  If successful,
 
  // they return true.  Otherwise they return false and add the given error
 
  // to the error list.
 

	
 
  // Consume a token with the exact text given.
 
  bool Consume(const char* text, const char* error);
 
  // Same as above, but automatically generates the error "Expected \"text\".",
 
  // where "text" is the expected token text.
 
  bool Consume(const char* text);
 
  // Consume a token of type IDENTIFIER and store its text in "output".
 
  bool ConsumeIdentifier(string* output, const char* error);
 
  // Consume an integer and store its value in "output".
 
  bool ConsumeInteger(int* output, const char* error);
 
  // Consume a 64-bit integer and store its value in "output".  If the value
 
  // is greater than max_value, an error will be reported.
 
  bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error);
 
  // Consume a number and store its value in "output".  This will accept
 
  // tokens of either INTEGER or FLOAT type.
 
  bool ConsumeNumber(double* output, const char* error);
 
  // Consume a string literal and store its (unescaped) value in "output".
 
  bool ConsumeString(string* output, const char* error);
 

	
 
  // -----------------------------------------------------------------
 
  // Error logging helpers
 

	
 
  // Invokes error_collector_->AddError(), if error_collector_ is not NULL.
 
  void AddError(int line, int column, const string& error);
 

	
 
  // Invokes error_collector_->AddError() with the line and column number
 
  // of the current token.
 
  void AddError(const string& error);
 

	
 
  // Records a location in the SourceCodeInfo.location table (see
 
  // descriptor.proto).  We use RAII to ensure that the start and end locations
 
  // are recorded -- the constructor records the start location and the
 
  // destructor records the end location.  Since the parser is
 
  // recursive-descent, this works out beautifully.
 
  class LIBPROTOBUF_EXPORT LocationRecorder {
 
   public:
 
    // Construct the file's "root" location.
 
    LocationRecorder(Parser* parser);
 

	
 
    // Construct a location that represents a declaration nested within the
 
    // given parent.  E.g. a field's location is nested within the location
 
    // for a message type.  The parent's path will be copied, so you should
 
    // call AddPath() only to add the path components leading from the parent
 
    // to the child (as opposed to leading from the root to the child).
 
    LocationRecorder(const LocationRecorder& parent);
 

	
 
    // Convenience constructors that call AddPath() one or two times.
 
    LocationRecorder(const LocationRecorder& parent, int path1);
 
    LocationRecorder(const LocationRecorder& parent, int path1, int path2);
 

	
 
    ~LocationRecorder();
 

	
 
    // Add a path component.  See SourceCodeInfo.Location.path in
 
    // descriptor.proto.
 
    void AddPath(int path_component);
 

	
 
    // By default the location is considered to start at the current token at
 
    // the time the LocationRecorder is created.  StartAt() sets the start
 
    // location to the given token instead.
 
    void StartAt(const io::Tokenizer::Token& token);
 

	
 
    // By default the location is considered to end at the previous token at
 
    // the time the LocationRecorder is destroyed.  EndAt() sets the end
 
    // location to the given token instead.
 
    void EndAt(const io::Tokenizer::Token& token);
 

	
 
    // Records the start point of this location to the SourceLocationTable that
 
    // was passed to RecordSourceLocationsTo(), if any.  SourceLocationTable
 
    // is an older way of keeping track of source locations which is still
 
    // used in some places.
 
    void RecordLegacyLocation(const Message* descriptor,
 
        DescriptorPool::ErrorCollector::ErrorLocation location);
 

	
 
   private:
 
    Parser* parser_;
 
    SourceCodeInfo::Location* location_;
 

	
 
    void Init(const LocationRecorder& parent);
 
  };
 

	
 
  // =================================================================
 
  // Parsers for various language constructs
 

	
 
  // Parses the "syntax = \"proto2\";" line at the top of the file.  Returns
 
  // false if it failed to parse or if the syntax identifier was not
 
  // recognized.
 
  bool ParseSyntaxIdentifier();
 

	
 
  // These methods parse various individual bits of code.  They return
 
  // false if they completely fail to parse the construct.  In this case,
 
  // it is probably necessary to skip the rest of the statement to recover.
 
  // However, if these methods return true, it does NOT mean that there
 
  // were no errors; only that there were no *syntax* errors.  For instance,
 
  // if a service method is defined using proper syntax but uses a primitive
 
  // type as its input or output, ParseMethodField() still returns true
 
  // and only reports the error by calling AddError().  In practice, this
 
  // makes logic much simpler for the caller.
 

	
 
  // Parse a top-level message, enum, service, etc.
 
  bool ParseTopLevelStatement(FileDescriptorProto* file,
 
                              const LocationRecorder& root_location);
 

	
 
  // Parse various language high-level language construrcts.
 
  bool ParseMessageDefinition(DescriptorProto* message,
 
                              const LocationRecorder& message_location);
 
  bool ParseEnumDefinition(EnumDescriptorProto* enum_type,
 
                           const LocationRecorder& enum_location);
 
  bool ParseServiceDefinition(ServiceDescriptorProto* service,
 
                              const LocationRecorder& service_location);
 
  bool ParsePackage(FileDescriptorProto* file,
 
                    const LocationRecorder& root_location);
 
  bool ParseImport(string* import_filename,
 
                   const LocationRecorder& root_location,
 
                   int index);
 
  bool ParseOption(Message* options,
 
                   const LocationRecorder& options_location);
 

	
 
  // These methods parse the contents of a message, enum, or service type and
 
  // add them to the given object.  They consume the entire block including
 
  // the beginning and ending brace.
 
  bool ParseMessageBlock(DescriptorProto* message,
 
                         const LocationRecorder& message_location);
 
  bool ParseEnumBlock(EnumDescriptorProto* enum_type,
 
                      const LocationRecorder& enum_location);
 
  bool ParseServiceBlock(ServiceDescriptorProto* service,
 
                         const LocationRecorder& service_location);
 

	
 
  // Parse one statement within a message, enum, or service block, inclunding
 
  // final semicolon.
 
  bool ParseMessageStatement(DescriptorProto* message,
 
                             const LocationRecorder& message_location);
 
  bool ParseEnumStatement(EnumDescriptorProto* message,
 
                          const LocationRecorder& enum_location);
 
  bool ParseServiceStatement(ServiceDescriptorProto* message,
 
                             const LocationRecorder& service_location);
 

	
 
  // Parse a field of a message.  If the field is a group, its type will be
 
  // added to "messages".
 
  //
 
  // parent_location and location_field_number_for_nested_type are needed when
 
  // parsing groups -- we need to generate a nested message type within the
 
  // parent and record its location accordingly.  Since the parent could be
 
  // either a FileDescriptorProto or a DescriptorProto, we must pass in the
 
  // correct field number to use.
 
  bool ParseMessageField(FieldDescriptorProto* field,
 
                         RepeatedPtrField<DescriptorProto>* messages,
 
                         const LocationRecorder& parent_location,
 
                         int location_field_number_for_nested_type,
 
                         const LocationRecorder& field_location);
 

	
 
  // Parse an "extensions" declaration.
 
  bool ParseExtensions(DescriptorProto* message,
 
                       const LocationRecorder& extensions_location);
 

	
 
  // Parse an "extend" declaration.  (See also comments for
 
  // ParseMessageField().)
 
  bool ParseExtend(RepeatedPtrField<FieldDescriptorProto>* extensions,
 
                   RepeatedPtrField<DescriptorProto>* messages,
 
                   const LocationRecorder& parent_location,
 
                   int location_field_number_for_nested_type,
 
                   const LocationRecorder& extend_location);
 

	
 
  // Parse a single enum value within an enum block.
 
  bool ParseEnumConstant(EnumValueDescriptorProto* enum_value,
 
                         const LocationRecorder& enum_value_location);
 

	
 
  // Parse enum constant options, i.e. the list in square brackets at the end
 
  // of the enum constant value definition.
 
  bool ParseEnumConstantOptions(EnumValueDescriptorProto* value,
 
                                const LocationRecorder& enum_value_location);
 

	
 
  // Parse a single method within a service definition.
 
  bool ParseServiceMethod(MethodDescriptorProto* method,
 
                          const LocationRecorder& method_location);
 

	
 
  // Parse "required", "optional", or "repeated" and fill in "label"
 
  // with the value.
 
  bool ParseLabel(FieldDescriptorProto::Label* label);
 

	
 
  // Parse a type name and fill in "type" (if it is a primitive) or
 
  // "type_name" (if it is not) with the type parsed.
 
  bool ParseType(FieldDescriptorProto::Type* type,
 
                 string* type_name);
 
  // Parse a user-defined type and fill in "type_name" with the name.
 
  // If a primitive type is named, it is treated as an error.
 
  bool ParseUserDefinedType(string* type_name);
 

	
 
  // Parses field options, i.e. the stuff in square brackets at the end
 
  // of a field definition.  Also parses default value.
 
  bool ParseFieldOptions(FieldDescriptorProto* field,
 
                         const LocationRecorder& field_location);
 

	
 
  // Parse the "default" option.  This needs special handling because its
 
  // type is the field's type.
 
  bool ParseDefaultAssignment(FieldDescriptorProto* field,
 
                              const LocationRecorder& field_location);
 

	
 
  // Parse a single option name/value pair, e.g. "ctype = CORD".  The name
 
  // identifies a field of the given Message, and the value of that field
 
  // is set to the parsed value.
 
  bool ParseOptionAssignment(Message* options,
 
                             const LocationRecorder& options_location);
 

	
 
  // Parses a single part of a multipart option name. A multipart name consists
 
  // of names separated by dots. Each name is either an identifier or a series
 
  // of identifiers separated by dots and enclosed in parentheses. E.g.,
 
  // "foo.(bar.baz).qux".
 
  bool ParseOptionNamePart(UninterpretedOption* uninterpreted_option,
 
                           const LocationRecorder& part_location);
 

	
 
  // Parses a string surrounded by balanced braces.  Strips off the outer
 
  // braces and stores the enclosed string in *value.
 
  // E.g.,
 
  //     { foo }                     *value gets 'foo'
 
  //     { foo { bar: box } }        *value gets 'foo { bar: box }'
 
  //     {}                          *value gets ''
 
  //
 
  // REQUIRES: LookingAt("{")
 
  // When finished successfully, we are looking at the first token past
 
  // the ending brace.
 
  bool ParseUninterpretedBlock(string* value);
 

	
 
  // =================================================================
 

	
 
  io::Tokenizer* input_;
 
  io::ErrorCollector* error_collector_;
 
  SourceCodeInfo* source_code_info_;
 
  SourceLocationTable* source_location_table_;  // legacy
 
  bool had_errors_;
 
  bool require_syntax_identifier_;
 
  bool stop_after_syntax_identifier_;
 
  string syntax_identifier_;
 

	
 
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser);
 
};
 

	
 
// A table mapping (descriptor, ErrorLocation) pairs -- as reported by
 
// DescriptorPool when validating descriptors -- to line and column numbers
 
// within the original source code.
 
//
 
// This is semi-obsolete:  FileDescriptorProto.source_code_info now contains
 
// far more complete information about source locations.  However, as of this
 
// writing you still need to use SourceLocationTable when integrating with
 
// DescriptorPool.
 
class LIBPROTOBUF_EXPORT SourceLocationTable {
 
 public:
 
  SourceLocationTable();
 
  ~SourceLocationTable();
 

	
 
  // Finds the precise location of the given error and fills in *line and
 
  // *column with the line and column numbers.  If not found, sets *line to
 
  // -1 and *column to 0 (since line = -1 is used to mean "error has no exact
 
  // location" in the ErrorCollector interface).  Returns true if found, false
 
  // otherwise.
 
  bool Find(const Message* descriptor,
 
            DescriptorPool::ErrorCollector::ErrorLocation location,
 
            int* line, int* column) const;
 

	
 
  // Adds a location to the table.
 
  void Add(const Message* descriptor,
 
           DescriptorPool::ErrorCollector::ErrorLocation location,
 
           int line, int column);
 

	
 
  // Clears the contents of the table.
 
  void Clear();
 

	
 
 private:
 
  typedef map<
 
    pair<const Message*, DescriptorPool::ErrorCollector::ErrorLocation>,
 
    pair<int, int> > LocationMap;
 
  LocationMap location_map_;
 
};
 

	
 
}  // namespace compiler
 
}  // namespace protobuf
 

	
 
}  // namespace google
 
#endif  // GOOGLE_PROTOBUF_COMPILER_PARSER_H__

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)