mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-23 03:20:01 +00:00
Schemas now support include files.
Bug: 15521443 Change-Id: I2e1ef97e7225a1a0ecf2ca65e31d49d443003747 Tested: on Linux.
This commit is contained in:
@@ -53,7 +53,8 @@ $(document).ready(function(){initNavTree('md__grammar.html','');});
|
|||||||
<div class="title">Formal Grammar of the schema language </div> </div>
|
<div class="title">Formal Grammar of the schema language </div> </div>
|
||||||
</div><!--header-->
|
</div><!--header-->
|
||||||
<div class="contents">
|
<div class="contents">
|
||||||
<div class="textblock"><p>schema = namespace_decl | type_decl | enum_decl | root_decl | object</p>
|
<div class="textblock"><p>schema = include* ( namespace_decl | type_decl | enum_decl | root_decl | object )*</p>
|
||||||
|
<p>include = <code>include</code> string_constant <code>;</code></p>
|
||||||
<p>namespace_decl = <code>namespace</code> ident ( <code>.</code> ident )* <code>;</code></p>
|
<p>namespace_decl = <code>namespace</code> ident ( <code>.</code> ident )* <code>;</code></p>
|
||||||
<p>type_decl = ( <code>table</code> | <code>struct</code> ) ident metadata <code>{</code> field_decl+ <code>}</code></p>
|
<p>type_decl = ( <code>table</code> | <code>struct</code> ) ident metadata <code>{</code> field_decl+ <code>}</code></p>
|
||||||
<p>enum_decl = ( <code>enum</code> | <code>union</code> ) ident [ <code>:</code> type ] metadata <code>{</code> commasep( enumval_decl ) <code>}</code></p>
|
<p>enum_decl = ( <code>enum</code> | <code>union</code> ) ident [ <code>:</code> type ] metadata <code>{</code> commasep( enumval_decl ) <code>}</code></p>
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ root_type Monster;
|
|||||||
<p>Unions share a lot of properties with enums, but instead of new names for constants, you use names of tables. You can then declare a union field which can hold a reference to any of those types, and additionally a hidden field with the suffix <code>_type</code> is generated that holds the corresponding enum value, allowing you to know which type to cast to at runtime.</p>
|
<p>Unions share a lot of properties with enums, but instead of new names for constants, you use names of tables. You can then declare a union field which can hold a reference to any of those types, and additionally a hidden field with the suffix <code>_type</code> is generated that holds the corresponding enum value, allowing you to know which type to cast to at runtime.</p>
|
||||||
<h3>Namespaces</h3>
|
<h3>Namespaces</h3>
|
||||||
<p>These will generate the corresponding namespace in C++ for all helper code, and packages in Java. You can use <code>.</code> to specify nested namespaces / packages.</p>
|
<p>These will generate the corresponding namespace in C++ for all helper code, and packages in Java. You can use <code>.</code> to specify nested namespaces / packages.</p>
|
||||||
|
<h3>Includes</h3>
|
||||||
|
<p>You can include other schemas files in your current one, e.g.: </p><pre class="fragment">include "mydefinitions.fbs"
|
||||||
|
</pre><p>This makes it easier to refer to types defined elsewhere. <code>include</code> automatically ensures each file is parsed just once, even when referred to more than once.</p>
|
||||||
|
<p>When using the <code>flatc</code> compiler to generate code for schema definitions, only definitions in the current file will be generated, not those from the included files (those you still generate separately).</p>
|
||||||
<h3>Root type</h3>
|
<h3>Root type</h3>
|
||||||
<p>This declares what you consider to be the root table (or struct) of the serialized data. This is particular important for parsing JSON data, which doesn't include object type information.</p>
|
<p>This declares what you consider to be the root table (or struct) of the serialized data. This is particular important for parsing JSON data, which doesn't include object type information.</p>
|
||||||
<h3>File identification and extension</h3>
|
<h3>File identification and extension</h3>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# Formal Grammar of the schema language
|
# Formal Grammar of the schema language
|
||||||
|
|
||||||
schema = namespace\_decl | type\_decl | enum\_decl | root\_decl | object
|
schema = include*
|
||||||
|
( namespace\_decl | type\_decl | enum\_decl | root\_decl | object )*
|
||||||
|
|
||||||
|
include = `include` string\_constant `;`
|
||||||
|
|
||||||
namespace\_decl = `namespace` ident ( `.` ident )* `;`
|
namespace\_decl = `namespace` ident ( `.` ident )* `;`
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,20 @@ These will generate the corresponding namespace in C++ for all helper
|
|||||||
code, and packages in Java. You can use `.` to specify nested namespaces /
|
code, and packages in Java. You can use `.` to specify nested namespaces /
|
||||||
packages.
|
packages.
|
||||||
|
|
||||||
|
### Includes
|
||||||
|
|
||||||
|
You can include other schemas files in your current one, e.g.:
|
||||||
|
|
||||||
|
include "mydefinitions.fbs"
|
||||||
|
|
||||||
|
This makes it easier to refer to types defined elsewhere. `include`
|
||||||
|
automatically ensures each file is parsed just once, even when referred to
|
||||||
|
more than once.
|
||||||
|
|
||||||
|
When using the `flatc` compiler to generate code for schema definitions,
|
||||||
|
only definitions in the current file will be generated, not those from the
|
||||||
|
included files (those you still generate separately).
|
||||||
|
|
||||||
### Root type
|
### Root type
|
||||||
|
|
||||||
This declares what you consider to be the root table (or struct) of the
|
This declares what you consider to be the root table (or struct) of the
|
||||||
|
|||||||
@@ -249,11 +249,16 @@ class Parser {
|
|||||||
|
|
||||||
// Parse the string containing either schema or JSON data, which will
|
// Parse the string containing either schema or JSON data, which will
|
||||||
// populate the SymbolTable's or the FlatBufferBuilder above.
|
// populate the SymbolTable's or the FlatBufferBuilder above.
|
||||||
bool Parse(const char *_source);
|
// filepath indicates the file that _source was loaded from, it is
|
||||||
|
// used to resolve any include statements.
|
||||||
|
bool Parse(const char *_source, const char *filepath);
|
||||||
|
|
||||||
// Set the root type. May override the one set in the schema.
|
// Set the root type. May override the one set in the schema.
|
||||||
bool SetRootType(const char *name);
|
bool SetRootType(const char *name);
|
||||||
|
|
||||||
|
// Mark all definitions as already having code generated.
|
||||||
|
void MarkGenerated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Next();
|
void Next();
|
||||||
bool IsNext(int t);
|
bool IsNext(int t);
|
||||||
@@ -295,6 +300,7 @@ class Parser {
|
|||||||
|
|
||||||
std::vector<std::pair<Value, FieldDef *>> field_stack_;
|
std::vector<std::pair<Value, FieldDef *>> field_stack_;
|
||||||
std::vector<uint8_t> struct_stack_;
|
std::vector<uint8_t> struct_stack_;
|
||||||
|
std::map<std::string, bool> included_files_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility functions for generators:
|
// Utility functions for generators:
|
||||||
|
|||||||
@@ -25,13 +25,6 @@
|
|||||||
|
|
||||||
namespace flatbuffers {
|
namespace flatbuffers {
|
||||||
|
|
||||||
static const char kPosixPathSeparator = '/';
|
|
||||||
#ifdef _WIN32
|
|
||||||
static const char kPathSeparator = '\\';
|
|
||||||
#else
|
|
||||||
static const char kPathSeparator = kPosixPathSeparator;
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
// Convert an integer or floating point value to a string.
|
// Convert an integer or floating point value to a string.
|
||||||
// In contrast to std::stringstream, "char" values are
|
// In contrast to std::stringstream, "char" values are
|
||||||
// converted to a string of digits.
|
// converted to a string of digits.
|
||||||
@@ -107,6 +100,32 @@ inline bool SaveFile(const char *name, const std::string &buf, bool binary) {
|
|||||||
return SaveFile(name, buf.c_str(), buf.size(), binary);
|
return SaveFile(name, buf.c_str(), buf.size(), binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Functionality for minimalistic portable path handling:
|
||||||
|
|
||||||
|
static const char kPosixPathSeparator = '/';
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const char kPathSeparator = '\\';
|
||||||
|
static const char *PathSeparatorSet = "\\:/";
|
||||||
|
#else
|
||||||
|
static const char kPathSeparator = kPosixPathSeparator;
|
||||||
|
static const char *PathSeparatorSet = "/";
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
inline std::string StripExtension(const std::string &filepath) {
|
||||||
|
size_t i = filepath.find_last_of(".");
|
||||||
|
return i != std::string::npos ? filepath.substr(0, i) : filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string StripPath(const std::string &filepath) {
|
||||||
|
size_t i = filepath.find_last_of(PathSeparatorSet);
|
||||||
|
return i != std::string::npos ? filepath.substr(i + 1) : filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string StripFileName(const std::string &filepath) {
|
||||||
|
size_t i = filepath.find_last_of(PathSeparatorSet);
|
||||||
|
return i != std::string::npos ? filepath.substr(0, i + 1) : "";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace flatbuffers
|
} // namespace flatbuffers
|
||||||
|
|
||||||
#endif // FLATBUFFERS_UTIL_H_
|
#endif // FLATBUFFERS_UTIL_H_
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ int main(int /*argc*/, const char * /*argv*/[]) {
|
|||||||
|
|
||||||
// parse schema first, so we can use it to parse the data after
|
// parse schema first, so we can use it to parse the data after
|
||||||
flatbuffers::Parser parser;
|
flatbuffers::Parser parser;
|
||||||
ok = parser.Parse(schemafile.c_str()) &&
|
ok = parser.Parse(schemafile.c_str(), "samples/") &&
|
||||||
parser.Parse(jsonfile.c_str());
|
parser.Parse(jsonfile.c_str(), "samples/");
|
||||||
assert(ok);
|
assert(ok);
|
||||||
|
|
||||||
// here, parser.builder_ contains a binary buffer that is the parsed data.
|
// here, parser.builder_ contains a binary buffer that is the parsed data.
|
||||||
|
|||||||
@@ -100,22 +100,6 @@ static void Error(const char *err, const char *obj, bool usage) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string StripExtension(const std::string &filename) {
|
|
||||||
size_t i = filename.find_last_of(".");
|
|
||||||
return i != std::string::npos ? filename.substr(0, i) : filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string StripPath(const std::string &filename) {
|
|
||||||
size_t i = filename.find_last_of(
|
|
||||||
#ifdef _WIN32
|
|
||||||
"\\:"
|
|
||||||
#else
|
|
||||||
"/"
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
return i != std::string::npos ? filename.substr(i + 1) : filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
program_name = argv[0];
|
program_name = argv[0];
|
||||||
flatbuffers::Parser parser;
|
flatbuffers::Parser parser;
|
||||||
@@ -187,11 +171,12 @@ int main(int argc, const char *argv[]) {
|
|||||||
reinterpret_cast<const uint8_t *>(contents.c_str()),
|
reinterpret_cast<const uint8_t *>(contents.c_str()),
|
||||||
contents.length());
|
contents.length());
|
||||||
} else {
|
} else {
|
||||||
if (!parser.Parse(contents.c_str()))
|
if (!parser.Parse(contents.c_str(), file_it->c_str()))
|
||||||
Error(parser.error_.c_str());
|
Error(parser.error_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string filebase = StripPath(StripExtension(*file_it));
|
std::string filebase = flatbuffers::StripPath(
|
||||||
|
flatbuffers::StripExtension(*file_it));
|
||||||
|
|
||||||
for (size_t i = 0; i < num_generators; ++i) {
|
for (size_t i = 0; i < num_generators; ++i) {
|
||||||
if (generator_enabled[i]) {
|
if (generator_enabled[i]) {
|
||||||
@@ -204,17 +189,9 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the Parser object retains definitions across files, we must
|
// We do not want to generate code for the definitions in this file
|
||||||
// ensure we only output code for these once, in the file they are first
|
// in any files coming up next.
|
||||||
// declared:
|
parser.MarkGenerated();
|
||||||
for (auto it = parser.enums_.vec.begin();
|
|
||||||
it != parser.enums_.vec.end(); ++it) {
|
|
||||||
(*it)->generated = true;
|
|
||||||
}
|
|
||||||
for (auto it = parser.structs_.vec.begin();
|
|
||||||
it != parser.structs_.vec.end(); ++it) {
|
|
||||||
(*it)->generated = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ template<> inline Offset<void> atot<Offset<void>>(const char *s) {
|
|||||||
TD(NameSpace, 265, "namespace") \
|
TD(NameSpace, 265, "namespace") \
|
||||||
TD(RootType, 266, "root_type") \
|
TD(RootType, 266, "root_type") \
|
||||||
TD(FileIdentifier, 267, "file_identifier") \
|
TD(FileIdentifier, 267, "file_identifier") \
|
||||||
TD(FileExtension, 268, "file_extension")
|
TD(FileExtension, 268, "file_extension") \
|
||||||
|
TD(Include, 269, "include")
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
|
__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
|
||||||
#endif
|
#endif
|
||||||
@@ -196,6 +197,7 @@ void Parser::Next() {
|
|||||||
if (attribute_ == "union") { token_ = kTokenUnion; return; }
|
if (attribute_ == "union") { token_ = kTokenUnion; return; }
|
||||||
if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
|
if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
|
||||||
if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
|
if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
|
||||||
|
if (attribute_ == "include") { token_ = kTokenInclude; return; }
|
||||||
if (attribute_ == "file_identifier") {
|
if (attribute_ == "file_identifier") {
|
||||||
token_ = kTokenFileIdentifier;
|
token_ = kTokenFileIdentifier;
|
||||||
return;
|
return;
|
||||||
@@ -781,13 +783,58 @@ bool Parser::SetRootType(const char *name) {
|
|||||||
return root_struct_def != nullptr;
|
return root_struct_def != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::Parse(const char *source) {
|
void Parser::MarkGenerated() {
|
||||||
|
// Since the Parser object retains definitions across files, we must
|
||||||
|
// ensure we only output code for definitions once, in the file they are first
|
||||||
|
// declared. This function marks all existing definitions as having already
|
||||||
|
// been generated.
|
||||||
|
for (auto it = enums_.vec.begin();
|
||||||
|
it != enums_.vec.end(); ++it) {
|
||||||
|
(*it)->generated = true;
|
||||||
|
}
|
||||||
|
for (auto it = structs_.vec.begin();
|
||||||
|
it != structs_.vec.end(); ++it) {
|
||||||
|
(*it)->generated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::Parse(const char *source, const char *filepath) {
|
||||||
|
included_files_[filepath] = true;
|
||||||
|
// This is the starting point to reset to if we interrupted our parsing
|
||||||
|
// to deal with an include:
|
||||||
|
restart_parse_after_include:
|
||||||
source_ = cursor_ = source;
|
source_ = cursor_ = source;
|
||||||
line_ = 1;
|
line_ = 1;
|
||||||
error_.clear();
|
error_.clear();
|
||||||
builder_.Clear();
|
builder_.Clear();
|
||||||
try {
|
try {
|
||||||
Next();
|
Next();
|
||||||
|
// Includes must come first:
|
||||||
|
while (IsNext(kTokenInclude)) {
|
||||||
|
auto name = attribute_;
|
||||||
|
Expect(kTokenStringConstant);
|
||||||
|
name = StripFileName(filepath) + name;
|
||||||
|
if (included_files_.find(name) == included_files_.end()) {
|
||||||
|
// We found an include file that we have not parsed yet.
|
||||||
|
// Load it and parse it.
|
||||||
|
std::string contents;
|
||||||
|
if (!LoadFile(name.c_str(), true, &contents))
|
||||||
|
Error("unable to load include file: " + name);
|
||||||
|
Parse(contents.c_str(), name.c_str());
|
||||||
|
// Any errors, we're done.
|
||||||
|
if (error_.length()) return false;
|
||||||
|
// We do not want to output code for any included files:
|
||||||
|
MarkGenerated();
|
||||||
|
// This is the easiest way to continue this file after an include:
|
||||||
|
// instead of saving and restoring all the state, we simply start the
|
||||||
|
// file anew. This will cause it to encounter the same include statement
|
||||||
|
// again, but this time it will skip it, because it was entered into
|
||||||
|
// included_files_.
|
||||||
|
goto restart_parse_after_include;
|
||||||
|
}
|
||||||
|
Expect(';');
|
||||||
|
}
|
||||||
|
// Now parse all other kinds of declarations:
|
||||||
while (token_ != kTokenEof) {
|
while (token_ != kTokenEof) {
|
||||||
if (token_ == kTokenNameSpace) {
|
if (token_ == kTokenNameSpace) {
|
||||||
Next();
|
Next();
|
||||||
@@ -832,6 +879,8 @@ bool Parser::Parse(const char *source) {
|
|||||||
file_extension_ = attribute_;
|
file_extension_ = attribute_;
|
||||||
Expect(kTokenStringConstant);
|
Expect(kTokenStringConstant);
|
||||||
Expect(';');
|
Expect(';');
|
||||||
|
} else if(token_ == kTokenInclude) {
|
||||||
|
Error("includes must come before declarations");
|
||||||
} else {
|
} else {
|
||||||
ParseDecl();
|
ParseDecl();
|
||||||
}
|
}
|
||||||
|
|||||||
5
tests/include_test1.fbs
Normal file
5
tests/include_test1.fbs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
include "include_test2.fbs";
|
||||||
|
include "include_test2.fbs"; // should be skipped
|
||||||
|
include "include_test1.fbs"; // should be skipped
|
||||||
|
|
||||||
|
|
||||||
4
tests/include_test2.fbs
Normal file
4
tests/include_test2.fbs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
include "include_test2.fbs"; // should be skipped
|
||||||
|
|
||||||
|
enum FromInclude:long { IncludeVal }
|
||||||
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
// example IDL file
|
// example IDL file
|
||||||
|
|
||||||
|
include "include_test1.fbs";
|
||||||
|
|
||||||
namespace MyGame.Example;
|
namespace MyGame.Example;
|
||||||
|
|
||||||
enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3 }
|
enum Color:byte (bit_flags) { Red = 0, Green, Blue = 3 }
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ void ParseAndGenerateTextTest() {
|
|||||||
|
|
||||||
// parse schema first, so we can use it to parse the data after
|
// parse schema first, so we can use it to parse the data after
|
||||||
flatbuffers::Parser parser;
|
flatbuffers::Parser parser;
|
||||||
TEST_EQ(parser.Parse(schemafile.c_str()), true);
|
TEST_EQ(parser.Parse(schemafile.c_str(), "tests/"), true);
|
||||||
TEST_EQ(parser.Parse(jsonfile.c_str()), true);
|
TEST_EQ(parser.Parse(jsonfile.c_str(), "tests/"), true);
|
||||||
|
|
||||||
// here, parser.builder_ contains a binary buffer that is the parsed data.
|
// here, parser.builder_ contains a binary buffer that is the parsed data.
|
||||||
|
|
||||||
@@ -406,12 +406,12 @@ void FuzzTest2() {
|
|||||||
|
|
||||||
// Parse the schema, parse the generated data, then generate text back
|
// Parse the schema, parse the generated data, then generate text back
|
||||||
// from the binary and compare against the original.
|
// from the binary and compare against the original.
|
||||||
TEST_EQ(parser.Parse(schema.c_str()), true);
|
TEST_EQ(parser.Parse(schema.c_str(), ""), true);
|
||||||
|
|
||||||
const std::string &json =
|
const std::string &json =
|
||||||
definitions[num_definitions - 1].instances[0] + "\n";
|
definitions[num_definitions - 1].instances[0] + "\n";
|
||||||
|
|
||||||
TEST_EQ(parser.Parse(json.c_str()), true);
|
TEST_EQ(parser.Parse(json.c_str(), ""), true);
|
||||||
|
|
||||||
std::string jsongen;
|
std::string jsongen;
|
||||||
flatbuffers::GeneratorOptions opts;
|
flatbuffers::GeneratorOptions opts;
|
||||||
@@ -443,7 +443,7 @@ void FuzzTest2() {
|
|||||||
// Test that parser errors are actually generated.
|
// Test that parser errors are actually generated.
|
||||||
void TestError(const char *src, const char *error_substr) {
|
void TestError(const char *src, const char *error_substr) {
|
||||||
flatbuffers::Parser parser;
|
flatbuffers::Parser parser;
|
||||||
TEST_EQ(parser.Parse(src), false); // Must signal error
|
TEST_EQ(parser.Parse(src, ""), false); // Must signal error
|
||||||
// Must be the error we're expecting
|
// Must be the error we're expecting
|
||||||
TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr));
|
TEST_NOTNULL(strstr(parser.error_.c_str(), error_substr));
|
||||||
}
|
}
|
||||||
@@ -495,10 +495,10 @@ void ScientificTest() {
|
|||||||
flatbuffers::Parser parser;
|
flatbuffers::Parser parser;
|
||||||
|
|
||||||
// Simple schema.
|
// Simple schema.
|
||||||
TEST_EQ(parser.Parse("table X { Y:float; } root_type X;"), true);
|
TEST_EQ(parser.Parse("table X { Y:float; } root_type X;", ""), true);
|
||||||
|
|
||||||
// Test scientific notation numbers.
|
// Test scientific notation numbers.
|
||||||
TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }"), true);
|
TEST_EQ(parser.Parse("{ Y:0.0314159e+2 }", ""), true);
|
||||||
auto root = flatbuffers::GetRoot<float>(parser.builder_.GetBufferPointer());
|
auto root = flatbuffers::GetRoot<float>(parser.builder_.GetBufferPointer());
|
||||||
// root will point to the table, which is a 32bit vtable offset followed
|
// root will point to the table, which is a 32bit vtable offset followed
|
||||||
// by a float:
|
// by a float:
|
||||||
@@ -509,11 +509,11 @@ void EnumStringsTest() {
|
|||||||
flatbuffers::Parser parser1;
|
flatbuffers::Parser parser1;
|
||||||
TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
|
TEST_EQ(parser1.Parse("enum E:byte { A, B, C } table T { F:[E]; }"
|
||||||
"root_type T;"
|
"root_type T;"
|
||||||
"{ F:[ A, B, \"C\", \"A B C\" ] }"), true);
|
"{ F:[ A, B, \"C\", \"A B C\" ] }", ""), true);
|
||||||
flatbuffers::Parser parser2;
|
flatbuffers::Parser parser2;
|
||||||
TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
|
TEST_EQ(parser2.Parse("enum E:byte { A, B, C } table T { F:[int]; }"
|
||||||
"root_type T;"
|
"root_type T;"
|
||||||
"{ F:[ \"E.C\", \"E.A E.B E.C\" ] }"), true);
|
"{ F:[ \"E.C\", \"E.A E.B E.C\" ] }", ""), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user