From 39833d7cf051e4a2ecfb342419a86dc02c7e77aa Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Fri, 8 May 2015 15:04:53 -0700 Subject: [PATCH] Added namespaced way to refer to types in schemas. Also made proper namespacing work for enums. You can now say namespace.MyTable as the type when declaring a field that refers to a type in a different namespace. Previously, it would work just referring to MyTable, however with the recent commit fixing namespaced types this now is ambiguous. Change-Id: Ieaa3f4ac1662b8c4dc1f16e1898ea3cdb02e10fd Tested: on Linux. --- include/flatbuffers/idl.h | 2 ++ src/idl_parser.cpp | 42 +++++++++++++++++++++++++++++---------- tests/monster_test.fbs | 2 +- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index e06521d07..46f6540cd 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -329,6 +329,8 @@ class Parser { void Next(); bool IsNext(int t); void Expect(int t); + EnumDef *LookupEnum(const std::string &id); + void ParseNamespacing(std::string *id, std::string *last); void ParseTypeIdent(Type &type); void ParseType(Type &type); FieldDef &AddField(StructDef &struct_def, diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 2340cea2e..9baf0ff0b 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -285,14 +285,33 @@ void Parser::Expect(int t) { Next(); } +void Parser::ParseNamespacing(std::string *id, std::string *last) { + while (IsNext('.')) { + *id += "."; + *id += attribute_; + if (last) *last = attribute_; + Expect(kTokenIdentifier); + } +} + +EnumDef *Parser::LookupEnum(const std::string &id) { + auto ed = enums_.Lookup(GetFullyQualifiedName(id)); + // id may simply not have a namespace at all, so check that too. + if (!ed) ed = enums_.Lookup(id); + return ed; +} + void Parser::ParseTypeIdent(Type &type) { - auto enum_def = enums_.Lookup(attribute_); + std::string id = attribute_; + Expect(kTokenIdentifier); + ParseNamespacing(&id, nullptr); + auto enum_def = LookupEnum(id); 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_); + type.struct_def = LookupCreateStruct(id); } } @@ -300,6 +319,7 @@ void Parser::ParseTypeIdent(Type &type) { void Parser::ParseType(Type &type) { if (token_ >= kTokenBOOL && token_ <= kTokenSTRING) { type.base_type = static_cast(token_ - kTokenNONE); + Next(); } else { if (token_ == kTokenIdentifier) { ParseTypeIdent(type); @@ -320,12 +340,10 @@ void Parser::ParseType(Type &type) { type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def); type.element = subtype.base_type; Expect(']'); - return; } else { Error("illegal type syntax"); } } - Next(); } FieldDef &Parser::AddField(StructDef &struct_def, @@ -709,11 +727,11 @@ int64_t Parser::ParseIntegerFromString(Type &type) { if (!IsInteger(type.base_type)) Error("not a valid value for this field: " + word); // TODO: could check if its a valid number constant here. - const char *dot = strchr(word.c_str(), '.'); + const char *dot = strrchr(word.c_str(), '.'); if (!dot) Error("enum values need to be qualified by an enum type"); std::string enum_def_str(word.c_str(), dot); std::string enum_val_str(dot + 1, word.c_str() + word.length()); - auto enum_def = enums_.Lookup(enum_def_str); + auto enum_def = LookupEnum(enum_def_str); if (!enum_def) Error("unknown enum: " + enum_def_str); auto enum_val = enum_def->vals.Lookup(enum_val_str); if (!enum_val) Error("unknown enum value: " + enum_val_str); @@ -775,6 +793,8 @@ void Parser::ParseSingleValue(Value &e) { StructDef *Parser::LookupCreateStruct(const std::string &name) { std::string qualified_name = GetFullyQualifiedName(name); auto struct_def = structs_.Lookup(qualified_name); + // Unqualified names may simply have no namespace at all, so try that too. + if (!struct_def) struct_def = structs_.Lookup(name); if (!struct_def) { // Rather than failing, we create a "pre declared" StructDef, due to // circular references, and check for errors at the end of parsing. @@ -798,7 +818,8 @@ void Parser::ParseEnum(bool is_union) { enum_def.doc_comment = enum_comment; enum_def.is_union = is_union; enum_def.defined_namespace = namespaces_.back(); - if (enums_.Add(enum_name, &enum_def)) Error("enum already exists: " + enum_name); + if (enums_.Add(GetFullyQualifiedName(enum_name), &enum_def)) + Error("enum already exists: " + enum_name); if (is_union) { enum_def.underlying_type.base_type = BASE_TYPE_UTYPE; enum_def.underlying_type.enum_def = &enum_def; @@ -822,9 +843,11 @@ void Parser::ParseEnum(bool is_union) { Expect('{'); if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0)); do { - std::string value_name = attribute_; + auto value_name = attribute_; + auto full_name = value_name; std::vector value_comment = doc_comment_; Expect(kTokenIdentifier); + if (is_union) ParseNamespacing(&full_name, &value_name); auto prevsize = enum_def.vals.vec.size(); auto value = enum_def.vals.vec.size() ? enum_def.vals.vec.back()->value + 1 @@ -834,7 +857,7 @@ void Parser::ParseEnum(bool is_union) { Error("enum value already exists: " + value_name); ev.doc_comment = value_comment; if (is_union) { - ev.struct_def = LookupCreateStruct(value_name); + ev.struct_def = LookupCreateStruct(full_name); } if (IsNext('=')) { ev.value = atoi(attribute_.c_str()); @@ -1104,7 +1127,6 @@ Type Parser::ParseTypeFromProtoType() { } } ParseTypeIdent(type); - Expect(kTokenIdentifier); return type; } diff --git a/tests/monster_test.fbs b/tests/monster_test.fbs index 9bd713324..53fa87b81 100755 --- a/tests/monster_test.fbs +++ b/tests/monster_test.fbs @@ -39,7 +39,7 @@ table Monster { /// multiline too testarrayoftables:[Monster] (id: 11); testarrayofstring:[string] (id: 10); - enemy:Monster (id:12); + enemy:MyGame.Example.Monster (id:12); // Test referring by full namespace. test:Any (id: 8); test4:[Test] (id: 9); testnestedflatbuffer:[ubyte] (id:13, nested_flatbuffer: "Monster");