681 lines
24 KiB
C++
681 lines
24 KiB
C++
/*
|
|
* Copyright 2021 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 "bfbs_gen_lua.h"
|
|
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
// Ensure no includes to flatc internals. bfbs_gen.h and generator.h are OK.
|
|
#include "bfbs_gen.h"
|
|
#include "bfbs_namer.h"
|
|
|
|
// The intermediate representation schema.
|
|
#include "flatbuffers/code_generator.h"
|
|
#include "flatbuffers/reflection.h"
|
|
#include "flatbuffers/reflection_generated.h"
|
|
|
|
namespace flatbuffers {
|
|
namespace {
|
|
|
|
// To reduce typing
|
|
namespace r = ::reflection;
|
|
|
|
std::set<std::string> LuaKeywords() {
|
|
return { "and", "break", "do", "else", "elseif", "end",
|
|
"false", "for", "function", "goto", "if", "in",
|
|
"local", "nil", "not", "or", "repeat", "return",
|
|
"then", "true", "until", "while" };
|
|
}
|
|
|
|
Namer::Config LuaDefaultConfig() {
|
|
return { /*types=*/Case::kUpperCamel,
|
|
/*constants=*/Case::kUnknown,
|
|
/*methods=*/Case::kUpperCamel,
|
|
/*functions=*/Case::kUpperCamel,
|
|
/*fields=*/Case::kUpperCamel,
|
|
/*variables=*/Case::kLowerCamel,
|
|
/*variants=*/Case::kKeep,
|
|
/*enum_variant_seperator=*/"",
|
|
/*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
|
|
/*namespaces=*/Case::kKeep,
|
|
/*namespace_seperator=*/"__",
|
|
/*object_prefix=*/"",
|
|
/*object_suffix=*/"",
|
|
/*keyword_prefix=*/"",
|
|
/*keyword_suffix=*/"_",
|
|
/*filenames=*/Case::kKeep,
|
|
/*directories=*/Case::kKeep,
|
|
/*output_path=*/"",
|
|
/*filename_suffix=*/"",
|
|
/*filename_extension=*/".lua" };
|
|
}
|
|
|
|
class LuaBfbsGenerator : public BaseBfbsGenerator {
|
|
public:
|
|
explicit LuaBfbsGenerator(const std::string &flatc_version)
|
|
: BaseBfbsGenerator(),
|
|
keywords_(),
|
|
requires_(),
|
|
current_obj_(nullptr),
|
|
current_enum_(nullptr),
|
|
flatc_version_(flatc_version),
|
|
namer_(LuaDefaultConfig(), LuaKeywords()) {}
|
|
|
|
Status GenerateFromSchema(const r::Schema *schema,
|
|
const CodeGenOptions &options)
|
|
FLATBUFFERS_OVERRIDE {
|
|
options_ = options;
|
|
if (!GenerateEnums(schema->enums())) { return ERROR; }
|
|
if (!GenerateObjects(schema->objects(), schema->root_table())) {
|
|
return ERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
using BaseBfbsGenerator::GenerateCode;
|
|
|
|
Status GenerateCode(const Parser &, const std::string &,
|
|
const std::string &) override {
|
|
return Status::NOT_IMPLEMENTED;
|
|
}
|
|
|
|
Status GenerateMakeRule(const Parser &parser, const std::string &path,
|
|
const std::string &filename,
|
|
std::string &output) override {
|
|
(void)parser;
|
|
(void)path;
|
|
(void)filename;
|
|
(void)output;
|
|
return Status::NOT_IMPLEMENTED;
|
|
}
|
|
|
|
Status GenerateGrpcCode(const Parser &parser, const std::string &path,
|
|
const std::string &filename) override {
|
|
(void)parser;
|
|
(void)path;
|
|
(void)filename;
|
|
return Status::NOT_IMPLEMENTED;
|
|
}
|
|
|
|
Status GenerateRootFile(const Parser &parser,
|
|
const std::string &path) override {
|
|
(void)parser;
|
|
(void)path;
|
|
return Status::NOT_IMPLEMENTED;
|
|
}
|
|
|
|
bool IsSchemaOnly() const override { return true; }
|
|
|
|
bool SupportsBfbsGeneration() const override { return true; }
|
|
|
|
bool SupportsRootFileGeneration() const override { return false; }
|
|
|
|
IDLOptions::Language Language() const override { return IDLOptions::kLua; }
|
|
|
|
std::string LanguageName() const override { return "Lua"; }
|
|
|
|
uint64_t SupportedAdvancedFeatures() const FLATBUFFERS_OVERRIDE {
|
|
return 0xF;
|
|
}
|
|
|
|
protected:
|
|
bool GenerateEnums(
|
|
const flatbuffers::Vector<flatbuffers::Offset<r::Enum>> *enums) {
|
|
ForAllEnums(enums, [&](const r::Enum *enum_def) {
|
|
std::string code;
|
|
|
|
StartCodeBlock(enum_def);
|
|
|
|
std::string ns;
|
|
const std::string enum_name =
|
|
namer_.Type(namer_.Denamespace(enum_def, ns));
|
|
|
|
GenerateDocumentation(enum_def->documentation(), "", code);
|
|
code += "local " + enum_name + " = {\n";
|
|
|
|
ForAllEnumValues(enum_def, [&](const reflection::EnumVal *enum_val) {
|
|
GenerateDocumentation(enum_val->documentation(), " ", code);
|
|
code += " " + namer_.Variant(enum_val->name()->str()) + " = " +
|
|
NumToString(enum_val->value()) + ",\n";
|
|
});
|
|
code += "}\n";
|
|
code += "\n";
|
|
|
|
EmitCodeBlock(code, enum_name, ns, enum_def->declaration_file()->str());
|
|
});
|
|
return true;
|
|
}
|
|
|
|
bool GenerateObjects(
|
|
const flatbuffers::Vector<flatbuffers::Offset<r::Object>> *objects,
|
|
const r::Object *root_object) {
|
|
ForAllObjects(objects, [&](const r::Object *object) {
|
|
std::string code;
|
|
|
|
StartCodeBlock(object);
|
|
|
|
// Register the main flatbuffers module.
|
|
RegisterRequires("flatbuffers", "flatbuffers");
|
|
|
|
std::string ns;
|
|
const std::string object_name =
|
|
namer_.Type(namer_.Denamespace(object, ns));
|
|
|
|
GenerateDocumentation(object->documentation(), "", code);
|
|
|
|
code += "local " + object_name + " = {}\n";
|
|
code += "local mt = {}\n";
|
|
code += "\n";
|
|
code += "function " + object_name + ".New()\n";
|
|
code += " local o = {}\n";
|
|
code += " setmetatable(o, {__index = mt})\n";
|
|
code += " return o\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
|
|
if (object == root_object) {
|
|
code += "function " + object_name + ".GetRootAs" + object_name +
|
|
"(buf, offset)\n";
|
|
code += " if type(buf) == \"string\" then\n";
|
|
code += " buf = flatbuffers.binaryArray.New(buf)\n";
|
|
code += " end\n";
|
|
code += "\n";
|
|
code += " local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)\n";
|
|
code += " local o = " + object_name + ".New()\n";
|
|
code += " o:Init(buf, n + offset)\n";
|
|
code += " return o\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
}
|
|
|
|
// Generates a init method that receives a pre-existing accessor object,
|
|
// so that objects can be reused.
|
|
|
|
code += "function mt:Init(buf, pos)\n";
|
|
code += " self.view = flatbuffers.view.New(buf, pos)\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
|
|
// Create all the field accessors.
|
|
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
|
|
// Skip writing deprecated fields altogether.
|
|
if (field->deprecated()) { return; }
|
|
|
|
const std::string field_name = namer_.Field(*field);
|
|
const r::BaseType base_type = field->type()->base_type();
|
|
|
|
// Generate some fixed strings so we don't repeat outselves later.
|
|
const std::string getter_signature =
|
|
"function mt:" + field_name + "()\n";
|
|
const std::string offset_prefix = "local o = self.view:Offset(" +
|
|
NumToString(field->offset()) + ")\n";
|
|
const std::string offset_prefix_2 = "if o ~= 0 then\n";
|
|
|
|
GenerateDocumentation(field->documentation(), "", code);
|
|
|
|
if (IsScalar(base_type)) {
|
|
code += getter_signature;
|
|
|
|
if (object->is_struct()) {
|
|
// TODO(derekbailey): it would be nice to modify the view:Get to
|
|
// just pass in the offset and not have to add it its own
|
|
// self.view.pos.
|
|
code += " return " + GenerateGetter(field->type()) +
|
|
"self.view.pos + " + NumToString(field->offset()) + ")\n";
|
|
} else {
|
|
// Table accessors
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
|
|
std::string getter =
|
|
GenerateGetter(field->type()) + "self.view.pos + o)";
|
|
if (IsBool(base_type)) { getter = "(" + getter + " ~=0)"; }
|
|
code += " return " + getter + "\n";
|
|
code += " end\n";
|
|
code += " return " + DefaultValue(field) + "\n";
|
|
}
|
|
code += "end\n";
|
|
code += "\n";
|
|
} else {
|
|
switch (base_type) {
|
|
case r::String: {
|
|
code += getter_signature;
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
code += " return " + GenerateGetter(field->type()) +
|
|
"self.view.pos + o)\n";
|
|
code += " end\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
break;
|
|
}
|
|
case r::Obj: {
|
|
if (object->is_struct()) {
|
|
code += "function mt:" + field_name + "(obj)\n";
|
|
code += " obj:Init(self.view.bytes, self.view.pos + " +
|
|
NumToString(field->offset()) + ")\n";
|
|
code += " return obj\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
} else {
|
|
code += getter_signature;
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
|
|
const r::Object *field_object = GetObject(field->type());
|
|
if (!field_object) {
|
|
// TODO(derekbailey): this is an error condition. we
|
|
// should report it better.
|
|
return;
|
|
}
|
|
code += " local x = " +
|
|
std::string(
|
|
field_object->is_struct()
|
|
? "self.view.pos + o\n"
|
|
: "self.view:Indirect(self.view.pos + o)\n");
|
|
const std::string require_name = RegisterRequires(field);
|
|
code += " local obj = " + require_name + ".New()\n";
|
|
code += " obj:Init(self.view.bytes, x)\n";
|
|
code += " return obj\n";
|
|
code += " end\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
}
|
|
break;
|
|
}
|
|
case r::Union: {
|
|
code += getter_signature;
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
code +=
|
|
" local obj = "
|
|
"flatbuffers.view.New(flatbuffers.binaryArray.New("
|
|
"0), 0)\n";
|
|
code += " " + GenerateGetter(field->type()) + "obj, o)\n";
|
|
code += " return obj\n";
|
|
code += " end\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
break;
|
|
}
|
|
case r::Array:
|
|
case r::Vector: {
|
|
const r::BaseType vector_base_type = field->type()->element();
|
|
int32_t element_size = field->type()->element_size();
|
|
code += "function mt:" + field_name + "(j)\n";
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
|
|
if (IsStructOrTable(vector_base_type)) {
|
|
code += " local x = self.view:Vector(o)\n";
|
|
code +=
|
|
" x = x + ((j-1) * " + NumToString(element_size) + ")\n";
|
|
if (IsTable(field->type(), /*use_element=*/true)) {
|
|
code += " x = self.view:Indirect(x)\n";
|
|
} else {
|
|
// Vector of structs are inline, so we need to query the
|
|
// size of the struct.
|
|
const reflection::Object *obj =
|
|
GetObjectByIndex(field->type()->index());
|
|
element_size = obj->bytesize();
|
|
}
|
|
|
|
// Include the referenced type, thus we need to make sure
|
|
// we set `use_element` to true.
|
|
const std::string require_name =
|
|
RegisterRequires(field, /*use_element=*/true);
|
|
code += " local obj = " + require_name + ".New()\n";
|
|
code += " obj:Init(self.view.bytes, x)\n";
|
|
code += " return obj\n";
|
|
} else {
|
|
code += " local a = self.view:Vector(o)\n";
|
|
code += " return " + GenerateGetter(field->type()) +
|
|
"a + ((j-1) * " + NumToString(element_size) + "))\n";
|
|
}
|
|
code += " end\n";
|
|
// Only generate a default value for those types that are
|
|
// supported.
|
|
if (!IsStructOrTable(vector_base_type)) {
|
|
code +=
|
|
" return " +
|
|
std::string(vector_base_type == r::String ? "''\n" : "0\n");
|
|
}
|
|
code += "end\n";
|
|
code += "\n";
|
|
|
|
// If the vector is composed of single byte values, we
|
|
// generate a helper function to get it as a byte string in
|
|
// Lua.
|
|
if (IsSingleByte(vector_base_type)) {
|
|
code += "function mt:" + field_name + "AsString(start, stop)\n";
|
|
code += " return self.view:VectorAsString(" +
|
|
NumToString(field->offset()) + ", start, stop)\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
}
|
|
|
|
// We also make a new accessor to query just the length of the
|
|
// vector.
|
|
code += "function mt:" + field_name + "Length()\n";
|
|
code += " " + offset_prefix;
|
|
code += " " + offset_prefix_2;
|
|
code += " return self.view:VectorLen(o)\n";
|
|
code += " end\n";
|
|
code += " return 0\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
break;
|
|
}
|
|
default: {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
});
|
|
|
|
// Create all the builders
|
|
if (object->is_struct()) {
|
|
code += "function " + object_name + ".Create" + object_name +
|
|
"(builder" + GenerateStructBuilderArgs(object) + ")\n";
|
|
code += AppendStructBuilderBody(object);
|
|
code += " return builder:Offset()\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
} else {
|
|
// Table builders
|
|
code += "function " + object_name + ".Start(builder)\n";
|
|
code += " builder:StartObject(" +
|
|
NumToString(object->fields()->size()) + ")\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
|
|
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
|
|
if (field->deprecated()) { return; }
|
|
|
|
const std::string field_name = namer_.Field(*field);
|
|
const std::string variable_name = namer_.Variable(*field);
|
|
|
|
code += "function " + object_name + ".Add" + field_name +
|
|
"(builder, " + variable_name + ")\n";
|
|
code += " builder:Prepend" + GenerateMethod(field) + "Slot(" +
|
|
NumToString(field->id()) + ", " + variable_name + ", " +
|
|
DefaultValue(field) + ")\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
|
|
if (IsVector(field->type()->base_type())) {
|
|
code += "function " + object_name + ".Start" + field_name +
|
|
"Vector(builder, numElems)\n";
|
|
|
|
const int32_t element_size = field->type()->element_size();
|
|
int32_t alignment = 0;
|
|
if (IsStruct(field->type(), /*use_element=*/true)) {
|
|
alignment = GetObjectByIndex(field->type()->index())->minalign();
|
|
} else {
|
|
alignment = element_size;
|
|
}
|
|
|
|
code += " return builder:StartVector(" +
|
|
NumToString(element_size) + ", numElems, " +
|
|
NumToString(alignment) + ")\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
}
|
|
});
|
|
|
|
code += "function " + object_name + ".End(builder)\n";
|
|
code += " return builder:EndObject()\n";
|
|
code += "end\n";
|
|
code += "\n";
|
|
}
|
|
|
|
EmitCodeBlock(code, object_name, ns, object->declaration_file()->str());
|
|
});
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void GenerateDocumentation(
|
|
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
|
|
*documentation,
|
|
std::string indent, std::string &code) const {
|
|
flatbuffers::ForAllDocumentation(
|
|
documentation, [&](const flatbuffers::String *str) {
|
|
code += indent + "--" + str->str() + "\n";
|
|
});
|
|
}
|
|
|
|
std::string GenerateStructBuilderArgs(const r::Object *object,
|
|
std::string prefix = "") const {
|
|
std::string signature;
|
|
ForAllFields(object, /*reverse=*/false, [&](const r::Field *field) {
|
|
if (IsStructOrTable(field->type()->base_type())) {
|
|
const r::Object *field_object = GetObject(field->type());
|
|
signature += GenerateStructBuilderArgs(
|
|
field_object, prefix + namer_.Variable(*field) + "_");
|
|
} else {
|
|
signature += ", " + prefix + namer_.Variable(*field);
|
|
}
|
|
});
|
|
return signature;
|
|
}
|
|
|
|
std::string AppendStructBuilderBody(const r::Object *object,
|
|
std::string prefix = "") const {
|
|
std::string code;
|
|
code += " builder:Prep(" + NumToString(object->minalign()) + ", " +
|
|
NumToString(object->bytesize()) + ")\n";
|
|
|
|
// We need to reverse the order we iterate over, since we build the
|
|
// buffer backwards.
|
|
ForAllFields(object, /*reverse=*/true, [&](const r::Field *field) {
|
|
const int32_t num_padding_bytes = field->padding();
|
|
if (num_padding_bytes) {
|
|
code += " builder:Pad(" + NumToString(num_padding_bytes) + ")\n";
|
|
}
|
|
if (IsStructOrTable(field->type()->base_type())) {
|
|
const r::Object *field_object = GetObject(field->type());
|
|
code += AppendStructBuilderBody(field_object,
|
|
prefix + namer_.Variable(*field) + "_");
|
|
} else {
|
|
code += " builder:Prepend" + GenerateMethod(field) + "(" + prefix +
|
|
namer_.Variable(*field) + ")\n";
|
|
}
|
|
});
|
|
|
|
return code;
|
|
}
|
|
|
|
std::string GenerateMethod(const r::Field *field) const {
|
|
const r::BaseType base_type = field->type()->base_type();
|
|
if (IsScalar(base_type)) { return namer_.Type(GenerateType(base_type)); }
|
|
if (IsStructOrTable(base_type)) { return "Struct"; }
|
|
return "UOffsetTRelative";
|
|
}
|
|
|
|
std::string GenerateGetter(const r::Type *type,
|
|
bool element_type = false) const {
|
|
switch (element_type ? type->element() : type->base_type()) {
|
|
case r::String: return "self.view:String(";
|
|
case r::Union: return "self.view:Union(";
|
|
case r::Vector: return GenerateGetter(type, true);
|
|
default:
|
|
return "self.view:Get(flatbuffers.N." +
|
|
namer_.Type(GenerateType(type, element_type)) + ", ";
|
|
}
|
|
}
|
|
|
|
std::string GenerateType(const r::Type *type,
|
|
bool element_type = false) const {
|
|
const r::BaseType base_type =
|
|
element_type ? type->element() : type->base_type();
|
|
if (IsScalar(base_type)) { return GenerateType(base_type); }
|
|
switch (base_type) {
|
|
case r::String: return "string";
|
|
case r::Vector: return GenerateGetter(type, true);
|
|
case r::Obj: return namer_.Type(namer_.Denamespace(GetObject(type)));
|
|
|
|
default: return "*flatbuffers.Table";
|
|
}
|
|
}
|
|
|
|
std::string GenerateType(const r::BaseType base_type) const {
|
|
// Need to override the default naming to match the Lua runtime libraries.
|
|
// TODO(derekbailey): make overloads in the runtime libraries to avoid this.
|
|
switch (base_type) {
|
|
case r::None: return "uint8";
|
|
case r::UType: return "uint8";
|
|
case r::Byte: return "int8";
|
|
case r::UByte: return "uint8";
|
|
case r::Short: return "int16";
|
|
case r::UShort: return "uint16";
|
|
case r::Int: return "int32";
|
|
case r::UInt: return "uint32";
|
|
case r::Long: return "int64";
|
|
case r::ULong: return "uint64";
|
|
case r::Float: return "Float32";
|
|
case r::Double: return "Float64";
|
|
default: return r::EnumNameBaseType(base_type);
|
|
}
|
|
}
|
|
|
|
std::string DefaultValue(const r::Field *field) const {
|
|
const r::BaseType base_type = field->type()->base_type();
|
|
if (IsFloatingPoint(base_type)) {
|
|
return NumToString(field->default_real());
|
|
}
|
|
if (IsBool(base_type)) {
|
|
return field->default_integer() ? "true" : "false";
|
|
}
|
|
if (IsScalar(base_type)) { return NumToString((field->default_integer())); }
|
|
// represents offsets
|
|
return "0";
|
|
}
|
|
|
|
void StartCodeBlock(const reflection::Enum *enum_def) {
|
|
current_enum_ = enum_def;
|
|
current_obj_ = nullptr;
|
|
requires_.clear();
|
|
}
|
|
|
|
void StartCodeBlock(const reflection::Object *object) {
|
|
current_obj_ = object;
|
|
current_enum_ = nullptr;
|
|
requires_.clear();
|
|
}
|
|
|
|
std::string RegisterRequires(const r::Field *field,
|
|
bool use_element = false) {
|
|
std::string type_name;
|
|
|
|
const r::BaseType type =
|
|
use_element ? field->type()->element() : field->type()->base_type();
|
|
|
|
if (IsStructOrTable(type)) {
|
|
const r::Object *object = GetObjectByIndex(field->type()->index());
|
|
if (object == current_obj_) { return namer_.Denamespace(object); }
|
|
type_name = object->name()->str();
|
|
} else {
|
|
const r::Enum *enum_def = GetEnumByIndex(field->type()->index());
|
|
if (enum_def == current_enum_) { return namer_.Denamespace(enum_def); }
|
|
type_name = enum_def->name()->str();
|
|
}
|
|
|
|
// Prefix with double __ to avoid name clashing, since these are defined
|
|
// at the top of the file and have lexical scoping. Replace '.' with '_'
|
|
// so it can be a legal identifier.
|
|
std::string name = "__" + type_name;
|
|
std::replace(name.begin(), name.end(), '.', '_');
|
|
|
|
return RegisterRequires(name, type_name);
|
|
}
|
|
|
|
std::string RegisterRequires(const std::string &local_name,
|
|
const std::string &requires_name) {
|
|
requires_[local_name] = requires_name;
|
|
return local_name;
|
|
}
|
|
|
|
void EmitCodeBlock(const std::string &code_block, const std::string &name,
|
|
const std::string &ns,
|
|
const std::string &declaring_file) const {
|
|
const std::string root_type = schema_->root_table()->name()->str();
|
|
const std::string root_file =
|
|
schema_->root_table()->declaration_file()->str();
|
|
const std::string full_qualified_name = ns.empty() ? name : ns + "." + name;
|
|
|
|
std::string code = "--[[ " + full_qualified_name + "\n\n";
|
|
code +=
|
|
" Automatically generated by the FlatBuffers compiler, do not "
|
|
"modify.\n";
|
|
code += " Or modify. I'm a message, not a cop.\n";
|
|
code += "\n";
|
|
code += " flatc version: " + flatc_version_ + "\n";
|
|
code += "\n";
|
|
code += " Declared by : " + declaring_file + "\n";
|
|
code += " Rooting type : " + root_type + " (" + root_file + ")\n";
|
|
code += "\n--]]\n\n";
|
|
|
|
if (!requires_.empty()) {
|
|
for (auto it = requires_.cbegin(); it != requires_.cend(); ++it) {
|
|
code += "local " + it->first + " = require('" + it->second + "')\n";
|
|
}
|
|
code += "\n";
|
|
}
|
|
|
|
code += code_block;
|
|
code += "return " + name;
|
|
|
|
// Namespaces are '.' deliminted, so replace it with the path separator.
|
|
std::string path = ns;
|
|
|
|
if (ns.empty()) {
|
|
path = ".";
|
|
} else {
|
|
std::replace(path.begin(), path.end(), '.', '/');
|
|
}
|
|
|
|
// TODO(derekbailey): figure out a save file without depending on util.h
|
|
EnsureDirExists(path);
|
|
const std::string file_name =
|
|
options_.output_path + path + "/" + namer_.File(name);
|
|
SaveFile(file_name.c_str(), code, false);
|
|
}
|
|
|
|
std::unordered_set<std::string> keywords_;
|
|
std::map<std::string, std::string> requires_;
|
|
CodeGenOptions options_;
|
|
|
|
const r::Object *current_obj_;
|
|
const r::Enum *current_enum_;
|
|
const std::string flatc_version_;
|
|
const BfbsNamer namer_;
|
|
};
|
|
} // namespace
|
|
|
|
std::unique_ptr<CodeGenerator> NewLuaBfbsGenerator(
|
|
const std::string &flatc_version) {
|
|
return std::unique_ptr<LuaBfbsGenerator>(new LuaBfbsGenerator(flatc_version));
|
|
}
|
|
|
|
} // namespace flatbuffers
|