mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-07 13:53:38 +00:00
Initial commit of the FlatBuffers code.
Change-Id: I4c9f0f722490b374257adb3fec63e44ae93da920 Tested: using VS2010 / Xcode / gcc on Linux.
This commit is contained in:
180
src/flatc.cpp
Executable file
180
src/flatc.cpp
Executable file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
void Error(const char *err, const char *obj = nullptr, bool usage = false);
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
bool GenerateBinary(const Parser &parser,
|
||||
const std::string &path,
|
||||
const std::string &file_name) {
|
||||
return !parser.builder_.GetSize() ||
|
||||
flatbuffers::SaveFile(
|
||||
(path + file_name + "_wire.bin").c_str(),
|
||||
reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
|
||||
parser.builder_.GetSize(),
|
||||
true);
|
||||
}
|
||||
|
||||
bool GenerateTextFile(const Parser &parser,
|
||||
const std::string &path,
|
||||
const std::string &file_name) {
|
||||
if (!parser.builder_.GetSize()) return true;
|
||||
if (!parser.root_struct_def) Error("root_type not set");
|
||||
std::string text;
|
||||
GenerateText(parser, parser.builder_.GetBufferPointer(), 2, &text);
|
||||
return flatbuffers::SaveFile((path + file_name + "_wire.txt").c_str(),
|
||||
text,
|
||||
false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This struct allows us to create a table of all possible output generators
|
||||
// for the various programming languages and formats we support.
|
||||
struct Generator {
|
||||
bool (*generate)(const flatbuffers::Parser &parser,
|
||||
const std::string &path,
|
||||
const std::string &file_name);
|
||||
const char *extension;
|
||||
const char *name;
|
||||
const char *help;
|
||||
};
|
||||
|
||||
const Generator generators[] = {
|
||||
{ flatbuffers::GenerateBinary, "b", "binary",
|
||||
"Generate wire format binaries for any data definitions" },
|
||||
{ flatbuffers::GenerateTextFile, "t", "text",
|
||||
"Generate text output for any data definitions" },
|
||||
{ flatbuffers::GenerateCPP, "c", "C++",
|
||||
"Generate C++ headers for tables/structs" },
|
||||
{ flatbuffers::GenerateJava, "j", "Java",
|
||||
"Generate Java classes for tables/structs" },
|
||||
};
|
||||
|
||||
const char *program_name = NULL;
|
||||
|
||||
void Error(const char *err, const char *obj, bool usage) {
|
||||
printf("%s: %s\n", program_name, err);
|
||||
if (obj) printf(": %s", obj);
|
||||
printf("\n");
|
||||
if (usage) {
|
||||
printf("usage: %s [OPTION]... FILE...\n", program_name);
|
||||
for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i)
|
||||
printf(" -%s %s.\n", generators[i].extension, generators[i].help);
|
||||
printf(" -o PATH Prefix PATH to all generated files.\n"
|
||||
"FILEs may depend on declarations in earlier files.\n"
|
||||
"Output files are named using the base file name of the input,"
|
||||
"and written to the current directory or the path given by -o.\n"
|
||||
"example: %s -c -b schema1.fbs schema2.fbs data.json\n",
|
||||
program_name);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
program_name = argv[0];
|
||||
flatbuffers::Parser parser;
|
||||
std::string output_path;
|
||||
const size_t num_generators = sizeof(generators) / sizeof(generators[0]);
|
||||
bool generator_enabled[num_generators] = { false };
|
||||
bool any_generator = false;
|
||||
std::vector<std::string> filenames;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (arg[0] == '-') {
|
||||
if (filenames.size())
|
||||
Error("invalid option location", arg, true);
|
||||
if (strlen(arg) != 2)
|
||||
Error("invalid commandline argument", arg, true);
|
||||
switch (arg[1]) {
|
||||
case 'o':
|
||||
if (++i >= argc) Error("missing path following", arg, true);
|
||||
output_path = argv[i];
|
||||
break;
|
||||
default:
|
||||
for (size_t i = 0; i < num_generators; ++i) {
|
||||
if(!strcmp(arg+1, generators[i].extension)) {
|
||||
generator_enabled[i] = true;
|
||||
any_generator = true;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
Error("unknown commandline argument", arg, true);
|
||||
found:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
filenames.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!filenames.size()) Error("missing input files", nullptr, true);
|
||||
|
||||
if (!any_generator)
|
||||
Error("no options: no output files generated.",
|
||||
"specify one of -c -j -t -b etc.", true);
|
||||
|
||||
// Now process the files:
|
||||
for (auto file_it = filenames.begin();
|
||||
file_it != filenames.end();
|
||||
++file_it) {
|
||||
std::string contents;
|
||||
if (!flatbuffers::LoadFile(file_it->c_str(), true, &contents))
|
||||
Error("unable to load file", file_it->c_str());
|
||||
|
||||
if (!parser.Parse(contents.c_str()))
|
||||
Error(parser.error_.c_str());
|
||||
|
||||
std::string filebase = StripExtension(*file_it);
|
||||
|
||||
for (size_t i = 0; i < num_generators; ++i) {
|
||||
if (generator_enabled[i]) {
|
||||
if (!generators[i].generate(parser, output_path, filebase)) {
|
||||
Error((std::string("Unable to generate ") +
|
||||
generators[i].name +
|
||||
" for " +
|
||||
filebase).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the Parser object retains definitions across files, we must
|
||||
// ensure we only output code for these once, in the file they are first
|
||||
// declared:
|
||||
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;
|
||||
}
|
||||
|
||||
371
src/idl_gen_cpp.cpp
Normal file
371
src/idl_gen_cpp.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// independent from idl_parser, since this code is not needed for most clients
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
namespace cpp {
|
||||
|
||||
// Return a C++ type from the table in idl.h
|
||||
static std::string GenTypeBasic(const Type &type) {
|
||||
static const char *ctypename[] = {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) #CTYPE,
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
};
|
||||
return ctypename[type.base_type];
|
||||
}
|
||||
|
||||
static std::string GenTypeWire(const Type &type, const char *postfix);
|
||||
|
||||
// Return a C++ pointer type, specialized to the actual struct/table types,
|
||||
// and vector element types.
|
||||
static std::string GenTypePointer(const Type &type) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRING:
|
||||
return "flatbuffers::String";
|
||||
case BASE_TYPE_VECTOR:
|
||||
return "flatbuffers::Vector<" + GenTypeWire(type.VectorType(), "") + ">";
|
||||
case BASE_TYPE_STRUCT:
|
||||
return type.struct_def->name;
|
||||
case BASE_TYPE_UNION:
|
||||
// fall through
|
||||
default:
|
||||
return "void";
|
||||
}
|
||||
}
|
||||
|
||||
// Return a C++ type for any type (scalar/pointer) specifically for
|
||||
// building a flatbuffer.
|
||||
static std::string GenTypeWire(const Type &type, const char *postfix) {
|
||||
return IsScalar(type.base_type)
|
||||
? GenTypeBasic(type) + postfix
|
||||
: IsStruct(type)
|
||||
? "const " + GenTypePointer(type) + " *"
|
||||
: "flatbuffers::Offset<" + GenTypePointer(type) + ">" + postfix;
|
||||
}
|
||||
|
||||
// Return a C++ type for any type (scalar/pointer) specifically for
|
||||
// using a flatbuffer.
|
||||
static std::string GenTypeGet(const Type &type, const char *afterbasic,
|
||||
const char *beforeptr, const char *afterptr) {
|
||||
return IsScalar(type.base_type)
|
||||
? GenTypeBasic(type) + afterbasic
|
||||
: beforeptr + GenTypePointer(type) + afterptr;
|
||||
}
|
||||
|
||||
// Generate a documentation comment, if available.
|
||||
static void GenComment(const std::string &dc,
|
||||
std::string *code_ptr,
|
||||
const char *prefix = "") {
|
||||
std::string &code = *code_ptr;
|
||||
if (dc.length()) {
|
||||
code += std::string(prefix) + "///" + dc + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Generate an enum declaration and an enum string lookup table.
|
||||
static void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
|
||||
if (enum_def.generated) return;
|
||||
std::string &code = *code_ptr;
|
||||
GenComment(enum_def.doc_comment, code_ptr);
|
||||
code += "enum {\n";
|
||||
for (auto it = enum_def.vals.vec.begin();
|
||||
it != enum_def.vals.vec.end();
|
||||
++it) {
|
||||
auto &ev = **it;
|
||||
GenComment(ev.doc_comment, code_ptr, " ");
|
||||
code += " " + enum_def.name + "_" + ev.name + " = ";
|
||||
code += NumToString(ev.value) + ",\n";
|
||||
}
|
||||
code += "};\n\n";
|
||||
|
||||
// Generate a generate string table for enum values.
|
||||
// Problem is, if values are very sparse that could generate really big
|
||||
// tables. Ideally in that case we generate a map lookup instead, but for
|
||||
// the moment we simply don't output a table at all.
|
||||
int range = enum_def.vals.vec.back()->value -
|
||||
enum_def.vals.vec.front()->value + 1;
|
||||
// Average distance between values above which we consider a table
|
||||
// "too sparse". Change at will.
|
||||
static const int kMaxSparseness = 5;
|
||||
if (range / static_cast<int>(enum_def.vals.vec.size()) < kMaxSparseness) {
|
||||
code += "inline const char **EnumNames" + enum_def.name + "() {\n";
|
||||
code += " static const char *names[] = { ";
|
||||
int val = enum_def.vals.vec.front()->value;
|
||||
for (auto it = enum_def.vals.vec.begin();
|
||||
it != enum_def.vals.vec.end();
|
||||
++it) {
|
||||
while (val++ != (*it)->value) code += "\"\", ";
|
||||
code += "\"" + (*it)->name + "\", ";
|
||||
}
|
||||
code += "nullptr };\n return names;\n}\n\n";
|
||||
code += "inline const char *EnumName" + enum_def.name;
|
||||
code += "(int e) { return EnumNames" + enum_def.name + "()[e";
|
||||
if (enum_def.vals.vec.front()->value)
|
||||
code += " - " + enum_def.name + "_" + enum_def.vals.vec.front()->name;
|
||||
code += "]; }\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Generate an accessor struct, builder structs & function for a table.
|
||||
static void GenTable(StructDef &struct_def, std::string *code_ptr) {
|
||||
if (struct_def.generated) return;
|
||||
std::string &code = *code_ptr;
|
||||
|
||||
// Generate an accessor struct, with methods of the form:
|
||||
// type name() const { return GetField<type>(offset, defaultval); }
|
||||
GenComment(struct_def.doc_comment, code_ptr);
|
||||
code += "struct " + struct_def.name + " : private flatbuffers::Table";
|
||||
code += " {\n";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (!field.deprecated) { // Deprecated fields won't be accessible.
|
||||
GenComment(field.doc_comment, code_ptr, " ");
|
||||
code += " " + GenTypeGet(field.value.type, " ", "const ", " *");
|
||||
code += field.name + "() const { return ";
|
||||
// Call a different accessor for pointers, that indirects.
|
||||
code += IsScalar(field.value.type.base_type)
|
||||
? "GetField<"
|
||||
: (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<");
|
||||
code += GenTypeGet(field.value.type, "", "const ", " *") + ">(";
|
||||
code += NumToString(field.value.offset);
|
||||
// Default value as second arg for non-pointer types.
|
||||
if (IsScalar(field.value.type.base_type))
|
||||
code += ", " + field.value.constant;
|
||||
code += "); }\n";
|
||||
}
|
||||
}
|
||||
code += "};\n\n";
|
||||
|
||||
// Generate a builder struct, with methods of the form:
|
||||
// void add_name(type name) { fbb_.AddElement<type>(offset, name, default); }
|
||||
code += "struct " + struct_def.name;
|
||||
code += "Builder {\n flatbuffers::FlatBufferBuilder &fbb_;\n";
|
||||
code += " flatbuffers::uoffset_t start_;\n";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (!field.deprecated) {
|
||||
code += " void add_" + field.name + "(";
|
||||
code += GenTypeWire(field.value.type, " ") + field.name + ") { fbb_.Add";
|
||||
if (IsScalar(field.value.type.base_type))
|
||||
code += "Element<" + GenTypeWire(field.value.type, "") + ">";
|
||||
else if (IsStruct(field.value.type))
|
||||
code += "Struct";
|
||||
else
|
||||
code += "Offset";
|
||||
code += "(" + NumToString(field.value.offset) + ", " + field.name;
|
||||
if (IsScalar(field.value.type.base_type))
|
||||
code += ", " + field.value.constant;
|
||||
code += "); }\n";
|
||||
}
|
||||
}
|
||||
code += " " + struct_def.name;
|
||||
code += "Builder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) ";
|
||||
code += "{ start_ = fbb_.StartTable(); }\n";
|
||||
code += " flatbuffers::Offset<" + struct_def.name;
|
||||
code += "> Finish() { return flatbuffers::Offset<" + struct_def.name;
|
||||
code += ">(fbb_.EndTable(start_, ";
|
||||
code += NumToString(struct_def.fields.vec.size()) + ")); }\n};\n\n";
|
||||
|
||||
// Generate a convenient CreateX function that uses the above builder
|
||||
// to create a table in one go.
|
||||
code += "inline flatbuffers::Offset<" + struct_def.name + "> Create";
|
||||
code += struct_def.name;
|
||||
code += "(flatbuffers::FlatBufferBuilder &_fbb";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (!field.deprecated) {
|
||||
code += ", " + GenTypeWire(field.value.type, " ") + field.name;
|
||||
}
|
||||
}
|
||||
code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n";
|
||||
for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
|
||||
size;
|
||||
size /= 2) {
|
||||
for (auto it = struct_def.fields.vec.rbegin();
|
||||
it != struct_def.fields.vec.rend();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (!field.deprecated &&
|
||||
(!struct_def.sortbysize ||
|
||||
size == SizeOf(field.value.type.base_type))) {
|
||||
code += " builder_.add_" + field.name + "(" + field.name + ");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
code += " return builder_.Finish();\n}\n\n";
|
||||
}
|
||||
|
||||
// Generate an accessor struct with constructor for a flatbuffers struct.
|
||||
static void GenStruct(StructDef &struct_def, std::string *code_ptr) {
|
||||
if (struct_def.generated) return;
|
||||
std::string &code = *code_ptr;
|
||||
|
||||
// Generate an accessor struct, with private variables of the form:
|
||||
// type name_;
|
||||
// Generates manual padding and alignment.
|
||||
// Variables are private because they contain little endian data on all
|
||||
// platforms.
|
||||
GenComment(struct_def.doc_comment, code_ptr);
|
||||
code += "MANUALLY_ALIGNED_STRUCT(" + NumToString(struct_def.minalign) + ") ";
|
||||
code += struct_def.name + " {\n private:\n";
|
||||
int padding_id = 0;
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
code += " " + GenTypeGet(field.value.type, " ", "", " ");
|
||||
code += field.name + "_;\n";
|
||||
if (field.padding) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (static_cast<int>(field.padding) & (1 << i))
|
||||
code += " int" + NumToString((1 << i) * 8) +
|
||||
"_t __padding" + NumToString(padding_id++) + ";\n";
|
||||
assert(!(field.padding & ~0xF));
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a constructor that takes all fields as arguments.
|
||||
code += "\n public:\n " + struct_def.name + "(";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (it != struct_def.fields.vec.begin()) code += ", ";
|
||||
code += GenTypeGet(field.value.type, " ", "const ", " &") + field.name;
|
||||
}
|
||||
code += ")\n : ";
|
||||
padding_id = 0;
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (it != struct_def.fields.vec.begin()) code += ", ";
|
||||
code += field.name + "_(";
|
||||
if (IsScalar(field.value.type.base_type))
|
||||
code += "flatbuffers::EndianScalar(" + field.name + "))";
|
||||
else
|
||||
code += field.name + ")";
|
||||
if (field.padding)
|
||||
code += ", __padding" + NumToString(padding_id++) + "(0)";
|
||||
}
|
||||
code += " {}\n\n";
|
||||
|
||||
// Generate accessor methods of the form:
|
||||
// type name() const { return flatbuffers::EndianScalar(name_); }
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
GenComment(field.doc_comment, code_ptr, " ");
|
||||
code += " " + GenTypeGet(field.value.type, " ", "const ", " &");
|
||||
code += field.name + "() const { return ";
|
||||
if (IsScalar(field.value.type.base_type))
|
||||
code += "flatbuffers::EndianScalar(" + field.name + "_)";
|
||||
else
|
||||
code += field.name + "_";
|
||||
code += "; }\n";
|
||||
}
|
||||
code += "};\nSTRUCT_END(" + struct_def.name + ", ";
|
||||
code += NumToString(struct_def.bytesize) + ");\n\n";
|
||||
}
|
||||
|
||||
} // namespace cpp
|
||||
|
||||
// Iterate through all definitions we haven't generate code for (enums, structs,
|
||||
// and tables) and output them to a single file.
|
||||
static std::string GenerateCPP(const Parser &parser) {
|
||||
using namespace cpp;
|
||||
|
||||
// Generate code for all the enum declarations.
|
||||
std::string enum_code;
|
||||
for (auto it = parser.enums_.vec.begin();
|
||||
it != parser.enums_.vec.end(); ++it) {
|
||||
GenEnum(**it, &enum_code);
|
||||
}
|
||||
|
||||
// Generate forward declarations for all structs/tables, since they may
|
||||
// have circular references.
|
||||
std::string forward_decl_code;
|
||||
for (auto it = parser.structs_.vec.begin();
|
||||
it != parser.structs_.vec.end(); ++it) {
|
||||
if (!(*it)->generated)
|
||||
forward_decl_code += "struct " + (*it)->name + ";\n";
|
||||
}
|
||||
|
||||
// Generate code for all structs, then all tables.
|
||||
std::string decl_code;
|
||||
for (auto it = parser.structs_.vec.begin();
|
||||
it != parser.structs_.vec.end(); ++it) {
|
||||
if ((**it).fixed) GenStruct(**it, &decl_code);
|
||||
}
|
||||
for (auto it = parser.structs_.vec.begin();
|
||||
it != parser.structs_.vec.end(); ++it) {
|
||||
if (!(**it).fixed) GenTable(**it, &decl_code);
|
||||
}
|
||||
|
||||
// Only output file-level code if there were any declarations.
|
||||
if (enum_code.length() || forward_decl_code.length() || decl_code.length()) {
|
||||
std::string code;
|
||||
code = "// automatically generated, do not modify\n\n";
|
||||
code += "#include \"flatbuffers/flatbuffers.h\"\n\n";
|
||||
for (auto it = parser.name_space_.begin();
|
||||
it != parser.name_space_.end(); ++it) {
|
||||
code += "namespace " + *it + " {\n";
|
||||
}
|
||||
code += "\n";
|
||||
code += enum_code;
|
||||
code += forward_decl_code;
|
||||
code += "\n";
|
||||
code += decl_code;
|
||||
if (parser.root_struct_def) {
|
||||
code += "inline const " + parser.root_struct_def->name + " *Get";
|
||||
code += parser.root_struct_def->name;
|
||||
code += "(const void *buf) { return flatbuffers::GetRoot<";
|
||||
code += parser.root_struct_def->name + ">(buf); }\n\n";
|
||||
}
|
||||
for (auto it = parser.name_space_.begin();
|
||||
it != parser.name_space_.end(); ++it) {
|
||||
code += "}; // namespace " + *it + "\n";
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool GenerateCPP(const Parser &parser,
|
||||
const std::string &path,
|
||||
const std::string &file_name) {
|
||||
auto code = GenerateCPP(parser);
|
||||
return !code.length() ||
|
||||
SaveFile((path + file_name + "_generated.h").c_str(), code, false);
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
399
src/idl_gen_java.cpp
Executable file
399
src/idl_gen_java.cpp
Executable file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// independent from idl_parser, since this code is not needed for most clients
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define PATH_SEPARATOR "\\"
|
||||
#define mkdir(n, m) _mkdir(n)
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#define PATH_SEPARATOR "/"
|
||||
#endif
|
||||
|
||||
namespace flatbuffers {
|
||||
namespace java {
|
||||
|
||||
static std::string GenTypeBasic(const Type &type) {
|
||||
static const char *ctypename[] = {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) #JTYPE,
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
};
|
||||
return ctypename[type.base_type];
|
||||
}
|
||||
|
||||
static std::string GenTypeGet(const Type &type);
|
||||
|
||||
static std::string GenTypePointer(const Type &type) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRING:
|
||||
return "String";
|
||||
case BASE_TYPE_VECTOR:
|
||||
return GenTypeGet(type.VectorType());
|
||||
case BASE_TYPE_STRUCT:
|
||||
return type.struct_def->name;
|
||||
case BASE_TYPE_UNION:
|
||||
// fall through
|
||||
default:
|
||||
return "Table";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string GenTypeGet(const Type &type) {
|
||||
return IsScalar(type.base_type)
|
||||
? GenTypeBasic(type)
|
||||
: GenTypePointer(type);
|
||||
}
|
||||
|
||||
static void GenComment(const std::string &dc,
|
||||
std::string *code_ptr,
|
||||
const char *prefix = "") {
|
||||
std::string &code = *code_ptr;
|
||||
if (dc.length()) {
|
||||
code += std::string(prefix) + "///" + dc + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an underscore_based_indentifier in to camelCase.
|
||||
// Also uppercases the first character if first is true.
|
||||
static std::string MakeCamel(const std::string &in, bool first = true) {
|
||||
std::string s;
|
||||
for (size_t i = 0; i < in.length(); i++) {
|
||||
if (!i && first) s += toupper(in[0]);
|
||||
else if (in[i] == '_' && i + 1 < in.length()) s += toupper(in[++i]);
|
||||
else s += in[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static void GenEnum(EnumDef &enum_def, std::string *code_ptr) {
|
||||
std::string &code = *code_ptr;
|
||||
if (enum_def.generated) return;
|
||||
|
||||
// Generate enum definitions of the form:
|
||||
// public static final int name = value;
|
||||
// We use ints rather than the Java Enum feature, because we want them
|
||||
// to map directly to how they're used in C/C++ and file formats.
|
||||
// That, and Java Enums are expensive, and not universally liked.
|
||||
GenComment(enum_def.doc_comment, code_ptr);
|
||||
code += "public class " + enum_def.name + " {\n";
|
||||
for (auto it = enum_def.vals.vec.begin();
|
||||
it != enum_def.vals.vec.end();
|
||||
++it) {
|
||||
auto &ev = **it;
|
||||
GenComment(ev.doc_comment, code_ptr, " ");
|
||||
code += " public static final " + GenTypeBasic(enum_def.underlying_type);
|
||||
code += " " + ev.name + " = ";
|
||||
code += NumToString(ev.value) + ";\n";
|
||||
}
|
||||
code += "};\n\n";
|
||||
}
|
||||
|
||||
// Returns the function name that is able to read a value of the given type.
|
||||
static std::string GenGetter(const Type &type) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_STRING: return "__string";
|
||||
case BASE_TYPE_STRUCT: return "__struct";
|
||||
case BASE_TYPE_UNION: return "__union";
|
||||
case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
|
||||
default:
|
||||
return "bb.get" + (SizeOf(type.base_type) > 1
|
||||
? MakeCamel(GenTypeGet(type))
|
||||
: "");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the method name for use with add/put calls.
|
||||
static std::string GenMethod(const FieldDef &field) {
|
||||
return IsScalar(field.value.type.base_type)
|
||||
? MakeCamel(GenTypeBasic(field.value.type))
|
||||
: (IsStruct(field.value.type) ? "Struct" : "Offset");
|
||||
}
|
||||
|
||||
// Recursively generate arguments for a constructor, to deal with nested
|
||||
// structs.
|
||||
static void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
|
||||
const char *nameprefix) {
|
||||
std::string &code = *code_ptr;
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (IsStruct(field.value.type)) {
|
||||
// Generate arguments for a struct inside a struct. To ensure names
|
||||
// don't clash, and to make it obvious these arguments are constructing
|
||||
// a nested struct, prefix the name with the struct name.
|
||||
GenStructArgs(*field.value.type.struct_def, code_ptr,
|
||||
(field.value.type.struct_def->name + "_").c_str());
|
||||
} else {
|
||||
code += ", " + GenTypeBasic(field.value.type) + " " + nameprefix;
|
||||
code += MakeCamel(field.name, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recusively generate struct construction statements of the form:
|
||||
// builder.putType(name);
|
||||
// and insert manual padding.
|
||||
static void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
|
||||
const char *nameprefix) {
|
||||
std::string &code = *code_ptr;
|
||||
code += " builder.prep(" + NumToString(struct_def.minalign) + ", 0);\n";
|
||||
for (auto it = struct_def.fields.vec.rbegin();
|
||||
it != struct_def.fields.vec.rend();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (field.padding)
|
||||
code += " builder.pad(" + NumToString(field.padding) + ");\n";
|
||||
if (IsStruct(field.value.type)) {
|
||||
GenStructBody(*field.value.type.struct_def, code_ptr,
|
||||
(field.value.type.struct_def->name + "_").c_str());
|
||||
} else {
|
||||
code += " builder.put" + GenMethod(field) + "(";
|
||||
code += nameprefix + MakeCamel(field.name, false) + ");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GenStruct(StructDef &struct_def,
|
||||
std::string *code_ptr,
|
||||
StructDef *root_struct_def) {
|
||||
if (struct_def.generated) return;
|
||||
std::string &code = *code_ptr;
|
||||
|
||||
// Generate a struct accessor class, with methods of the form:
|
||||
// public type name() { return bb.getType(i + offset); }
|
||||
// or for tables of the form:
|
||||
// public type name() {
|
||||
// int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
|
||||
// }
|
||||
GenComment(struct_def.doc_comment, code_ptr);
|
||||
code += "public class " + struct_def.name + " extends ";
|
||||
code += struct_def.fixed ? "Struct" : "Table";
|
||||
code += " {\n";
|
||||
if (&struct_def == root_struct_def) {
|
||||
// Generate a special accessor for the table that has been declared as
|
||||
// the root type.
|
||||
code += " public static " + struct_def.name + " getRootAs";
|
||||
code += struct_def.name;
|
||||
code += "(ByteBuffer _bb, int offset) { ";
|
||||
code += "_bb.order(ByteOrder.LITTLE_ENDIAN); ";
|
||||
code += "return (new " + struct_def.name;
|
||||
code += "()).__init(_bb.getInt(offset) + offset, _bb); }\n";
|
||||
}
|
||||
// Generate the __init method that sets the field in a pre-existing
|
||||
// accessor object. This is to allow object reuse.
|
||||
code += " public " + struct_def.name;
|
||||
code += " __init(int _i, ByteBuffer _bb) ";
|
||||
code += "{ bb_pos = _i; bb = _bb; return this; }\n";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (field.deprecated) continue;
|
||||
GenComment(field.doc_comment, code_ptr, " ");
|
||||
std::string type_name = GenTypeGet(field.value.type);
|
||||
std::string method_start = " public " + type_name + " " +
|
||||
MakeCamel(field.name, false);
|
||||
// Generate the accessors that don't do object reuse.
|
||||
if (field.value.type.base_type == BASE_TYPE_STRUCT) {
|
||||
// Calls the accessor that takes an accessor object with a new object.
|
||||
code += method_start + "() { return " + MakeCamel(field.name, false);
|
||||
code += "(new ";
|
||||
code += type_name + "()); }\n";
|
||||
} else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
|
||||
field.value.type.element == BASE_TYPE_STRUCT) {
|
||||
// Accessors for vectors of structs also take accessor objects, this
|
||||
// generates a variant without that argument.
|
||||
code += method_start + "(int j) { return " + MakeCamel(field.name, false);
|
||||
code += "(new ";
|
||||
code += type_name + "(), j); }\n";
|
||||
}
|
||||
std::string getter = GenGetter(field.value.type);
|
||||
code += method_start + "(";
|
||||
// Most field accessors need to retrieve and test the field offset first,
|
||||
// this is the prefix code for that:
|
||||
auto offset_prefix = ") { int o = __offset(" +
|
||||
NumToString(field.value.offset) +
|
||||
"); return o != 0 ? ";
|
||||
if (IsScalar(field.value.type.base_type)) {
|
||||
if (struct_def.fixed) {
|
||||
code += ") { return " + getter;
|
||||
code += "(bb_pos + " + NumToString(field.value.offset) + ")";
|
||||
} else {
|
||||
code += offset_prefix + getter;
|
||||
code += "(o + bb_pos) : " + field.value.constant;
|
||||
}
|
||||
} else {
|
||||
switch (field.value.type.base_type) {
|
||||
case BASE_TYPE_STRUCT:
|
||||
code += type_name + " obj";
|
||||
if (struct_def.fixed) {
|
||||
code += ") { return obj.__init(bb_pos + ";
|
||||
code += NumToString(field.value.offset) + ", bb)";
|
||||
} else {
|
||||
code += offset_prefix;
|
||||
code += "obj.__init(";
|
||||
code += field.value.type.struct_def->fixed
|
||||
? "o + bb_pos"
|
||||
: "__indirect(o + i)";
|
||||
code += ", bb) : null";
|
||||
}
|
||||
break;
|
||||
case BASE_TYPE_STRING:
|
||||
code += offset_prefix + getter +"(o) : null";
|
||||
break;
|
||||
case BASE_TYPE_VECTOR: {
|
||||
auto vectortype = field.value.type.VectorType();
|
||||
if (vectortype.base_type == BASE_TYPE_STRUCT) {
|
||||
code += type_name + " obj, ";
|
||||
getter = "obj.__init";
|
||||
}
|
||||
code += "int j" + offset_prefix + getter +"(";
|
||||
auto index = "__vector(o) + j * " +
|
||||
NumToString(InlineSize(vectortype));
|
||||
if (vectortype.base_type == BASE_TYPE_STRUCT) {
|
||||
code += vectortype.struct_def->fixed
|
||||
? index
|
||||
: "__indirect(" + index + ")";
|
||||
code += ", bb";
|
||||
} else {
|
||||
code += index;
|
||||
}
|
||||
code += ") : ";
|
||||
code += IsScalar(field.value.type.element) ? "0" : "null";
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_UNION:
|
||||
code += type_name + " obj" + offset_prefix + getter;
|
||||
code += "(obj, o) : null";
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
code += "; }\n";
|
||||
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
|
||||
code += " public int " + MakeCamel(field.name, false) + "Length(";
|
||||
code += offset_prefix;
|
||||
code += "__vector_len(o) : 0; }\n";
|
||||
}
|
||||
}
|
||||
code += "\n";
|
||||
if (struct_def.fixed) {
|
||||
// create a struct constructor function
|
||||
code += " public static int create" + struct_def.name;
|
||||
code += "(FlatBufferBuilder builder";
|
||||
GenStructArgs(struct_def, code_ptr, "");
|
||||
code += ") {\n";
|
||||
GenStructBody(struct_def, code_ptr, "");
|
||||
code += " return builder.offset();\n }\n";
|
||||
} else {
|
||||
// Create a set of static methods that allow table construction,
|
||||
// of the form:
|
||||
// public static void addName(FlatBufferBuilder builder, short name)
|
||||
// { builder.addShort(id, name, default); }
|
||||
code += " public static void start" + struct_def.name;
|
||||
code += "(FlatBufferBuilder builder) { builder.startObject(";
|
||||
code += NumToString(struct_def.fields.vec.size()) + "); }\n";
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
auto &field = **it;
|
||||
if (field.deprecated) continue;
|
||||
code += " public static void add" + MakeCamel(field.name);
|
||||
code += "(FlatBufferBuilder builder, " + GenTypeBasic(field.value.type);
|
||||
code += " " + MakeCamel(field.name, false) + ") { builder.add";
|
||||
code += GenMethod(field) + "(";
|
||||
code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
|
||||
code += MakeCamel(field.name, false) + ", " + field.value.constant;
|
||||
code += "); }\n";
|
||||
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
|
||||
code += " public static void start" + MakeCamel(field.name);
|
||||
code += "Vector(FlatBufferBuilder builder, int numElems) ";
|
||||
code += "{ builder.startVector(";
|
||||
code += NumToString(InlineSize(field.value.type));
|
||||
code += ", numElems); }\n";
|
||||
}
|
||||
}
|
||||
code += " public static int end" + struct_def.name;
|
||||
code += "(FlatBufferBuilder builder) { return builder.endObject(); }\n";
|
||||
}
|
||||
code += "};\n\n";
|
||||
}
|
||||
|
||||
// Save out the generated code for a single Java class while adding
|
||||
// declaration boilerplate.
|
||||
static bool SaveClass(const Parser &parser, const Definition &def,
|
||||
const std::string &classcode, const std::string &path) {
|
||||
if (!classcode.length()) return true;
|
||||
|
||||
std::string name_space_java;
|
||||
std::string name_space_dir = path;
|
||||
for (auto it = parser.name_space_.begin();
|
||||
it != parser.name_space_.end(); ++it) {
|
||||
if (name_space_java.length()) {
|
||||
name_space_java += ".";
|
||||
name_space_dir += PATH_SEPARATOR;
|
||||
}
|
||||
name_space_java += *it;
|
||||
name_space_dir += *it;
|
||||
mkdir(name_space_dir.c_str(), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
||||
}
|
||||
|
||||
std::string code = "// automatically generated, do not modify\n\n";
|
||||
code += "package " + name_space_java + ";\n\n";
|
||||
code += "import java.nio.*;\nimport java.lang.*;\nimport java.util.*;\n";
|
||||
code += "import flatbuffers.*;\n\n";
|
||||
code += classcode;
|
||||
auto filename = name_space_dir + PATH_SEPARATOR + def.name + ".java";
|
||||
return SaveFile(filename.c_str(), code, false);
|
||||
}
|
||||
|
||||
} // namespace java
|
||||
|
||||
bool GenerateJava(const Parser &parser,
|
||||
const std::string &path,
|
||||
const std::string &file_name) {
|
||||
using namespace java;
|
||||
|
||||
for (auto it = parser.enums_.vec.begin();
|
||||
it != parser.enums_.vec.end(); ++it) {
|
||||
std::string enumcode;
|
||||
GenEnum(**it, &enumcode);
|
||||
if (!SaveClass(parser, **it, enumcode, path))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto it = parser.structs_.vec.begin();
|
||||
it != parser.structs_.vec.end(); ++it) {
|
||||
std::string declcode;
|
||||
GenStruct(**it, &declcode, parser.root_struct_def);
|
||||
if (!SaveClass(parser, **it, declcode, path))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
214
src/idl_gen_text.cpp
Normal file
214
src/idl_gen_text.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// independent from idl_parser, since this code is not needed for most clients
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
static void GenStruct(const StructDef &struct_def, const Table *table,
|
||||
int indent, int indent_step, std::string *_text);
|
||||
|
||||
// Print (and its template specialization below for pointers) generate text
|
||||
// for a single FlatBuffer value into JSON format.
|
||||
// The general case for scalars:
|
||||
template<typename T> void Print(T val, Type type, int indent, int indent_step,
|
||||
StructDef * /*union_sd*/, std::string *_text) {
|
||||
std::string &text = *_text;
|
||||
text += NumToString(val);
|
||||
}
|
||||
|
||||
// Print a vector a sequence of JSON values, comma separated, wrapped in "[]".
|
||||
template<typename T> void PrintVector(const Vector<T> &v, Type type,
|
||||
int indent, int indent_step,
|
||||
std::string *_text) {
|
||||
std::string &text = *_text;
|
||||
text += "[\n";
|
||||
for (uoffset_t i = 0; i < v.Length(); i++) {
|
||||
if (i) text += ",\n";
|
||||
text.append(indent + indent_step, ' ');
|
||||
if (IsStruct(type))
|
||||
Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type,
|
||||
indent + indent_step, indent_step, nullptr, _text);
|
||||
else
|
||||
Print(v.Get(i), type, indent + indent_step, indent_step, nullptr, _text);
|
||||
}
|
||||
text += "\n";
|
||||
text.append(indent, ' ');
|
||||
text += "]";
|
||||
}
|
||||
|
||||
static void EscapeString(const String &s, std::string *_text) {
|
||||
std::string &text = *_text;
|
||||
text += "\"";
|
||||
for (uoffset_t i = 0; i < s.Length(); i++) {
|
||||
char c = s.Get(i);
|
||||
switch (c) {
|
||||
case '\n': text += "\\n"; break;
|
||||
case '\t': text += "\\t"; break;
|
||||
case '\r': text += "\\r"; break;
|
||||
case '\"': text += "\\\""; break;
|
||||
case '\\': text += "\\\\"; break;
|
||||
default:
|
||||
if (c >= ' ' && c <= '~') {
|
||||
text += c;
|
||||
} else {
|
||||
auto u = static_cast<unsigned char>(c);
|
||||
text += "\\x" + IntToStringHex(u);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
text += "\"";
|
||||
}
|
||||
|
||||
// Specialization of Print above for pointer types.
|
||||
template<> void Print<const void *>(const void *val,
|
||||
Type type, int indent, int indent_step,
|
||||
StructDef *union_sd, std::string *_text) {
|
||||
switch (type.base_type) {
|
||||
case BASE_TYPE_UNION:
|
||||
// If this assert hits, you have an corrupt buffer, a union type field
|
||||
// was not present or was out of range.
|
||||
assert(union_sd);
|
||||
GenStruct(*union_sd,
|
||||
reinterpret_cast<const Table *>(val),
|
||||
indent,
|
||||
indent_step,
|
||||
_text);
|
||||
break;
|
||||
case BASE_TYPE_STRUCT:
|
||||
GenStruct(*type.struct_def,
|
||||
reinterpret_cast<const Table *>(val),
|
||||
indent,
|
||||
indent_step,
|
||||
_text);
|
||||
break;
|
||||
case BASE_TYPE_STRING: {
|
||||
EscapeString(*reinterpret_cast<const String *>(val), _text);
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_VECTOR:
|
||||
type = type.VectorType();
|
||||
// Call PrintVector above specifically for each element type:
|
||||
switch (type.base_type) {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM: \
|
||||
PrintVector<CTYPE>( \
|
||||
*reinterpret_cast<const Vector<CTYPE> *>(val), \
|
||||
type, indent, indent_step, _text); break;
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
}
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate text for a scalar field.
|
||||
template<typename T> static void GenField(const FieldDef &fd,
|
||||
const Table *table, bool fixed,
|
||||
int indent_step, int indent,
|
||||
std::string *_text) {
|
||||
Print(fixed ?
|
||||
reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset) :
|
||||
table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, indent_step,
|
||||
nullptr, _text);
|
||||
}
|
||||
|
||||
// Generate text for non-scalar field.
|
||||
static void GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
|
||||
int indent, int indent_step, StructDef *union_sd,
|
||||
std::string *_text) {
|
||||
const void *val = nullptr;
|
||||
if (fixed) {
|
||||
// The only non-scalar fields in structs are structs.
|
||||
assert(IsStruct(fd.value.type));
|
||||
val = reinterpret_cast<const Struct *>(table)->
|
||||
GetStruct<const void *>(fd.value.offset);
|
||||
} else {
|
||||
val = IsStruct(fd.value.type)
|
||||
? table->GetStruct<const void *>(fd.value.offset)
|
||||
: table->GetPointer<const void *>(fd.value.offset);
|
||||
}
|
||||
Print(val, fd.value.type, indent, indent_step, union_sd, _text);
|
||||
}
|
||||
|
||||
// Generate text for a struct or table, values separated by commas, indented,
|
||||
// and bracketed by "{}"
|
||||
static void GenStruct(const StructDef &struct_def, const Table *table,
|
||||
int indent, int indent_step, std::string *_text) {
|
||||
std::string &text = *_text;
|
||||
text += "{\n";
|
||||
int fieldout = 0;
|
||||
StructDef *union_sd = nullptr;
|
||||
for (auto it = struct_def.fields.vec.begin();
|
||||
it != struct_def.fields.vec.end();
|
||||
++it) {
|
||||
FieldDef &fd = **it;
|
||||
if (struct_def.fixed || table->CheckField(fd.value.offset)) {
|
||||
// The field is present.
|
||||
if (fieldout++) text += ",\n";
|
||||
text.append(indent + indent_step, ' ');
|
||||
text += fd.name;
|
||||
text += ": ";
|
||||
switch (fd.value.type.base_type) {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM: \
|
||||
GenField<CTYPE>(fd, table, struct_def.fixed, \
|
||||
indent + indent_step, indent_step, _text); \
|
||||
break;
|
||||
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
// Generate drop-thru case statements for all pointer types:
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM:
|
||||
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
GenFieldOffset(fd, table, struct_def.fixed, indent + indent_step,
|
||||
indent_step, union_sd, _text);
|
||||
break;
|
||||
}
|
||||
if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
|
||||
union_sd = fd.value.type.enum_def->ReverseLookup(
|
||||
table->GetField<uint8_t>(fd.value.offset, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
text += "\n";
|
||||
text.append(indent, ' ');
|
||||
text += "}";
|
||||
}
|
||||
|
||||
// Generate a text representation of a flatbuffer in JSON format.
|
||||
void GenerateText(const Parser &parser, const void *flatbuffer,
|
||||
int indent_step, std::string *_text) {
|
||||
std::string &text = *_text;
|
||||
assert(parser.root_struct_def); // call SetRootType()
|
||||
text.reserve(1024); // Reduce amount of inevitable reallocs.
|
||||
GenStruct(*parser.root_struct_def,
|
||||
GetRoot<Table>(flatbuffer),
|
||||
0,
|
||||
indent_step,
|
||||
_text);
|
||||
text += "\n";
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
724
src/idl_parser.cpp
Normal file
724
src/idl_parser.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers/idl.h"
|
||||
#include "flatbuffers/util.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
const char *const kTypeNames[] = {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) IDLTYPE,
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char kTypeSizes[] = {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) sizeof(CTYPE),
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
};
|
||||
|
||||
static void Error(const std::string &msg) {
|
||||
throw msg;
|
||||
}
|
||||
|
||||
// Ensure that integer values we parse fit inside the declared integer type.
|
||||
static void CheckBitsFit(int64_t val, size_t bits) {
|
||||
auto mask = (1ll << bits) - 1; // Bits we allow to be used.
|
||||
if (bits < 64 &&
|
||||
(val & ~mask) != 0 && // Positive or unsigned.
|
||||
(val | mask) != -1) // Negative.
|
||||
Error("constant does not fit in a " + NumToString(bits) + "-bit field");
|
||||
}
|
||||
|
||||
// atot: templated version of atoi/atof: convert a string to an instance of T.
|
||||
template<typename T> inline T atot(const char *s) {
|
||||
auto val = StringToInt(s);
|
||||
CheckBitsFit(val, sizeof(T) * 8);
|
||||
return (T)val;
|
||||
}
|
||||
template<> inline bool atot<bool>(const char *s) {
|
||||
return 0 != atoi(s);
|
||||
}
|
||||
template<> inline float atot<float>(const char *s) {
|
||||
return static_cast<float>(strtod(s, nullptr));
|
||||
}
|
||||
template<> inline double atot<double>(const char *s) {
|
||||
return strtod(s, nullptr);
|
||||
}
|
||||
|
||||
template<> inline Offset<void> atot<Offset<void>>(const char *s) {
|
||||
return Offset<void>(atoi(s));
|
||||
}
|
||||
|
||||
// Declare tokens we'll use. Single character tokens are represented by their
|
||||
// ascii character code (e.g. '{'), others above 256.
|
||||
#define FLATBUFFERS_GEN_TOKENS(TD) \
|
||||
TD(Eof, 256, "end of file") \
|
||||
TD(StringConstant, 257, "string constant") \
|
||||
TD(IntegerConstant, 258, "integer constant") \
|
||||
TD(FloatConstant, 259, "float constant") \
|
||||
TD(Identifier, 260, "identifier") \
|
||||
TD(Table, 261, "table") \
|
||||
TD(Struct, 262, "struct") \
|
||||
TD(Enum, 263, "enum") \
|
||||
TD(Union, 264, "union") \
|
||||
TD(NameSpace, 265, "namespace") \
|
||||
TD(RootType, 266, "root_type")
|
||||
enum {
|
||||
#define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME,
|
||||
FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
|
||||
#undef FLATBUFFERS_TOKEN
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) kToken ## ENUM,
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
};
|
||||
|
||||
static std::string TokenToString(int t) {
|
||||
static const char *tokens[] = {
|
||||
#define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
|
||||
FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
|
||||
#undef FLATBUFFERS_TOKEN
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) IDLTYPE,
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
};
|
||||
if (t < 256) { // A single ascii char token.
|
||||
std::string s;
|
||||
s.append(1, t);
|
||||
return s;
|
||||
} else { // Other tokens.
|
||||
return tokens[t - 256];
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::Next() {
|
||||
doc_comment_.clear();
|
||||
bool seen_newline = false;
|
||||
for (;;) {
|
||||
char c = *cursor_++;
|
||||
token_ = c;
|
||||
switch (c) {
|
||||
case '\0': cursor_--; token_ = kTokenEof; return;
|
||||
case ' ': case '\r': case '\t': break;
|
||||
case '\n': line_++; seen_newline = true; break;
|
||||
case '{': case '}': case '(': case ')': case '[': case ']': return;
|
||||
case ',': case ':': case ';': case '=': return;
|
||||
case '.':
|
||||
if(!isdigit(*cursor_)) return;
|
||||
Error("floating point constant can\'t start with \".\"");
|
||||
break;
|
||||
case '\"':
|
||||
attribute_ = "";
|
||||
while (*cursor_ != '\"') {
|
||||
if (*cursor_ < ' ' && *cursor_ >= 0)
|
||||
Error("illegal character in string constant");
|
||||
if (*cursor_ == '\\') {
|
||||
cursor_++;
|
||||
switch (*cursor_) {
|
||||
case 'n': attribute_ += '\n'; cursor_++; break;
|
||||
case 't': attribute_ += '\t'; cursor_++; break;
|
||||
case 'r': attribute_ += '\r'; cursor_++; break;
|
||||
case '\"': attribute_ += '\"'; cursor_++; break;
|
||||
case '\\': attribute_ += '\\'; cursor_++; break;
|
||||
default: Error("unknown escape code in string constant"); break;
|
||||
}
|
||||
} else { // printable chars + UTF-8 bytes
|
||||
attribute_ += *cursor_++;
|
||||
}
|
||||
}
|
||||
cursor_++;
|
||||
token_ = kTokenStringConstant;
|
||||
return;
|
||||
case '/':
|
||||
if (*cursor_ == '/') {
|
||||
const char *start = ++cursor_;
|
||||
while (*cursor_ && *cursor_ != '\n') cursor_++;
|
||||
if (*start == '/') { // documentation comment
|
||||
if (!seen_newline)
|
||||
Error("a documentation comment should be on a line on its own");
|
||||
// todo: do we want to support multiline comments instead?
|
||||
doc_comment_ += std::string(start + 1, cursor_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// fall thru
|
||||
default:
|
||||
if (isalpha(static_cast<unsigned char>(c))) {
|
||||
// Collect all chars of an identifier:
|
||||
const char *start = cursor_ - 1;
|
||||
while (isalnum(static_cast<unsigned char>(*cursor_)) ||
|
||||
*cursor_ == '_')
|
||||
cursor_++;
|
||||
attribute_.clear();
|
||||
attribute_.append(start, cursor_);
|
||||
// First, see if it is a type keyword from the table of types:
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
if (attribute_ == IDLTYPE) { \
|
||||
token_ = kToken ## ENUM; \
|
||||
return; \
|
||||
}
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
// If it's a boolean constant keyword, turn those into integers,
|
||||
// which simplifies our logic downstream.
|
||||
if (attribute_ == "true" || attribute_ == "false") {
|
||||
attribute_ = NumToString(attribute_ == "true");
|
||||
token_ = kTokenIntegerConstant;
|
||||
return;
|
||||
}
|
||||
// Check for declaration keywords:
|
||||
if (attribute_ == "table") { token_ = kTokenTable; return; }
|
||||
if (attribute_ == "struct") { token_ = kTokenStruct; return; }
|
||||
if (attribute_ == "enum") { token_ = kTokenEnum; return; }
|
||||
if (attribute_ == "union") { token_ = kTokenUnion; return; }
|
||||
if (attribute_ == "namespace") { token_ = kTokenNameSpace; return; }
|
||||
if (attribute_ == "root_type") { token_ = kTokenRootType; return; }
|
||||
// If not, it is a user-defined identifier:
|
||||
token_ = kTokenIdentifier;
|
||||
return;
|
||||
} else if (isdigit(static_cast<unsigned char>(c)) || c == '-') {
|
||||
const char *start = cursor_ - 1;
|
||||
while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
|
||||
if (*cursor_ == '.') {
|
||||
cursor_++;
|
||||
while (isdigit(static_cast<unsigned char>(*cursor_))) cursor_++;
|
||||
token_ = kTokenFloatConstant;
|
||||
} else {
|
||||
token_ = kTokenIntegerConstant;
|
||||
}
|
||||
attribute_.clear();
|
||||
attribute_.append(start, cursor_);
|
||||
return;
|
||||
}
|
||||
std::string ch;
|
||||
ch = c;
|
||||
if (c < ' ' || c > '~') ch = "code: " + NumToString(c);
|
||||
Error("illegal character: " + ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a given token is next, if so, consume it as well.
|
||||
bool Parser::IsNext(int t) {
|
||||
bool isnext = t == token_;
|
||||
if (isnext) Next();
|
||||
return isnext;
|
||||
}
|
||||
|
||||
// Expect a given token to be next, consume it, or error if not present.
|
||||
void Parser::Expect(int t) {
|
||||
if (t != token_) {
|
||||
Error("expecting: " + TokenToString(t) + " instead got: " +
|
||||
TokenToString(token_));
|
||||
}
|
||||
Next();
|
||||
}
|
||||
|
||||
// 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_);
|
||||
}
|
||||
} else if (token_ == '[') {
|
||||
Next();
|
||||
Type subtype;
|
||||
ParseType(subtype);
|
||||
if (subtype.base_type == BASE_TYPE_VECTOR) {
|
||||
// We could support this, but it will complicate things, and it's
|
||||
// easier to work around with a struct around the inner vector.
|
||||
Error("nested vector types not supported (wrap in table first).");
|
||||
}
|
||||
if (subtype.base_type == BASE_TYPE_UNION) {
|
||||
// We could support this if we stored a struct of 2 elements per
|
||||
// union element.
|
||||
Error("vector of union types not supported (wrap in table first).");
|
||||
}
|
||||
type = Type(BASE_TYPE_VECTOR, subtype.struct_def);
|
||||
type.element = subtype.base_type;
|
||||
Expect(']');
|
||||
return;
|
||||
} else {
|
||||
Error("illegal type syntax");
|
||||
}
|
||||
}
|
||||
Next();
|
||||
}
|
||||
|
||||
FieldDef &Parser::AddField(StructDef &struct_def,
|
||||
const std::string &name,
|
||||
const Type &type) {
|
||||
auto &field = *new FieldDef();
|
||||
field.value.offset =
|
||||
FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
|
||||
field.name = name;
|
||||
field.value.type = type;
|
||||
if (struct_def.fixed) { // statically compute the field offset
|
||||
auto size = InlineSize(type);
|
||||
auto alignment = InlineAlignment(type);
|
||||
// structs_ need to have a predictable format, so we need to align to
|
||||
// the largest scalar
|
||||
struct_def.minalign = std::max(struct_def.minalign, alignment);
|
||||
struct_def.PadLastField(alignment);
|
||||
field.value.offset = static_cast<uoffset_t>(struct_def.bytesize);
|
||||
struct_def.bytesize += size;
|
||||
}
|
||||
if (struct_def.fields.Add(name, &field))
|
||||
Error("field already exists: " + name);
|
||||
return field;
|
||||
}
|
||||
|
||||
void Parser::ParseField(StructDef &struct_def) {
|
||||
std::string name = attribute_;
|
||||
std::string dc = doc_comment_;
|
||||
Expect(kTokenIdentifier);
|
||||
Expect(':');
|
||||
Type type;
|
||||
ParseType(type);
|
||||
|
||||
if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type))
|
||||
Error("structs_ may contain only scalar or struct fields");
|
||||
|
||||
if (type.base_type == BASE_TYPE_UNION) {
|
||||
// For union fields, add a second auto-generated field to hold the type,
|
||||
// with _type appended as the name.
|
||||
AddField(struct_def, name + "_type", type.enum_def->underlying_type);
|
||||
}
|
||||
|
||||
auto &field = AddField(struct_def, name, type);
|
||||
|
||||
if (token_ == '=') {
|
||||
Next();
|
||||
ParseSingleValue(field.value);
|
||||
}
|
||||
|
||||
field.doc_comment = dc;
|
||||
ParseMetaData(field);
|
||||
field.deprecated = field.attributes.Lookup("deprecated") != nullptr;
|
||||
if (field.deprecated && struct_def.fixed)
|
||||
Error("can't deprecate fields in a struct");
|
||||
|
||||
Expect(';');
|
||||
}
|
||||
|
||||
void Parser::ParseAnyValue(Value &val, FieldDef *field) {
|
||||
switch (val.type.base_type) {
|
||||
case BASE_TYPE_UNION: {
|
||||
assert(field);
|
||||
if (!field_stack_.size() ||
|
||||
field_stack_.back().second->value.type.base_type != BASE_TYPE_UTYPE)
|
||||
Error("missing type field before this union value: " + field->name);
|
||||
auto enum_idx = atot<unsigned char>(
|
||||
field_stack_.back().first.constant.c_str());
|
||||
auto struct_def = val.type.enum_def->ReverseLookup(enum_idx);
|
||||
if (!struct_def) Error("illegal type id for: " + field->name);
|
||||
val.constant = NumToString(ParseTable(*struct_def));
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_STRUCT:
|
||||
val.constant = NumToString(ParseTable(*val.type.struct_def));
|
||||
break;
|
||||
case BASE_TYPE_STRING: {
|
||||
auto s = attribute_;
|
||||
Expect(kTokenStringConstant);
|
||||
val.constant = NumToString(builder_.CreateString(s).o);
|
||||
break;
|
||||
}
|
||||
case BASE_TYPE_VECTOR: {
|
||||
Expect('[');
|
||||
val.constant = NumToString(ParseVector(val.type.VectorType()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ParseSingleValue(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
|
||||
auto off = atot<uoffset_t>(val.constant.c_str());
|
||||
assert(struct_stack_.size() - off == struct_def.bytesize);
|
||||
builder_.Align(struct_def.minalign);
|
||||
builder_.PushBytes(&struct_stack_[off], struct_def.bytesize);
|
||||
struct_stack_.resize(struct_stack_.size() - struct_def.bytesize);
|
||||
builder_.AddStructOffset(val.offset, builder_.GetSize());
|
||||
}
|
||||
|
||||
uoffset_t Parser::ParseTable(const StructDef &struct_def) {
|
||||
Expect('{');
|
||||
size_t fieldn = 0;
|
||||
for (;;) {
|
||||
std::string name = attribute_;
|
||||
if (!IsNext(kTokenStringConstant)) Expect(kTokenIdentifier);
|
||||
auto field = struct_def.fields.Lookup(name);
|
||||
if (!field) Error("unknown field: " + name);
|
||||
if (struct_def.fixed && (fieldn >= struct_def.fields.vec.size()
|
||||
|| struct_def.fields.vec[fieldn] != field)) {
|
||||
Error("struct field appearing out of order: " + name);
|
||||
}
|
||||
Expect(':');
|
||||
Value val = field->value;
|
||||
ParseAnyValue(val, field);
|
||||
field_stack_.push_back(std::make_pair(val, field));
|
||||
fieldn++;
|
||||
if (IsNext('}')) break;
|
||||
Expect(',');
|
||||
}
|
||||
if (struct_def.fixed && fieldn != struct_def.fields.vec.size())
|
||||
Error("incomplete struct initialization: " + struct_def.name);
|
||||
auto start = struct_def.fixed
|
||||
? builder_.StartStruct(struct_def.minalign)
|
||||
: builder_.StartTable();
|
||||
|
||||
for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
|
||||
size;
|
||||
size /= 2) {
|
||||
// Go through elements in reverse, since we're building the data backwards.
|
||||
for (auto it = field_stack_.rbegin();
|
||||
it != field_stack_.rbegin() + fieldn; ++it) {
|
||||
auto &value = it->first;
|
||||
auto field = it->second;
|
||||
if (!struct_def.sortbysize || size == SizeOf(value.type.base_type)) {
|
||||
switch (value.type.base_type) {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM: \
|
||||
builder_.Pad(field->padding); \
|
||||
builder_.AddElement(value.offset, \
|
||||
atot<CTYPE>( value.constant.c_str()), \
|
||||
atot<CTYPE>(field->value.constant.c_str())); \
|
||||
break;
|
||||
FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
|
||||
#undef FLATBUFFERS_TD
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM: \
|
||||
builder_.Pad(field->padding); \
|
||||
if (IsStruct(field->value.type)) { \
|
||||
SerializeStruct(*field->value.type.struct_def, value); \
|
||||
} else { \
|
||||
builder_.AddOffset(value.offset, \
|
||||
atot<CTYPE>(value.constant.c_str())); \
|
||||
} \
|
||||
break;
|
||||
FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
|
||||
#undef FLATBUFFERS_TD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < fieldn; i++) field_stack_.pop_back();
|
||||
|
||||
if (struct_def.fixed) {
|
||||
builder_.ClearOffsets();
|
||||
builder_.EndStruct();
|
||||
// Temporarily store this struct in a side buffer, since this data has to
|
||||
// be stored in-line later in the parent object.
|
||||
auto off = struct_stack_.size();
|
||||
struct_stack_.insert(struct_stack_.end(),
|
||||
builder_.GetBufferPointer(),
|
||||
builder_.GetBufferPointer() + struct_def.bytesize);
|
||||
builder_.PopBytes(struct_def.bytesize);
|
||||
return static_cast<uoffset_t>(off);
|
||||
} else {
|
||||
return builder_.EndTable(
|
||||
start,
|
||||
static_cast<voffset_t>(struct_def.fields.vec.size()));
|
||||
}
|
||||
}
|
||||
|
||||
uoffset_t Parser::ParseVector(const Type &type) {
|
||||
int count = 0;
|
||||
if (token_ != ']') for (;;) {
|
||||
Value val;
|
||||
val.type = type;
|
||||
ParseAnyValue(val, NULL);
|
||||
field_stack_.push_back(std::make_pair(val, nullptr));
|
||||
count++;
|
||||
if (token_ == ']') break;
|
||||
Expect(',');
|
||||
}
|
||||
Next();
|
||||
|
||||
builder_.StartVector(count * InlineSize(type), InlineAlignment((type)));
|
||||
for (int i = 0; i < count; i++) {
|
||||
// start at the back, since we're building the data backwards.
|
||||
auto &val = field_stack_.back().first;
|
||||
switch (val.type.base_type) {
|
||||
#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE) \
|
||||
case BASE_TYPE_ ## ENUM: \
|
||||
if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
|
||||
else builder_.PushElement(atot<CTYPE>(val.constant.c_str())); \
|
||||
break;
|
||||
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
||||
#undef FLATBUFFERS_TD
|
||||
}
|
||||
field_stack_.pop_back();
|
||||
}
|
||||
|
||||
builder_.ClearOffsets();
|
||||
return builder_.EndVector(count);
|
||||
}
|
||||
|
||||
void Parser::ParseMetaData(Definition &def) {
|
||||
if (IsNext('(')) {
|
||||
for (;;) {
|
||||
auto name = attribute_;
|
||||
Expect(kTokenIdentifier);
|
||||
auto e = new Value();
|
||||
def.attributes.Add(name, e);
|
||||
if (IsNext(':')) {
|
||||
ParseSingleValue(*e);
|
||||
}
|
||||
if (IsNext(')')) break;
|
||||
Expect(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::TryTypedValue(int dtoken,
|
||||
bool check,
|
||||
Value &e,
|
||||
BaseType req) {
|
||||
bool match = dtoken == token_;
|
||||
if (match) {
|
||||
e.constant = attribute_;
|
||||
if (!check) {
|
||||
if (e.type.base_type == BASE_TYPE_NONE) {
|
||||
e.type.base_type = req;
|
||||
} else {
|
||||
Error(std::string("type mismatch: expecting: ") +
|
||||
kTypeNames[e.type.base_type] +
|
||||
", found: " +
|
||||
kTypeNames[req]);
|
||||
}
|
||||
}
|
||||
Next();
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
void Parser::ParseSingleValue(Value &e) {
|
||||
if (TryTypedValue(kTokenIntegerConstant,
|
||||
IsScalar(e.type.base_type),
|
||||
e,
|
||||
BASE_TYPE_INT) ||
|
||||
TryTypedValue(kTokenFloatConstant,
|
||||
IsFloat(e.type.base_type),
|
||||
e,
|
||||
BASE_TYPE_FLOAT) ||
|
||||
TryTypedValue(kTokenStringConstant,
|
||||
e.type.base_type == BASE_TYPE_STRING,
|
||||
e,
|
||||
BASE_TYPE_STRING)) {
|
||||
} else if (token_ == kTokenIdentifier) {
|
||||
for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
|
||||
auto ev = (*it)->vals.Lookup(attribute_);
|
||||
if (ev) {
|
||||
attribute_ = NumToString(ev->value);
|
||||
TryTypedValue(kTokenIdentifier,
|
||||
IsInteger(e.type.base_type),
|
||||
e,
|
||||
BASE_TYPE_INT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Error("not valid enum value: " + attribute_);
|
||||
} else {
|
||||
Error("cannot parse value starting with: " + TokenToString(token_));
|
||||
}
|
||||
}
|
||||
|
||||
StructDef *Parser::LookupCreateStruct(const std::string &name) {
|
||||
auto 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.
|
||||
struct_def = new StructDef();
|
||||
structs_.Add(name, struct_def);
|
||||
struct_def->name = name;
|
||||
struct_def->predecl = true;
|
||||
}
|
||||
return struct_def;
|
||||
}
|
||||
|
||||
void Parser::ParseEnum(bool is_union) {
|
||||
std::string dc = doc_comment_;
|
||||
Next();
|
||||
std::string name = attribute_;
|
||||
Expect(kTokenIdentifier);
|
||||
auto &enum_def = *new EnumDef();
|
||||
enum_def.name = name;
|
||||
enum_def.doc_comment = dc;
|
||||
enum_def.is_union = is_union;
|
||||
if (enums_.Add(name, &enum_def)) Error("enum already exists: " + name);
|
||||
if (is_union) {
|
||||
enum_def.underlying_type.base_type = BASE_TYPE_UTYPE;
|
||||
enum_def.underlying_type.enum_def = &enum_def;
|
||||
} else if (IsNext(':')) {
|
||||
// short is the default type for fields when you use enums,
|
||||
// though people are encouraged to pick any integer type instead.
|
||||
ParseType(enum_def.underlying_type);
|
||||
if (!IsInteger(enum_def.underlying_type.base_type))
|
||||
Error("underlying enum type must be integral");
|
||||
} else {
|
||||
enum_def.underlying_type.base_type = BASE_TYPE_SHORT;
|
||||
}
|
||||
ParseMetaData(enum_def);
|
||||
Expect('{');
|
||||
if (is_union) enum_def.vals.Add("NONE", new EnumVal("NONE", 0));
|
||||
do {
|
||||
std::string name = attribute_;
|
||||
std::string dc = doc_comment_;
|
||||
Expect(kTokenIdentifier);
|
||||
auto prevsize = enum_def.vals.vec.size();
|
||||
auto &ev = *new EnumVal(name, static_cast<int>(
|
||||
enum_def.vals.vec.size()
|
||||
? enum_def.vals.vec.back()->value + 1
|
||||
: 0));
|
||||
if (enum_def.vals.Add(name, &ev))
|
||||
Error("enum value already exists: " + name);
|
||||
ev.doc_comment = dc;
|
||||
if (is_union) {
|
||||
ev.struct_def = LookupCreateStruct(name);
|
||||
}
|
||||
if (IsNext('=')) {
|
||||
ev.value = atoi(attribute_.c_str());
|
||||
Expect(kTokenIntegerConstant);
|
||||
if (prevsize && enum_def.vals.vec[prevsize - 1]->value >= ev.value)
|
||||
Error("enum values must be specified in ascending order");
|
||||
}
|
||||
} while (IsNext(','));
|
||||
Expect('}');
|
||||
}
|
||||
|
||||
void Parser::ParseDecl() {
|
||||
std::string dc = doc_comment_;
|
||||
bool fixed = IsNext(kTokenStruct);
|
||||
if (!fixed) Expect(kTokenTable);
|
||||
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.
|
||||
remove(structs_.vec.begin(), structs_.vec.end(), &struct_def);
|
||||
structs_.vec.back() = &struct_def;
|
||||
ParseMetaData(struct_def);
|
||||
struct_def.sortbysize =
|
||||
struct_def.attributes.Lookup("original_order") == nullptr && !fixed;
|
||||
Expect('{');
|
||||
while (token_ != '}') ParseField(struct_def);
|
||||
struct_def.PadLastField(struct_def.minalign);
|
||||
Expect('}');
|
||||
auto force_align = struct_def.attributes.Lookup("force_align");
|
||||
if (fixed && force_align) {
|
||||
auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
|
||||
if (force_align->type.base_type != BASE_TYPE_INT ||
|
||||
align < struct_def.minalign ||
|
||||
align > 256 ||
|
||||
align & (align - 1))
|
||||
Error("force_align must be a power of two integer ranging from the"
|
||||
"struct\'s natural alignment to 256");
|
||||
struct_def.minalign = align;
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::SetRootType(const char *name) {
|
||||
root_struct_def = structs_.Lookup(name);
|
||||
return root_struct_def != nullptr;
|
||||
}
|
||||
|
||||
bool Parser::Parse(const char *source) {
|
||||
source_ = cursor_ = source;
|
||||
line_ = 1;
|
||||
error_.clear();
|
||||
builder_.Clear();
|
||||
try {
|
||||
Next();
|
||||
while (token_ != kTokenEof) {
|
||||
if (token_ == kTokenNameSpace) {
|
||||
Next();
|
||||
for (;;) {
|
||||
name_space_.push_back(attribute_);
|
||||
Expect(kTokenIdentifier);
|
||||
if (!IsNext('.')) break;
|
||||
}
|
||||
Expect(';');
|
||||
} else if (token_ == '{') {
|
||||
if (!root_struct_def) Error("no root type set to parse json with");
|
||||
if (builder_.GetSize()) {
|
||||
Error("cannot have more than one json object in a file");
|
||||
}
|
||||
builder_.Finish(Offset<Table>(ParseTable(*root_struct_def)));
|
||||
} else if (token_ == kTokenEnum) {
|
||||
ParseEnum(false);
|
||||
} else if (token_ == kTokenUnion) {
|
||||
ParseEnum(true);
|
||||
} else if (token_ == kTokenRootType) {
|
||||
Next();
|
||||
auto root_type = attribute_;
|
||||
Expect(kTokenIdentifier);
|
||||
Expect(';');
|
||||
if (!SetRootType(root_type.c_str()))
|
||||
Error("unknown root type: " + root_type);
|
||||
if (root_struct_def->fixed)
|
||||
Error("root type must be a table");
|
||||
} else {
|
||||
ParseDecl();
|
||||
}
|
||||
}
|
||||
for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
|
||||
if ((*it)->predecl)
|
||||
Error("type referenced but not defined: " + (*it)->name);
|
||||
}
|
||||
for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
|
||||
auto &enum_def = **it;
|
||||
if (enum_def.is_union) {
|
||||
for (auto it = enum_def.vals.vec.begin();
|
||||
it != enum_def.vals.vec.end();
|
||||
++it) {
|
||||
auto &val = **it;
|
||||
if (val.struct_def && val.struct_def->fixed)
|
||||
Error("only tables can be union elements: " + val.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::string &msg) {
|
||||
error_ = "line " + NumToString(line_) + ": " + msg;
|
||||
return false;
|
||||
}
|
||||
assert(!struct_stack_.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
Reference in New Issue
Block a user