mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-03 20:31:23 +00:00
Added .proto parsing and convertion to .fbs.
Bug: 15777858 Change-Id: Iabef9b8c8044e593bb89510feebdee00d2f1840b Tested: on Linux and Windows.
This commit is contained in:
@@ -280,20 +280,24 @@ void Parser::Expect(int t) {
|
||||
Next();
|
||||
}
|
||||
|
||||
void Parser::ParseTypeIdent(Type &type) {
|
||||
auto enum_def = enums_.Lookup(attribute_);
|
||||
if (enum_def) {
|
||||
type = enum_def->underlying_type;
|
||||
if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
|
||||
} else {
|
||||
type.base_type = BASE_TYPE_STRUCT;
|
||||
type.struct_def = LookupCreateStruct(attribute_);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse any IDL type.
|
||||
void Parser::ParseType(Type &type) {
|
||||
if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) {
|
||||
type.base_type = static_cast<BaseType>(token_ - kTokenNONE);
|
||||
} else {
|
||||
if (token_ == kTokenIdentifier) {
|
||||
auto enum_def = enums_.Lookup(attribute_);
|
||||
if (enum_def) {
|
||||
type = enum_def->underlying_type;
|
||||
if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
|
||||
} else {
|
||||
type.base_type = BASE_TYPE_STRUCT;
|
||||
type.struct_def = LookupCreateStruct(attribute_);
|
||||
}
|
||||
ParseTypeIdent(type);
|
||||
} else if (token_ == '[') {
|
||||
Next();
|
||||
Type subtype;
|
||||
@@ -374,7 +378,8 @@ void Parser::ParseField(StructDef &struct_def) {
|
||||
IsScalar(type.base_type) &&
|
||||
!struct_def.fixed &&
|
||||
!type.enum_def->attributes.Lookup("bit_flags") &&
|
||||
!type.enum_def->ReverseLookup(StringToInt(field.value.constant.c_str())))
|
||||
!type.enum_def->ReverseLookup(static_cast<int>(
|
||||
StringToInt(field.value.constant.c_str()))))
|
||||
Error("enum " + type.enum_def->name +
|
||||
" does not have a declaration for this field\'s default of " +
|
||||
field.value.constant);
|
||||
@@ -717,14 +722,18 @@ void Parser::ParseEnum(bool is_union) {
|
||||
enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
|
||||
enum_def.underlying_type.enum_def = &enum_def;
|
||||
} else {
|
||||
// Give specialized error message, since this type spec used to
|
||||
// be optional in the first FlatBuffers release.
|
||||
if (!IsNext(':')) Error("must specify the underlying integer type for this"
|
||||
" enum (e.g. \': short\', which was the default).");
|
||||
// Specify the integer type underlying this enum.
|
||||
ParseType(enum_def.underlying_type);
|
||||
if (!IsInteger(enum_def.underlying_type.base_type))
|
||||
Error("underlying enum type must be integral");
|
||||
if (proto_mode_) {
|
||||
enum_def.underlying_type.base_type = BASE_TYPE_SHORT;
|
||||
} else {
|
||||
// Give specialized error message, since this type spec used to
|
||||
// be optional in the first FlatBuffers release.
|
||||
if (!IsNext(':')) Error("must specify the underlying integer type for this"
|
||||
" enum (e.g. \': short\', which was the default).");
|
||||
// Specify the integer type underlying this enum.
|
||||
ParseType(enum_def.underlying_type);
|
||||
if (!IsInteger(enum_def.underlying_type.base_type))
|
||||
Error("underlying enum type must be integral");
|
||||
}
|
||||
// Make this type refer back to the enum it was derived from.
|
||||
enum_def.underlying_type.enum_def = &enum_def;
|
||||
}
|
||||
@@ -752,7 +761,7 @@ void Parser::ParseEnum(bool is_union) {
|
||||
if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
|
||||
Error("enum values must be specified in ascending order");
|
||||
}
|
||||
} while (IsNext(',') && token_ != '}');
|
||||
} while (IsNext(proto_mode_ ? ';' : ',') && token_ != '}');
|
||||
Expect('}');
|
||||
if (enum_def.attributes.Lookup("bit_flags")) {
|
||||
for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end();
|
||||
@@ -765,22 +774,27 @@ void Parser::ParseEnum(bool is_union) {
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::ParseDecl() {
|
||||
std::vector<std::string> dc = doc_comment_;
|
||||
bool fixed = IsNext(kTokenStruct);
|
||||
if (!fixed) Expect(kTokenTable);
|
||||
StructDef &Parser::StartStruct() {
|
||||
std::string name = attribute_;
|
||||
Expect(kTokenIdentifier);
|
||||
auto &struct_def = *LookupCreateStruct(name);
|
||||
if (!struct_def.predecl) Error("datatype already exists: " + name);
|
||||
struct_def.predecl = false;
|
||||
struct_def.name = name;
|
||||
struct_def.doc_comment = dc;
|
||||
struct_def.fixed = fixed;
|
||||
// Move this struct to the back of the vector just in case it was predeclared,
|
||||
// to preserve declartion order.
|
||||
// to preserve declaration order.
|
||||
remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
|
||||
structs_.vec.back() = &struct_def;
|
||||
return struct_def;
|
||||
}
|
||||
|
||||
void Parser::ParseDecl() {
|
||||
std::vector<std::string> dc = doc_comment_;
|
||||
bool fixed = IsNext(kTokenStruct);
|
||||
if (!fixed) Expect(kTokenTable);
|
||||
auto &struct_def = StartStruct();
|
||||
struct_def.doc_comment = dc;
|
||||
struct_def.fixed = fixed;
|
||||
ParseMetaData(struct_def);
|
||||
struct_def.sortbysize =
|
||||
struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
|
||||
@@ -875,6 +889,119 @@ void Parser::MarkGenerated() {
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::ParseNamespace() {
|
||||
Next();
|
||||
auto ns = new Namespace();
|
||||
namespaces_.push_back(ns);
|
||||
for (;;) {
|
||||
ns->components.push_back(attribute_);
|
||||
Expect(kTokenIdentifier);
|
||||
if (!IsNext('.')) break;
|
||||
}
|
||||
Expect(';');
|
||||
}
|
||||
|
||||
// Best effort parsing of .proto declarations, with the aim to turn them
|
||||
// in the closest corresponding FlatBuffer equivalent.
|
||||
// We parse everything as identifiers instead of keywords, since we don't
|
||||
// want protobuf keywords to become invalid identifiers in FlatBuffers.
|
||||
void Parser::ParseProtoDecl() {
|
||||
if (attribute_ == "package") {
|
||||
// These are identical in syntax to FlatBuffer's namespace decl.
|
||||
ParseNamespace();
|
||||
} else if (attribute_ == "message") {
|
||||
Next();
|
||||
auto &struct_def = StartStruct();
|
||||
Expect('{');
|
||||
while (token_ != '}') {
|
||||
// Parse the qualifier.
|
||||
bool required = false;
|
||||
bool repeated = false;
|
||||
if (attribute_ == "optional") {
|
||||
// This is the default.
|
||||
} else if (attribute_ == "required") {
|
||||
required = true;
|
||||
} else if (attribute_ == "repeated") {
|
||||
repeated = true;
|
||||
} else {
|
||||
Error("expecting optional/required/repeated, got: " + attribute_);
|
||||
}
|
||||
Type type = ParseTypeFromProtoType();
|
||||
// Repeated elements get mapped to a vector.
|
||||
if (repeated) {
|
||||
type.element = type.base_type;
|
||||
type.base_type = BASE_TYPE_VECTOR;
|
||||
}
|
||||
std::string name = attribute_;
|
||||
Expect(kTokenIdentifier);
|
||||
// Parse the field id. Since we're just translating schemas, not
|
||||
// any kind of binary compatibility, we can safely ignore these, and
|
||||
// assign our own.
|
||||
Expect('=');
|
||||
Expect(kTokenIntegerConstant);
|
||||
auto &field = AddField(struct_def, name, type);
|
||||
field.required = required;
|
||||
// See if there's a default specified.
|
||||
if (IsNext('[')) {
|
||||
if (attribute_ != "default") Error("\'default\' expected");
|
||||
Next();
|
||||
Expect('=');
|
||||
field.value.constant = attribute_;
|
||||
Next();
|
||||
Expect(']');
|
||||
}
|
||||
Expect(';');
|
||||
}
|
||||
Next();
|
||||
} else if (attribute_ == "enum") {
|
||||
// These are almost the same, just with different terminator:
|
||||
ParseEnum(false);
|
||||
} else if (attribute_ == "import") {
|
||||
Next();
|
||||
included_files_[attribute_] = true;
|
||||
Expect(kTokenStringConstant);
|
||||
Expect(';');
|
||||
} else if (attribute_ == "option") { // Skip these.
|
||||
Next();
|
||||
Expect(kTokenIdentifier);
|
||||
Expect('=');
|
||||
Next(); // Any single token.
|
||||
Expect(';');
|
||||
} else {
|
||||
Error("don\'t know how to parse .proto declaration starting with " +
|
||||
attribute_);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a protobuf type, and map it to the corresponding FlatBuffer one.
|
||||
Type Parser::ParseTypeFromProtoType() {
|
||||
Expect(kTokenIdentifier);
|
||||
struct type_lookup { const char *proto_type; BaseType fb_type; };
|
||||
static type_lookup lookup[] = {
|
||||
{ "float", BASE_TYPE_FLOAT }, { "double", BASE_TYPE_DOUBLE },
|
||||
{ "int32", BASE_TYPE_INT }, { "int64", BASE_TYPE_LONG },
|
||||
{ "uint32", BASE_TYPE_UINT }, { "uint64", BASE_TYPE_ULONG },
|
||||
{ "sint32", BASE_TYPE_INT }, { "sint64", BASE_TYPE_LONG },
|
||||
{ "fixed32", BASE_TYPE_UINT }, { "fixed64", BASE_TYPE_ULONG },
|
||||
{ "sfixed32", BASE_TYPE_INT }, { "sfixed64", BASE_TYPE_LONG },
|
||||
{ "bool", BASE_TYPE_BOOL },
|
||||
{ "string", BASE_TYPE_STRING },
|
||||
{ "bytes", BASE_TYPE_STRING },
|
||||
{ nullptr, BASE_TYPE_NONE }
|
||||
};
|
||||
Type type;
|
||||
for (auto tl = lookup; tl->proto_type; tl++) {
|
||||
if (attribute_ == tl->proto_type) {
|
||||
type.base_type = tl->fb_type;
|
||||
Next();
|
||||
return type;
|
||||
}
|
||||
}
|
||||
ParseTypeIdent(type);
|
||||
Expect(kTokenIdentifier);
|
||||
return type;
|
||||
}
|
||||
|
||||
bool Parser::Parse(const char *source, const char **include_paths,
|
||||
const char *source_filename) {
|
||||
if (source_filename) included_files_[source_filename] = true;
|
||||
@@ -922,16 +1049,10 @@ bool Parser::Parse(const char *source, const char **include_paths,
|
||||
}
|
||||
// Now parse all other kinds of declarations:
|
||||
while (token_ != kTokenEof) {
|
||||
if (token_ == kTokenNameSpace) {
|
||||
Next();
|
||||
auto ns = new Namespace();
|
||||
namespaces_.push_back(ns);
|
||||
for (;;) {
|
||||
ns->components.push_back(attribute_);
|
||||
Expect(kTokenIdentifier);
|
||||
if (!IsNext('.')) break;
|
||||
}
|
||||
Expect(';');
|
||||
if (proto_mode_) {
|
||||
ParseProtoDecl();
|
||||
} else if (token_ == kTokenNameSpace) {
|
||||
ParseNamespace();
|
||||
} else if (token_ == '{') {
|
||||
if (!root_struct_def) Error("no root type set to parse json with");
|
||||
if (builder_.GetSize()) {
|
||||
|
||||
Reference in New Issue
Block a user